summaryrefslogtreecommitdiff
path: root/mesonbuild/build.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/build.py')
-rw-r--r--mesonbuild/build.py144
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)):