diff options
Diffstat (limited to 'mesonbuild/build.py')
| -rw-r--r-- | mesonbuild/build.py | 144 |
1 files changed, 58 insertions, 86 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 763788539..61894fe8a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -26,7 +26,7 @@ from . import mlog from .mesonlib import ( File, MesonException, MachineChoice, PerMachine, OrderedSet, listify, extract_as_list, typeslistify, stringlistify, classify_unity_sources, - get_filenames_templates_dict, substitute_values, has_path_sep, + get_filenames_templates_dict, substitute_values, has_path_sep, unholder ) from .compilers import Compiler, is_object, clink_langs, sort_clink, lang_suffixes, is_known_suffix from .linkers import StaticLinker @@ -106,6 +106,12 @@ def get_target_macos_dylib_install_name(ld) -> str: class InvalidArguments(MesonException): pass +class DependencyOverride: + def __init__(self, dep, node, explicit=True): + self.dep = dep + self.node = node + self.explicit = explicit + class Build: """A class that holds the status of one build including all dependencies and so on. @@ -141,6 +147,7 @@ class Build: self.test_setup_default_name = None self.find_overrides = {} self.searched_programs = set() # The list of all programs that have been searched for. + self.dependency_overrides = PerMachine({}, {}) def copy(self): other = Build(self.environment) @@ -341,9 +348,9 @@ class Target: def __init__(self, name, subdir, subproject, build_by_default, for_machine: MachineChoice): if has_path_sep(name): # Fix failing test 53 when this becomes an error. - mlog.warning('''Target "%s" has a path separator in its name. + mlog.warning('''Target "{}" has a path separator in its name. This is not supported, it can cause unexpected failures and will become -a hard error in the future.''' % name) +a hard error in the future.'''.format(name)) self.name = name self.subdir = subdir self.subproject = subproject @@ -502,7 +509,7 @@ class BuildTarget(Target): self.check_unknown_kwargs(kwargs) self.process_compilers() if not any([self.sources, self.generated, self.objects, self.link_whole]): - raise InvalidArguments('Build target %s has no sources.' % name) + raise InvalidArguments('Build target {} has no sources.'.format(name)) self.process_compilers_late() self.validate_sources() self.validate_install(environment) @@ -530,14 +537,11 @@ class BuildTarget(Target): if k not in known_kwargs: unknowns.append(k) if len(unknowns) > 0: - mlog.warning('Unknown keyword argument(s) in target %s: %s.' % - (self.name, ', '.join(unknowns))) + mlog.warning('Unknown keyword argument(s) in target {}: {}.'.format(self.name, ', '.join(unknowns))) def process_objectlist(self, objects): assert(isinstance(objects, list)) - for s in objects: - if hasattr(s, 'held_object'): - s = s.held_object + for s in unholder(objects): if isinstance(s, (str, File, ExtractedObjects)): self.objects.append(s) elif isinstance(s, (GeneratedList, CustomTarget)): @@ -553,10 +557,7 @@ class BuildTarget(Target): def process_sourcelist(self, sources): sources = listify(sources) added_sources = {} # If the same source is defined multiple times, use it only once. - for s in sources: - # Holder unpacking. Ugly. - if hasattr(s, 'held_object'): - s = s.held_object + for s in unholder(sources): if isinstance(s, File): if s not in added_sources: self.sources.append(s) @@ -633,9 +634,7 @@ class BuildTarget(Target): # which is what we need. if not is_object(s): sources.append(s) - for d in self.external_deps: - if hasattr(d, 'held_object'): - d = d.held_object + for d in unholder(self.external_deps): for s in d.sources: if isinstance(s, (str, File)): sources.append(s) @@ -707,10 +706,7 @@ class BuildTarget(Target): link_depends. """ sources = listify(sources) - for s in sources: - if hasattr(s, 'held_object'): - s = s.held_object - + for s in unholder(sources): if isinstance(s, File): self.link_depends.append(s) elif isinstance(s, str): @@ -766,7 +762,7 @@ class BuildTarget(Target): raise MesonException('Object extraction arguments must be strings or Files.') # FIXME: It could be a generated source if src not in self.sources: - raise MesonException('Tried to extract unknown source %s.' % src) + raise MesonException('Tried to extract unknown source {}.'.format(src)) obj_src.append(src) return ExtractedObjects(self, obj_src) @@ -821,11 +817,7 @@ class BuildTarget(Target): kwargs.get('modules', []) self.need_install = kwargs.get('install', self.need_install) llist = extract_as_list(kwargs, 'link_with') - for linktarget in llist: - # Sorry for this hack. Keyword targets are kept in holders - # in kwargs. Unpack here without looking at the exact type. - if hasattr(linktarget, "held_object"): - linktarget = linktarget.held_object + for linktarget in unholder(llist): if isinstance(linktarget, dependencies.ExternalLibrary): raise MesonException('''An external library was used in link_with keyword argument, which is reserved for libraries built as part of this project. External @@ -838,8 +830,7 @@ just like those detected with the dependency() function.''') self.link_whole(linktarget) c_pchlist, cpp_pchlist, clist, cpplist, cudalist, cslist, valalist, objclist, objcpplist, fortranlist, rustlist \ - = extract_as_list(kwargs, 'c_pch', 'cpp_pch', 'c_args', 'cpp_args', 'cuda_args', 'cs_args', 'vala_args', 'objc_args', - 'objcpp_args', 'fortran_args', 'rust_args') + = [extract_as_list(kwargs, c) for c in ['c_pch', 'cpp_pch', 'c_args', 'cpp_args', 'cuda_args', 'cs_args', 'vala_args', 'objc_args', 'objcpp_args', 'fortran_args', 'rust_args']] self.add_pch('c', c_pchlist) self.add_pch('cpp', cpp_pchlist) @@ -867,7 +858,7 @@ just like those detected with the dependency() function.''') if dfeature_debug: dfeatures['debug'] = dfeature_debug if 'd_import_dirs' in kwargs: - dfeature_import_dirs = extract_as_list(kwargs, 'd_import_dirs', unholder=True) + dfeature_import_dirs = unholder(extract_as_list(kwargs, 'd_import_dirs')) for d in dfeature_import_dirs: if not isinstance(d, IncludeDirs): raise InvalidArguments('Arguments to d_import_dirs must be include_directories.') @@ -911,7 +902,7 @@ This will become a hard error in a future Meson release.''') assert(isinstance(i, File)) trial = os.path.join(environment.get_source_dir(), i.subdir, i.fname) if not(os.path.isfile(trial)): - raise InvalidArguments('Tried to add non-existing extra file %s.' % i) + raise InvalidArguments('Tried to add non-existing extra file {}.'.format(i)) self.extra_files = extra_files self.install_rpath = kwargs.get('install_rpath', '') if not isinstance(self.install_rpath, str): @@ -925,7 +916,7 @@ This will become a hard error in a future Meson release.''') raise InvalidArguments('Resource argument is not a string.') trial = os.path.join(environment.get_source_dir(), self.subdir, r) if not os.path.isfile(trial): - raise InvalidArguments('Tried to add non-existing resource %s.' % r) + raise InvalidArguments('Tried to add non-existing resource {}.'.format(r)) self.resources = resources if 'name_prefix' in kwargs: name_prefix = kwargs['name_prefix'] @@ -974,8 +965,7 @@ This will become a hard error in a future Meson release.''') if self.gnu_symbol_visibility != '': permitted = ['default', 'internal', 'hidden', 'protected', 'inlineshidden'] if self.gnu_symbol_visibility not in permitted: - raise InvalidArguments('GNU symbol visibility arg %s not one of: %s', - self.symbol_visibility, ', '.join(permitted)) + raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.symbol_visibility, ', '.join(permitted))) def _extract_pic_pie(self, kwargs, arg): # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags @@ -1046,9 +1036,7 @@ This will become a hard error in a future Meson release.''') def add_deps(self, deps): deps = listify(deps) - for dep in deps: - if hasattr(dep, 'held_object'): - dep = dep.held_object + for dep in unholder(deps): if isinstance(dep, dependencies.InternalDependency): # Those parts that are internal. self.process_sourcelist(dep.sources) @@ -1095,7 +1083,7 @@ You probably should put it in link_with instead.''') return isinstance(self, StaticLibrary) and not self.need_install def link(self, target): - for t in listify(target, unholder=True): + for t in unholder(listify(target)): if isinstance(self, StaticLibrary) and self.need_install and t.is_internal(): # When we're a static library and we link_with to an # internal/convenience library, promote to link_whole. @@ -1117,7 +1105,7 @@ You probably should put it in link_with instead.''') self.link_targets.append(t) def link_whole(self, target): - for t in listify(target, unholder=True): + for t in unholder(listify(target)): if isinstance(t, (CustomTarget, CustomTargetIndex)): if not t.is_linkable_target(): raise InvalidArguments('Custom target {!r} is not linkable.'.format(t)) @@ -1156,7 +1144,7 @@ You probably should put it in link_with instead.''') return elif len(pchlist) == 1: if not environment.is_header(pchlist[0]): - raise InvalidArguments('PCH argument %s is not a header.' % pchlist[0]) + raise InvalidArguments('PCH argument {} is not a header.'.format(pchlist[0])) elif len(pchlist) == 2: if environment.is_header(pchlist[0]): if not environment.is_source(pchlist[1]): @@ -1166,7 +1154,7 @@ You probably should put it in link_with instead.''') raise InvalidArguments('PCH definition must contain one header and at most one source.') pchlist = [pchlist[1], pchlist[0]] else: - raise InvalidArguments('PCH argument %s is of unknown type.' % pchlist[0]) + raise InvalidArguments('PCH argument {} is of unknown type.'.format(pchlist[0])) if (os.path.dirname(pchlist[0]) != os.path.dirname(pchlist[1])): raise InvalidArguments('PCH files must be stored in the same folder.') @@ -1178,15 +1166,12 @@ You probably should put it in link_with instead.''') if not isinstance(f, str): raise MesonException('PCH arguments must be strings.') if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, f)): - raise MesonException('File %s does not exist.' % f) + raise MesonException('File {} does not exist.'.format(f)) self.pch[language] = pchlist def add_include_dirs(self, args, set_is_system: T.Optional[str] = None): ids = [] - for a in args: - # FIXME same hack, forcibly unpack from holder. - if hasattr(a, 'held_object'): - a = a.held_object + for a in unholder(args): if not isinstance(a, IncludeDirs): raise InvalidArguments('Include directory to be added is not an include directory object.') ids.append(a) @@ -1273,6 +1258,8 @@ You probably should put it in link_with instead.''') if dl != linker.language: stdlib_args += all_compilers[dl].language_stdlib_only_link_flags() added_languages.add(dl) + # Type of var 'linker' is Compiler. + # Pretty hard to fix because the return value is passed everywhere return linker, stdlib_args m = 'Could not get a dynamic linker for build target {!r}' @@ -1300,9 +1287,9 @@ You probably should put it in link_with instead.''') 2. If the target contains only objects, process_compilers guesses and picks the first compiler that smells right. ''' - linker, _ = self.get_clink_dynamic_linker_and_stdlibs() + compiler, _ = self.get_clink_dynamic_linker_and_stdlibs() # Mixing many languages with MSVC is not supported yet so ignore stdlibs. - if linker and linker.get_id() in {'msvc', 'clang-cl', 'intel-cl', 'llvm', 'dmd', 'nvcc'}: + if compiler and compiler.get_linker_id() in {'link', 'lld-link', 'xilink', 'optlink'}: return True return False @@ -1324,9 +1311,7 @@ class Generator: def __init__(self, args, kwargs): if len(args) != 1: raise InvalidArguments('Generator requires exactly one positional argument: the executable') - exe = args[0] - if hasattr(exe, 'held_object'): - exe = exe.held_object + exe = unholder(args[0]) if not isinstance(exe, (Executable, dependencies.ExternalProgram)): raise InvalidArguments('First generator argument must be an executable.') self.exe = exe @@ -1382,7 +1367,7 @@ class Generator: raise InvalidArguments('Capture must be boolean.') self.capture = capture if 'depends' in kwargs: - depends = listify(kwargs['depends'], unholder=True) + depends = unholder(listify(kwargs['depends'])) for d in depends: if not isinstance(d, BuildTarget): raise InvalidArguments('Depends entries must be build targets.') @@ -1427,9 +1412,7 @@ class Generator: class GeneratedList: def __init__(self, generator, subdir, preserve_path_from=None, extra_args=None): - if hasattr(generator, 'held_object'): - generator = generator.held_object - self.generator = generator + self.generator = unholder(generator) self.name = self.generator.exe self.subdir = subdir self.infilelist = [] @@ -1439,10 +1422,10 @@ class GeneratedList: self.depend_files = [] self.preserve_path_from = preserve_path_from self.extra_args = extra_args if extra_args is not None else [] - if isinstance(generator.exe, dependencies.ExternalProgram): - if not generator.exe.found(): + if isinstance(self.generator.exe, dependencies.ExternalProgram): + if not self.generator.exe.found(): raise InvalidArguments('Tried to use not-found external program as generator') - path = generator.exe.get_path() + path = self.generator.exe.get_path() if os.path.isabs(path): # Can only add a dependency on an external program which we # know the absolute path of @@ -1884,9 +1867,7 @@ class SharedLibrary(BuildTarget): # Visual Studio module-definitions file if 'vs_module_defs' in kwargs: - path = kwargs['vs_module_defs'] - if hasattr(path, 'held_object'): - path = path.held_object + path = unholder(kwargs['vs_module_defs']) if isinstance(path, str): if os.path.isabs(path): self.vs_module_defs = File.from_absolute_file(path) @@ -2026,8 +2007,7 @@ class CustomTarget(Target): if k not in CustomTarget.known_kwargs: unknowns.append(k) if len(unknowns) > 0: - mlog.warning('Unknown keyword arguments in target %s: %s' % - (self.name, ', '.join(unknowns))) + mlog.warning('Unknown keyword arguments in target {}: {}'.format(self.name, ', '.join(unknowns))) def get_default_install_dir(self, environment): return None @@ -2039,9 +2019,7 @@ class CustomTarget(Target): def get_target_dependencies(self): deps = self.dependencies[:] deps += self.extra_depends - for c in self.sources: - if hasattr(c, 'held_object'): - c = c.held_object + for c in unholder(self.sources): if isinstance(c, (BuildTarget, CustomTarget)): deps.append(c) return deps @@ -2065,7 +2043,7 @@ class CustomTarget(Target): return bdeps def flatten_command(self, cmd): - cmd = listify(cmd, unholder=True) + cmd = unholder(listify(cmd)) final_cmd = [] for c in cmd: if isinstance(c, str): @@ -2093,7 +2071,7 @@ class CustomTarget(Target): def process_kwargs(self, kwargs, backend): self.process_kwargs_base(kwargs) - self.sources = extract_as_list(kwargs, 'input', unholder=True) + self.sources = unholder(extract_as_list(kwargs, 'input')) if 'output' not in kwargs: raise InvalidArguments('Missing keyword argument "output".') self.outputs = listify(kwargs['output']) @@ -2172,13 +2150,11 @@ class CustomTarget(Target): self.build_always_stale = kwargs['build_always_stale'] if not isinstance(self.build_always_stale, bool): raise InvalidArguments('Argument build_always_stale must be a boolean.') - extra_deps, depend_files = extract_as_list(kwargs, 'depends', 'depend_files', pop = False) - for ed in extra_deps: - while hasattr(ed, 'held_object'): - ed = ed.held_object + extra_deps, depend_files = [extract_as_list(kwargs, c, pop=False) for c in ['depends', 'depend_files']] + for ed in unholder(extra_deps): if not isinstance(ed, (CustomTarget, BuildTarget)): - raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target (executable or a library) got: %s(%s)' - % (type(ed), ed)) + raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target (executable or a library) got: {}({})' + .format(type(ed), ed)) self.extra_depends.append(ed) for i in depend_files: if isinstance(i, (File, str)): @@ -2210,9 +2186,7 @@ class CustomTarget(Target): def get_generated_lists(self): genlists = [] - for c in self.sources: - if hasattr(c, 'held_object'): - c = c.held_object + for c in unholder(self.sources): if isinstance(c, GeneratedList): genlists.append(c) return genlists @@ -2318,10 +2292,10 @@ class Jar(BuildTarget): super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs) for s in self.sources: if not s.endswith('.java'): - raise InvalidArguments('Jar source %s is not a java file.' % s) + raise InvalidArguments('Jar source {} is not a java file.'.format(s)) for t in self.link_targets: if not isinstance(t, Jar): - raise InvalidArguments('Link target %s is not a jar target.' % t) + raise InvalidArguments('Link target {} is not a jar target.'.format(t)) self.filename = self.name + '.jar' self.outputs = [self.filename] self.java_args = kwargs.get('java_args', []) @@ -2420,20 +2394,20 @@ class ConfigureFile: return self.targetname class ConfigurationData: - def __init__(self): + def __init__(self) -> None: super().__init__() - self.values = {} + self.values = {} # T.Dict[str, T.Union[str, int, bool]] def __repr__(self): return repr(self.values) - def __contains__(self, value): + def __contains__(self, value: str) -> bool: return value in self.values - def get(self, name): + def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: return self.values[name] # (val, desc) - def keys(self): + def keys(self) -> T.Iterator[str]: return self.values.keys() # A bit poorly named, but this represents plain data files to copy @@ -2475,9 +2449,7 @@ def get_sources_string_names(sources, backend): get all the output basenames. ''' names = [] - for s in sources: - if hasattr(s, 'held_object'): - s = s.held_object + for s in unholder(sources): if isinstance(s, str): names.append(s) elif isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)): |
