diff options
26 files changed, 218 insertions, 151 deletions
diff --git a/mesonbuild/astinterpreter.py b/mesonbuild/astinterpreter.py index 1cdf523..32d0845 100644 --- a/mesonbuild/astinterpreter.py +++ b/mesonbuild/astinterpreter.py @@ -160,7 +160,7 @@ class AstInterpreter(interpreterbase.InterpreterBase): assert(isinstance(args, mparser.ArgumentNode)) if args.incorrect_order(): raise InvalidArguments('All keyword arguments must be after positional arguments.') - return (args.arguments, args.kwargs) + return args.arguments, args.kwargs def transform(self): self.load_root_meson_file() diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 56c786b..e46c2c5 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -22,7 +22,7 @@ import json import subprocess from ..mesonlib import MesonException, get_compiler_for_source, classify_unity_sources -class CleanTrees(): +class CleanTrees: ''' Directories outputted by custom targets that have to be manually cleaned because on Linux `ninja clean` only deletes empty directories. @@ -31,7 +31,7 @@ class CleanTrees(): self.build_dir = build_dir self.trees = trees -class InstallData(): +class InstallData: def __init__(self, source_dir, build_dir, prefix): self.source_dir = source_dir self.build_dir = build_dir @@ -45,7 +45,7 @@ class InstallData(): self.install_scripts = [] self.install_subdirs = [] -class ExecutableSerialisation(): +class ExecutableSerialisation: def __init__(self, name, fname, cmd_args, env, is_cross, exe_wrapper, workdir, extra_paths, capture): self.name = name @@ -76,7 +76,7 @@ class TestSerialisation: # This class contains the basic functionality that is needed by all backends. # Feel free to move stuff in and out of it as you see fit. -class Backend(): +class Backend: def __init__(self, build): self.build = build self.environment = build.environment @@ -247,7 +247,7 @@ class Backend(): benchmark_data = os.path.join(self.environment.get_scratch_dir(), 'meson_benchmark_setup.dat') with open(benchmark_data, 'wb') as datafile: self.write_benchmark_file(datafile) - return (test_data, benchmark_data) + return test_data, benchmark_data def determine_linker(self, target): ''' @@ -618,7 +618,7 @@ class Backend(): dfilename = os.path.join(outdir, target.depfile) i = i.replace('@DEPFILE@', dfilename) elif '@PRIVATE_OUTDIR_' in i: - match = re.search('@PRIVATE_OUTDIR_(ABS_)?([^\/\s*]*)@', i) + match = re.search('@PRIVATE_OUTDIR_(ABS_)?([^/\s*]*)@', i) if not match: msg = 'Custom target {!r} has an invalid argument {!r}' \ ''.format(target.name, i) @@ -651,7 +651,7 @@ class Backend(): # # https://github.com/mesonbuild/meson/pull/737 cmd = [i.replace('\\', '/') for i in cmd] - return (srcs, ofilenames, cmd) + return srcs, ofilenames, cmd def run_postconf_scripts(self): env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(), diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 8d08824..e1a478c 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -36,7 +36,7 @@ else: def ninja_quote(text): return text.replace(' ', '$ ').replace(':', '$:') -class RawFilename(): +class RawFilename: """ Used when a filename is already relative to the root build directory, so that we know not to add the target's private build directory to it. @@ -56,7 +56,7 @@ class RawFilename(): def startswith(self, s): return self.fname.startswith(s) -class NinjaBuildElement(): +class NinjaBuildElement: def __init__(self, all_outputs, outfilenames, rule, infilenames): if isinstance(outfilenames, str): self.outfilenames = [outfilenames] @@ -830,7 +830,7 @@ int dummy; else: raise InvalidArguments('Unknown resource file %s.' % r) args.append(a) - return (args, deps) + return args, deps def generate_cs_target(self, target, outfile): buildtype = self.environment.coredata.get_builtin_option('buildtype') @@ -962,7 +962,7 @@ int dummy; raise InvalidArguments(msg) # Store 'somefile.vala': GeneratedList (or CustomTarget) srctype[f] = gensrc - return (vala, vapi, (others, othersgen)) + return vala, vapi, (others, othersgen) def generate_vala_compile(self, target, outfile): """Vala is compiled into C. Set up all necessary build steps here.""" @@ -1139,7 +1139,7 @@ int dummy; srcs.append(i) else: others.append(i) - return (srcs, others) + return srcs, others def generate_swift_target(self, target, outfile): module_name = self.target_swift_modulename(target) @@ -1933,7 +1933,7 @@ rule FORTRAN_DEP_HACK commands += pch_args commands += self.get_compile_debugfile_args(compiler, target, objname) dep = dst + '.' + compiler.get_depfile_suffix() - return (commands, dep, dst, [objname]) + return commands, dep, dst, [objname] def generate_gcc_pch_command(self, target, compiler, pch): commands = [] @@ -1941,7 +1941,7 @@ rule FORTRAN_DEP_HACK dst = os.path.join(self.get_target_private_dir(target), os.path.split(pch)[-1] + '.' + compiler.get_pch_suffix()) dep = dst + '.' + compiler.get_depfile_suffix() - return (commands, dep, dst, []) # Gcc does not create an object file during pch generation. + return commands, dep, dst, [] # Gcc does not create an object file during pch generation. def generate_pch(self, target, outfile): cstr = '' diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 137e9ae..61f755b 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -47,7 +47,7 @@ def split_o_flags_args(args): o_flags += ['/O' + f for f in flags] return o_flags -class RegenInfo(): +class RegenInfo: def __init__(self, source_dir, build_dir, depfiles): self.source_dir = source_dir self.build_dir = build_dir @@ -224,7 +224,7 @@ class Vs2010Backend(backends.Backend): ofile.write('Microsoft Visual Studio Solution File, Format ' 'Version 11.00\n') ofile.write('# Visual Studio ' + self.vs_version + '\n') - prj_templ = prj_line = 'Project("{%s}") = "%s", "%s", "{%s}"\n' + prj_templ = 'Project("{%s}") = "%s", "%s", "{%s}"\n' for p in projlist: prj_line = prj_templ % (self.environment.coredata.guid, p[0], p[1], p[2]) @@ -313,7 +313,7 @@ class Vs2010Backend(backends.Backend): else: # Everything that is not an object or source file is considered a header. headers.append(i) - return (sources, headers, objects, languages) + return sources, headers, objects, languages def target_to_build_root(self, target): if target.subdir == '': @@ -512,7 +512,7 @@ class Vs2010Backend(backends.Backend): libs.append(arg) else: other.append(arg) - return (lpaths, libs, other) + return lpaths, libs, other def _get_cl_compiler(self, target): for lang, c in target.compilers.items(): @@ -882,7 +882,6 @@ class Vs2010Backend(backends.Backend): header, impl, suffix = pch_sources[lang] relpath = os.path.join(proj_to_src_dir, impl) inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath) - lang = Vs2010Backend.lang_from_source_file(s) pch = ET.SubElement(inc_cl, 'PrecompiledHeader') pch.text = 'Create' pch_out = ET.SubElement(inc_cl, 'PrecompiledHeaderOutputFile') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 2ca1404..ceae49b 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -158,7 +158,7 @@ class Build: return link_args.get(compiler.get_language(), []) -class IncludeDirs(): +class IncludeDirs: def __init__(self, curdir, dirs, is_system, extra_build_dirs=None): self.curdir = curdir self.incdirs = dirs @@ -179,7 +179,7 @@ class IncludeDirs(): def get_extra_build_dirs(self): return self.extra_build_dirs -class ExtractedObjects(): +class ExtractedObjects: ''' Holds a list of sources for which the objects must be extracted ''' @@ -219,7 +219,7 @@ class ExtractedObjects(): def get_want_all_objects(self): return self.want_all_objects -class EnvironmentVariables(): +class EnvironmentVariables: def __init__(self): self.envvars = [] @@ -857,7 +857,7 @@ You probably should put it in link_with instead.''') return False -class Generator(): +class Generator: def __init__(self, args, kwargs): if len(args) != 1: raise InvalidArguments('Generator requires exactly one positional argument: the executable') @@ -940,7 +940,7 @@ class Generator(): return output -class GeneratedList(): +class GeneratedList: def __init__(self, generator, extra_args=[]): if hasattr(generator, 'held_object'): generator = generator.held_object @@ -1178,7 +1178,7 @@ class SharedLibrary(BuildTarget): # Visual Studio module-definitions file if 'vs_module_defs' in kwargs: path = kwargs['vs_module_defs'] - if (os.path.isabs(path)): + if os.path.isabs(path): self.vs_module_defs = File.from_absolute_file(path) else: self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path) @@ -1456,7 +1456,7 @@ class Jar(BuildTarget): def get_java_args(self): return self.java_args -class ConfigureFile(): +class ConfigureFile: def __init__(self, subdir, sourcename, targetname, configuration_data): self.subdir = subdir @@ -1482,7 +1482,7 @@ class ConfigureFile(): def get_target_name(self): return self.targetname -class ConfigurationData(): +class ConfigurationData: def __init__(self): super().__init__() self.values = {} @@ -1501,7 +1501,7 @@ class ConfigurationData(): # A bit poorly named, but this represents plain data files to copy # during install. -class Data(): +class Data: def __init__(self, sources, install_dir): self.sources = sources self.install_dir = install_dir diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py index 4f659ca..4178f0b 100644 --- a/mesonbuild/compilers.py +++ b/mesonbuild/compilers.py @@ -316,14 +316,14 @@ class CrossNoRunException(MesonException): def __init(self, *args, **kwargs): Exception.__init__(self, *args, **kwargs) -class RunResult(): +class RunResult: def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'): self.compiled = compiled self.returncode = returncode self.stdout = stdout self.stderr = stderr -class Compiler(): +class Compiler: def __init__(self, exelist, version): if isinstance(exelist, str): self.exelist = [exelist] @@ -562,7 +562,7 @@ class CCompiler(Compiler): return [] def split_shlib_to_parts(self, fname): - return (None, fname) + return None, fname # The default behaviour is this, override in # OSX and MSVC. @@ -1251,7 +1251,7 @@ class MonoCompiler(Compiler): return ['-warnaserror'] def split_shlib_to_parts(self, fname): - return (None, fname) + return None, fname def build_rpath_args(self, build_dir, rpath_paths, install_rpath): return [] @@ -1332,7 +1332,7 @@ class JavaCompiler(Compiler): return ['-Werror'] def split_shlib_to_parts(self, fname): - return (None, fname) + return None, fname def build_rpath_args(self, build_dir, rpath_paths, install_rpath): return [] @@ -1926,7 +1926,7 @@ class VisualStudioCCompiler(CCompiler): def gen_pch_args(self, header, source, pchname): objname = os.path.splitext(pchname)[0] + '.obj' - return (objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname]) + return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname] def gen_import_library_args(self, implibname): "The name of the outputted import library" @@ -2141,7 +2141,7 @@ class GnuCompiler: return 'gch' def split_shlib_to_parts(self, fname): - return (os.path.split(fname)[0], fname) + return os.path.split(fname)[0], fname def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) @@ -2261,7 +2261,7 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): # too strict without this and always fails. return self.get_no_optimization_args() + ['-fpermissive'] -class ClangCompiler(): +class ClangCompiler: def __init__(self, clang_type): self.id = 'clang' self.clang_type = clang_type @@ -2422,7 +2422,7 @@ class IntelCompiler: return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() def split_shlib_to_parts(self, fname): - return (os.path.split(fname)[0], fname) + return os.path.split(fname)[0], fname def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): if self.icc_type == ICC_STANDARD: @@ -2585,7 +2585,7 @@ end program prog return gnulike_buildtype_linker_args[buildtype] def split_shlib_to_parts(self, fname): - return (os.path.split(fname)[0], fname) + return os.path.split(fname)[0], fname def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) @@ -2778,7 +2778,7 @@ class NAGFortranCompiler(FortranCompiler): return NAGFortranCompiler.std_warn_args -class VisualStudioLinker(): +class VisualStudioLinker: always_args = ['/NOLOGO'] def __init__(self, exelist): @@ -2825,7 +2825,7 @@ class VisualStudioLinker(): pdbarr += ['pdb'] return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)] -class ArLinker(): +class ArLinker: def __init__(self, exelist): self.exelist = exelist diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 080067d..60bb10b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -105,7 +105,7 @@ class UserStringArrayOption(UserOption): # invocations of Meson. It is roughly the same thing as # cmakecache. -class CoreData(): +class CoreData: def __init__(self, options): self.guid = str(uuid.uuid4()).upper() @@ -131,10 +131,54 @@ class CoreData(): # Only to print a warning if it changes between Meson invocations. self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') + def sanitize_prefix(self, prefix): + if not os.path.isabs(prefix): + raise MesonException('prefix value {!r} must be an absolute path' + ''.format(prefix)) + if prefix.endswith('/') or prefix.endswith('\\'): + # On Windows we need to preserve the trailing slash if the + # string is of type 'C:\' because 'C:' is not an absolute path. + if len(prefix) == 3 and prefix[1] == ':': + pass + else: + prefix = prefix[:-1] + return prefix + + def sanitize_dir_option_value(self, prefix, option, value): + ''' + If the option is an installation directory option and the value is an + absolute path, check that it resides within prefix and return the value + as a path relative to the prefix. + + This way everyone can do f.ex, get_option('libdir') and be sure to get + the library directory relative to prefix. + ''' + if option.endswith('dir') and os.path.isabs(value) and \ + option not in builtin_dir_noprefix_options: + # Value must be a subdir of the prefix + if os.path.commonpath([value, prefix]) != prefix: + m = 'The value of the {!r} option is {!r} which must be a ' \ + 'subdir of the prefix {!r}.\nNote that if you pass a ' \ + 'relative path, it is assumed to be a subdir of prefix.' + raise MesonException(m.format(option, value, prefix)) + # Convert path to be relative to prefix + skip = len(prefix) + 1 + value = value[skip:] + return value + def init_builtins(self, options): self.builtins = {} + # Sanitize prefix + options.prefix = self.sanitize_prefix(options.prefix) + # Initialize other builtin options for key in get_builtin_options(): - args = [key] + builtin_options[key][1:-1] + [getattr(options, key, get_builtin_option_default(key))] + if hasattr(options, key): + value = getattr(options, key) + value = self.sanitize_dir_option_value(options.prefix, key, value) + setattr(options, key, value) + else: + value = get_builtin_option_default(key) + args = [key] + builtin_options[key][1:-1] + [value] self.builtins[key] = builtin_options[key][0](*args) def get_builtin_option(self, optname): @@ -143,7 +187,11 @@ class CoreData(): raise RuntimeError('Tried to get unknown builtin option %s.' % optname) def set_builtin_option(self, optname, value): - if optname in self.builtins: + if optname == 'prefix': + value = self.sanitize_prefix(value) + elif optname in self.builtins: + prefix = self.builtins['prefix'].value + value = self.sanitize_dir_option_value(prefix, optname, value) self.builtins[optname].set_value(value) else: raise RuntimeError('Tried to set unknown builtin option %s.' % optname) @@ -235,6 +283,9 @@ builtin_options = { 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True], } +# Installation directories that can reside in a path outside of the prefix +builtin_dir_noprefix_options = {'sysconfdir', 'localstatedir', 'sharedstatedir'} + forbidden_target_names = {'clean': None, 'clean-ctlist': None, 'clean-gcno': None, diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index e77abb8..97aec7e 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -30,10 +30,9 @@ from . import mesonlib from .environment import detect_cpu_family, for_windows class DependencyException(MesonException): - def __init__(self, *args, **kwargs): - MesonException.__init__(self, *args, **kwargs) + '''Exceptions raised while trying to find dependencies''' -class Dependency(): +class Dependency: def __init__(self, type_name='unknown'): self.name = "null" self.is_found = False @@ -170,17 +169,18 @@ class PkgConfigDependency(Dependency): if not self.silent: mlog.log(*found_msg) if self.required: - raise DependencyException( - 'Invalid version of a dependency, needed %s %s found %s.' % - (name, not_found, self.modversion)) + m = 'Invalid version of dependency, need {!r} {!r} found {!r}.' + raise DependencyException(m.format(name, not_found, self.modversion)) return found_msg += [mlog.green('YES'), self.modversion] - if not self.silent: - mlog.log(*found_msg) # Fetch cargs to be used while using this dependency self._set_cargs() # Fetch the libraries and library paths needed for using this self._set_libs() + # Print the found message only at the very end because fetching cflags + # and libs can also fail if other needed pkg-config files aren't found. + if not self.silent: + mlog.log(*found_msg) def __repr__(self): s = '<{0} {1}: {2} {3}>' @@ -189,7 +189,7 @@ class PkgConfigDependency(Dependency): def _call_pkgbin(self, args): p, out = Popen_safe([self.pkgbin] + args, env=os.environ)[0:2] - return (p.returncode, out.strip()) + return p.returncode, out.strip() def _set_cargs(self): ret, out = self._call_pkgbin(['--cflags', self.name]) @@ -392,7 +392,7 @@ class WxDependency(Dependency): def found(self): return self.is_found -class ExternalProgram(): +class ExternalProgram: windows_exts = ('exe', 'com', 'bat') def __init__(self, name, fullpath=None, silent=False, search_dir=None): @@ -668,7 +668,6 @@ class BoostDependency(Dependency): self.lib_modules_mt[modname] = fname def detect_lib_modules_nix(self): - libsuffix = None if mesonlib.is_osx(): libsuffix = 'dylib' else: @@ -1445,7 +1444,9 @@ def find_external_dependency(name, environment, kwargs): if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency(name, required) if required and not fwdep.found(): - raise DependencyException('Dependency "%s" not found' % name) + m = 'Dependency {!r} not found, tried Extra Frameworks ' \ + 'and Pkg-Config:\n\n' + str(pkg_exc) + raise DependencyException(m.format(name)) return fwdep if pkg_exc is not None: raise pkg_exc diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 720546f..e143b0b 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -34,7 +34,7 @@ def find_coverage_tools(): lcov_exe = None if not mesonlib.exe_exists([genhtml_exe, '--version']): genhtml_exe = None - return (gcovr_exe, lcov_exe, genhtml_exe) + return gcovr_exe, lcov_exe, genhtml_exe def detect_ninja(version='1.5'): for n in ['ninja', 'ninja-build']: @@ -182,7 +182,7 @@ def search_version(text): return match.group(0) return 'unknown version' -class Environment(): +class Environment: private_dir = 'meson-private' log_dir = 'meson-logs' coredata_file = os.path.join(private_dir, 'coredata.dat') @@ -635,7 +635,6 @@ class Environment(): raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_d_compiler(self, want_cross): - exelist = None is_cross = False # Search for a D compiler. # We prefer LDC over GDC unless overridden with the DC @@ -799,7 +798,7 @@ def get_args_from_envvars(compiler): compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist()) if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'): - return ([], []) + return [], [] # Compile flags cflags_mapping = {'c': 'CFLAGS', @@ -830,9 +829,9 @@ def get_args_from_envvars(compiler): log_var('CPPFLAGS', preproc_flags) compile_flags += preproc_flags.split() - return (compile_flags, link_flags) + return compile_flags, link_flags -class CrossBuildInfo(): +class CrossBuildInfo: def __init__(self, filename): self.config = {'properties': {}} self.parse_datafile(filename) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 35adc59..b5cd0b6 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -186,7 +186,7 @@ class ConfigurationDataHolder(MutableInterpreterObject): if desc is not None and not isinstance(desc, str): raise InterpreterException('Description must be a string.') - return (name, val, desc) + return name, val, desc def set_method(self, args, kwargs): (name, val, desc) = self.validate_args(args, kwargs) @@ -1314,7 +1314,7 @@ class Interpreter(InterpreterBase): def module_method_callback(self, return_object): if not isinstance(return_object, ModuleReturnValue): - assert(False) + assert False raise InterpreterException('Bug in module, it returned an invalid object') invalues = return_object.new_objects self.process_new_values(invalues) @@ -1338,7 +1338,7 @@ class Interpreter(InterpreterBase): projname, depname = di subproj = self.do_subproject(projname, {}) self.build.cross_stdlibs[l] = subproj.get_variable_method([depname], {}) - except KeyError as e: + except KeyError: pass @stringArgs @@ -1565,16 +1565,13 @@ class Interpreter(InterpreterBase): for defopt in self.default_project_options: key, value = defopt.split('=') pref = key + '=' - was_found = False for i in default_options: if i.startswith(pref): - was_found = True break - if was_found: - break - defopt = self.subproject + ':' + defopt - newoptions = [defopt] + self.environment.cmd_line_options.projectoptions - self.environment.cmd_line_options.projectoptions = newoptions + else: + defopt = self.subproject + ':' + defopt + newoptions = [defopt] + self.environment.cmd_line_options.projectoptions + self.environment.cmd_line_options.projectoptions = newoptions @stringArgs def func_project(self, node, args, kwargs): @@ -1714,7 +1711,7 @@ class Interpreter(InterpreterBase): new_options[i].set_value(value) new_options.update(self.coredata.compiler_options) self.coredata.compiler_options = new_options - return (comp, cross_comp) + return comp, cross_comp def add_languages(self, args, required): success = True diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 80413e2..e4dd58b 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -65,7 +65,7 @@ class InvalidCode(InterpreterException): class InvalidArguments(InterpreterException): pass -class InterpreterObject(): +class InterpreterObject: def __init__(self): self.methods = {} @@ -520,7 +520,7 @@ class InterpreterBase: reduced_kw[key] = self.evaluate_statement(a) if not isinstance(reduced_pos, list): reduced_pos = [reduced_pos] - return (reduced_pos, reduced_kw) + return reduced_pos, reduced_kw def flatten(self, args): if isinstance(args, mparser.StringNode): diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index dfdcb94..c921409 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -231,7 +231,7 @@ def run(args): except ConfException as e: print('Meson configurator encountered an error:\n') print(e) - return(1) + return 1 return 0 if __name__ == '__main__': diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index c4f6769..2587d6f 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -19,12 +19,10 @@ import platform, subprocess, operator, os, shutil, re from glob import glob class MesonException(Exception): - def __init__(self, *args, **kwargs): - Exception.__init__(self, *args, **kwargs) + '''Exceptions thrown by Meson''' class EnvironmentException(MesonException): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + '''Exceptions thrown while processing and creating the build environment''' class File: def __init__(self, is_built, subdir, fname): @@ -208,7 +206,7 @@ def version_compare_many(vstr1, conditions): not_found.append(req) else: found.append(req) - return (not_found == [], not_found, found) + return not_found == [], not_found, found def default_libdir(): if is_debianlike(): @@ -395,4 +393,4 @@ def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs): stdout=subprocess.PIPE, stderr=stderr, **kwargs) o, e = p.communicate(write) - return (p, o, e) + return p, o, e diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 23501f8..031486c 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -69,19 +69,10 @@ parser.add_argument('-v', '--version', action='version', version=coredata.version) parser.add_argument('directories', nargs='*') -class MesonApp(): +class MesonApp: def __init__(self, dir1, dir2, script_launcher, handshake, options, original_cmd_line_args): (self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake) - if not os.path.isabs(options.prefix): - raise RuntimeError('--prefix value must be an absolute path: {!r}'.format(options.prefix)) - if options.prefix.endswith('/') or options.prefix.endswith('\\'): - # On Windows we need to preserve the trailing slash if the - # string is of type 'C:\' because 'C:' is not an absolute path. - if len(options.prefix) == 3 and options.prefix[1] == ':': - pass - else: - options.prefix = options.prefix[:-1] self.meson_script_launcher = script_launcher self.options = options self.original_cmd_line_args = original_cmd_line_args @@ -106,9 +97,9 @@ class MesonApp(): if self.has_build_file(ndir1): if self.has_build_file(ndir2): raise RuntimeError('Both directories contain a build file %s.' % environment.build_filename) - return (ndir1, ndir2) + return ndir1, ndir2 if self.has_build_file(ndir2): - return (ndir2, ndir1) + return ndir2, ndir1 raise RuntimeError('Neither directory contains a build file %s.' % environment.build_filename) def validate_dirs(self, dir1, dir2, handshake): @@ -126,7 +117,7 @@ If you want to change option values, use the mesonconf tool instead.''' else: if handshake: raise RuntimeError('Something went terribly wrong. Please file a bug.') - return (src_dir, build_dir) + return src_dir, build_dir def check_pkgconfig_envvar(self, env): curvar = os.environ.get('PKG_CONFIG_PATH', '') diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 3c462c7..5a381c1 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -40,10 +40,10 @@ parser.add_argument('--tests', action='store_true', dest='tests', default=False, parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False, help='List all benchmarks.') parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False, - help='list external dependencies.') + help='List external dependencies.') parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False, - help='information about projects.') -parser.add_argument('args', nargs='+') + help='Information about projects.') +parser.add_argument('builddir', nargs='?', help='The build directory') def determine_installed_path(target, installdata): install_target = None @@ -61,9 +61,9 @@ def determine_installed_path(target, installdata): def list_installed(installdata): res = {} - for path, installpath in installdata.data: - res[path] = os.path.join(installdata.prefix, installpath) - + if installdata is not None: + for path, installpath in installdata.data: + res[path] = os.path.join(installdata.prefix, installpath) print(json.dumps(res)) @@ -92,7 +92,7 @@ def list_targets(coredata, builddata, installdata): else: typename = 'unknown' t['type'] = typename - if target.should_install(): + if installdata and target.should_install(): t['installed'] = True t['install_filename'] = determine_installed_path(target, installdata) else: @@ -205,19 +205,22 @@ def list_projinfo(builddata): print(json.dumps(result)) def run(args): + datadir = 'meson-private' options = parser.parse_args(args) - if len(options.args) > 1: - print('Too many arguments') + if options.builddir is not None: + datadir = os.path.join(options.builddir, datadir) + if not os.path.isdir(datadir): + print('Current directory is not a build dir. Please specify it or ' + 'change the working directory to it.') return 1 - elif len(options.args) == 1: - bdir = options.args[0] - else: - bdir = '' - corefile = os.path.join(bdir, 'meson-private/coredata.dat') - buildfile = os.path.join(bdir, 'meson-private/build.dat') - installfile = os.path.join(bdir, 'meson-private/install.dat') - testfile = os.path.join(bdir, 'meson-private/meson_test_setup.dat') - benchmarkfile = os.path.join(bdir, 'meson-private/meson_benchmark_setup.dat') + + corefile = os.path.join(datadir, 'coredata.dat') + buildfile = os.path.join(datadir, 'build.dat') + installfile = os.path.join(datadir, 'install.dat') + testfile = os.path.join(datadir, 'meson_test_setup.dat') + benchmarkfile = os.path.join(datadir, 'meson_benchmark_setup.dat') + + # Load all data files with open(corefile, 'rb') as f: coredata = pickle.load(f) with open(buildfile, 'rb') as f: @@ -226,8 +229,13 @@ def run(args): testdata = pickle.load(f) with open(benchmarkfile, 'rb') as f: benchmarkdata = pickle.load(f) - with open(installfile, 'rb') as f: - installdata = pickle.load(f) + # Install data is only available with the Ninja backend + if os.path.isfile(installfile): + with open(installfile, 'rb') as f: + installdata = pickle.load(f) + else: + installdata = None + if options.list_targets: list_targets(coredata, builddata, installdata) elif options.list_installed: diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index cded2b0..a4f93d0 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -32,7 +32,7 @@ def shutdown(): if log_file is not None: log_file.close() -class AnsiDecorator(): +class AnsiDecorator: plain_code = "\033[0m" def __init__(self, text, code): diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index fa8ade2..5871f65 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -47,7 +47,7 @@ class Lexer: ('eol_cont', re.compile(r'\\\n')), ('eol', re.compile(r'\n')), ('multiline_string', re.compile(r"'''(.|\n)*?'''", re.M)), - ('comment', re.compile(r'\#.*')), + ('comment', re.compile(r'#.*')), ('lparen', re.compile(r'\(')), ('rparen', re.compile(r'\)')), ('lbracket', re.compile(r'\[')), @@ -60,11 +60,11 @@ class Lexer: ('plus', re.compile(r'\+')), ('dash', re.compile(r'-')), ('star', re.compile(r'\*')), - ('percent', re.compile(r'\%')), + ('percent', re.compile(r'%')), ('fslash', re.compile(r'/')), ('colon', re.compile(r':')), ('equal', re.compile(r'==')), - ('nequal', re.compile(r'\!=')), + ('nequal', re.compile(r'!=')), ('assign', re.compile(r'=')), ('le', re.compile(r'<=')), ('lt', re.compile(r'<')), @@ -80,7 +80,7 @@ class Lexer: par_count = 0 bracket_count = 0 col = 0 - while(loc < len(code)): + while loc < len(code): matched = False value = None for (tid, reg) in self.token_specification: @@ -274,7 +274,7 @@ class PlusAssignmentNode: assert(isinstance(var_name, str)) self.value = value -class ForeachClauseNode(): +class ForeachClauseNode: def __init__(self, lineno, colno, varname, items, block): self.lineno = lineno self.colno = colno @@ -282,28 +282,28 @@ class ForeachClauseNode(): self.items = items self.block = block -class IfClauseNode(): +class IfClauseNode: def __init__(self, lineno, colno): self.lineno = lineno self.colno = colno self.ifs = [] self.elseblock = EmptyNode() -class UMinusNode(): +class UMinusNode: def __init__(self, current_location, value): self.subdir = current_location.subdir self.lineno = current_location.lineno self.colno = current_location.colno self.value = value -class IfNode(): +class IfNode: def __init__(self, lineno, colno, condition, block): self.lineno = lineno self.colno = colno self.condition = condition self.block = block -class TernaryNode(): +class TernaryNode: def __init__(self, lineno, colno, condition, trueblock, falseblock): self.lineno = lineno self.colno = colno @@ -311,7 +311,7 @@ class TernaryNode(): self.trueblock = trueblock self.falseblock = falseblock -class ArgumentNode(): +class ArgumentNode: def __init__(self, token): self.lineno = token.lineno self.colno = token.colno @@ -331,7 +331,7 @@ class ArgumentNode(): if self.num_kwargs() > 0: self.order_error = True if not isinstance(statement, EmptyNode): - self.arguments = self.arguments + [statement] + self.arguments += [statement] def set_kwarg(self, name, value): self.kwargs[name] = value diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 089eb2c..10b8fab 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -128,7 +128,7 @@ class OptionInterpreter: raise OptionException('Keyword argument name is not a string.') a = args.kwargs[key] reduced_kw[key] = self.reduce_single(a) - return (reduced_pos, reduced_kw) + return reduced_pos, reduced_kw def evaluate_statement(self, node): if not isinstance(node, mparser.FunctionNode): diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index 193d5b6..1404619 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -23,7 +23,7 @@ DT_STRTAB = 5 DT_SONAME = 14 DT_MIPS_RLD_MAP_REL = 1879048245 -class DataSizes(): +class DataSizes: def __init__(self, ptrsize, is_le): if is_le: p = '<' @@ -150,7 +150,7 @@ class Elf(DataSizes): is_le = False else: sys.exit('File "%s" has unknown ELF endianness.' % self.bfile) - return (ptrsize, is_le) + return ptrsize, is_le def parse_header(self): self.bf.seek(0) diff --git a/mesonbuild/wrap/wraptool.py b/mesonbuild/wrap/wraptool.py index 2663465..79bd5df 100644 --- a/mesonbuild/wrap/wraptool.py +++ b/mesonbuild/wrap/wraptool.py @@ -74,7 +74,7 @@ def get_latest_version(name): jd = get_result(API_ROOT + 'query/get_latest/' + name) branch = jd['branch'] revision = jd['revision'] - return (branch, revision) + return branch, revision def install(name): if not os.path.isdir('subprojects'): @@ -102,7 +102,7 @@ def get_current_version(wrapfile): arr = patch_url.split('/') branch = arr[-3] revision = int(arr[-2]) - return (branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename']) + return branch, revision, cp['directory'], cp['source_filename'], cp['patch_filename'] def update(name): if not os.path.isdir('subprojects'): diff --git a/mesontest.py b/mesontest.py index e334a46..bce4100 100755 --- a/mesontest.py +++ b/mesontest.py @@ -88,7 +88,7 @@ parser.add_argument('--setup', default=None, dest='setup', help='Which test setup to use.') parser.add_argument('args', nargs='*') -class TestRun(): +class TestRun: def __init__(self, res, returncode, should_fail, duration, stdo, stde, cmd, env): self.res = res @@ -217,7 +217,7 @@ class TestHarness: child_env.update(test.env) if len(test.extra_paths) > 0: - child_env['PATH'] = child_env['PATH'] + ';'.join([''] + test.extra_paths) + child_env['PATH'] += ';'.join([''] + test.extra_paths) setsid = None stdout = None @@ -333,12 +333,14 @@ TIMEOUT: %4d self.run_tests(tests) return self.fail_count + @staticmethod def split_suite_string(suite): if ':' in suite: return suite.split(':', 1) else: - return (suite, "") + return suite, "" + @staticmethod def test_in_suites(test, suites): for suite in suites: (prj_match, st_match) = TestHarness.split_suite_string(suite) @@ -398,7 +400,7 @@ TIMEOUT: %4d def open_log_files(self): if not self.options.logbase or self.options.verbose: - return (None, None, None, None) + return None, None, None, None logfile_base = os.path.join(self.options.wd, 'meson-logs', self.options.logbase) @@ -416,7 +418,7 @@ TIMEOUT: %4d logfile.write('Log of Meson test suite run on %s.\n\n' % datetime.datetime.now().isoformat()) - return (logfile, logfilename, jsonlogfile, jsonlogfilename) + return logfile, logfilename, jsonlogfile, jsonlogfilename def get_wrapper(self): wrap = [] diff --git a/run_project_tests.py b/run_project_tests.py index 1f48be7..4715dbb 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -43,7 +43,7 @@ class TestResult: self.buildtime = buildtime self.testtime = testtime -class AutoDeletedDir(): +class AutoDeletedDir: def __init__(self, d): self.dir = d @@ -202,7 +202,7 @@ def run_configure_inprocess(commandlist): finally: sys.stdout = old_stdout sys.stderr = old_stderr - return (returncode, mystdout.getvalue(), mystderr.getvalue()) + return returncode, mystdout.getvalue(), mystderr.getvalue() def run_test_inprocess(testdir): old_stdout = sys.stdout @@ -218,7 +218,7 @@ def run_test_inprocess(testdir): sys.stdout = old_stdout sys.stderr = old_stderr os.chdir(old_cwd) - return (max(returncode_test, returncode_benchmark), mystdout.getvalue(), mystderr.getvalue()) + return max(returncode_test, returncode_benchmark), mystdout.getvalue(), mystderr.getvalue() def parse_test_args(testdir): args = [] @@ -457,7 +457,7 @@ def run_tests(all_tests, log_name_base, extra_args): print("Total build time: %.2fs" % build_time) print("Total test time: %.2fs" % test_time) ET.ElementTree(element=junit_root).write(xmlname, xml_declaration=True, encoding='UTF-8') - return (passing_tests, failing_tests, skipped_tests) + return passing_tests, failing_tests, skipped_tests def check_file(fname): linenum = 1 @@ -522,7 +522,7 @@ def generate_prebuilt(): object_suffix = 'o' objectfile = generate_pb_object(compiler, object_suffix) stlibfile = generate_pb_static(compiler, object_suffix, static_suffix) - return (objectfile, stlibfile) + return objectfile, stlibfile if __name__ == '__main__': parser = argparse.ArgumentParser(description="Run the test suite of Meson.") diff --git a/run_unittests.py b/run_unittests.py index 123bca8..6aa5b2b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -33,10 +33,11 @@ def get_soname(fname): return m.group(1) raise RuntimeError('Could not determine soname:\n\n' + raw_out) -def get_fake_options(): +def get_fake_options(prefix): import argparse opts = argparse.Namespace() opts.cross_file = None + opts.prefix = prefix return opts class FakeEnvironment(object): @@ -85,7 +86,8 @@ class LinuxlikeTests(unittest.TestCase): super().tearDown() def _run(self, command): - self.output += subprocess.check_output(command, env=os.environ.copy()) + self.output += subprocess.check_output(command, stderr=subprocess.STDOUT, + env=os.environ.copy()) def init(self, srcdir, extra_args=None): if extra_args is None: @@ -510,7 +512,7 @@ class LinuxlikeTests(unittest.TestCase): ''' testdir = os.path.join(self.common_test_dir, '1 trivial') env = Environment(testdir, self.builddir, self.meson_command, - get_fake_options(), []) + get_fake_options(self.prefix), []) cc = env.detect_c_compiler(False) self._test_stds_impl(testdir, cc, 'c') @@ -521,11 +523,10 @@ class LinuxlikeTests(unittest.TestCase): ''' testdir = os.path.join(self.common_test_dir, '2 cpp') env = Environment(testdir, self.builddir, self.meson_command, - get_fake_options(), []) + get_fake_options(self.prefix), []) cpp = env.detect_cpp_compiler(False) self._test_stds_impl(testdir, cpp, 'cpp') - def test_build_by_default(self): testdir = os.path.join(self.unit_test_dir, '5 build by default') self.init(testdir) @@ -537,6 +538,21 @@ class LinuxlikeTests(unittest.TestCase): self._run(self.ninja_command + ['fooprog']) self.assertTrue(os.path.exists(exe)) + def test_libdir_must_be_inside_prefix(self): + testdir = os.path.join(self.common_test_dir, '1 trivial') + # libdir being inside prefix is ok + args = ['--prefix', '/opt', '--libdir', '/opt/lib32'] + self.init(testdir, args) + self.wipe() + # libdir not being inside prefix is not ok + args = ['--prefix', '/usr', '--libdir', '/opt/lib32'] + self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args) + self.wipe() + # libdir must be inside prefix even when set via mesonconf + self.init(testdir) + self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt') + + class RewriterTests(unittest.TestCase): def setUp(self): diff --git a/test cases/failing/39 libdir must be inside prefix/meson.build b/test cases/failing/39 libdir must be inside prefix/meson.build new file mode 100644 index 0000000..66272ea --- /dev/null +++ b/test cases/failing/39 libdir must be inside prefix/meson.build @@ -0,0 +1,2 @@ +project('libdir prefix', 'c', + default_options : ['libdir=/opt/lib']) diff --git a/test cases/failing/40 prefix absolute/meson.build b/test cases/failing/40 prefix absolute/meson.build new file mode 100644 index 0000000..e2863e7 --- /dev/null +++ b/test cases/failing/40 prefix absolute/meson.build @@ -0,0 +1,2 @@ +project('prefix-abs', 'c', + default_options : ['prefix=some/path/notabs']) diff --git a/tools/cmake2meson.py b/tools/cmake2meson.py index 647d0c3..eae6344 100755 --- a/tools/cmake2meson.py +++ b/tools/cmake2meson.py @@ -24,7 +24,7 @@ class Token: self.lineno = 0 self.colno = 0 -class Statement(): +class Statement: def __init__(self, name, args): self.name = name self.args = args @@ -38,7 +38,7 @@ class Lexer: ('varexp', re.compile(r'\${[-_0-9a-z/A-Z.]+}')), ('id', re.compile('''[,-><${}=+_0-9a-z/A-Z|@.*]+''')), ('eol', re.compile(r'\n')), - ('comment', re.compile(r'\#.*')), + ('comment', re.compile(r'#.*')), ('lparen', re.compile(r'\(')), ('rparen', re.compile(r'\)')), ] @@ -48,7 +48,7 @@ class Lexer: line_start = 0 loc = 0 col = 0 - while(loc < len(code)): + while loc < len(code): matched = False for (tid, reg) in self.token_specification: mo = reg.match(code, loc) @@ -83,7 +83,7 @@ class Lexer: if not matched: raise RuntimeError('Lexer got confused line %d column %d' % (lineno, col)) -class Parser(): +class Parser: def __init__(self, code): self.stream = Lexer().lex(code) self.getsym() @@ -278,6 +278,7 @@ class Converter: for o in self.options: (optname, description, default) = o if default is None: + typestr = '' defaultstr = '' else: if default == 'OFF': |