diff options
Diffstat (limited to 'mesonbuild/interpreter.py')
| -rw-r--r-- | mesonbuild/interpreter.py | 244 |
1 files changed, 172 insertions, 72 deletions
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 0303e6a18..b8d4fec75 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -29,10 +29,11 @@ from .interpreterbase import InterpreterBase from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound -from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs +from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs from .interpreterbase import ObjectHolder from .modules import ModuleReturnValue from .cmake import CMakeInterpreter +from .backend.backends import TestProtocol from pathlib import Path, PurePath import os @@ -509,11 +510,14 @@ class DependencyHolder(InterpreterObject, ObjectHolder): return DependencyHolder(new_dep, self.subproject) class ExternalProgramHolder(InterpreterObject, ObjectHolder): - def __init__(self, ep): + def __init__(self, ep, subproject, backend=None): InterpreterObject.__init__(self) ObjectHolder.__init__(self, ep) + self.subproject = subproject + self.backend = backend self.methods.update({'found': self.found_method, - 'path': self.path_method}) + 'path': self.path_method, + 'full_path': self.full_path_method}) self.cached_version = None @noPosargs @@ -523,8 +527,22 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): @noPosargs @permittedKwargs({}) + @FeatureDeprecated('ExternalProgram.path', '0.55.0', + 'use ExternalProgram.full_path() instead') def path_method(self, args, kwargs): - return self.held_object.get_path() + return self._full_path() + + @noPosargs + @permittedKwargs({}) + @FeatureNew('ExternalProgram.full_path', '0.55.0') + def full_path_method(self, args, kwargs): + return self._full_path() + + def _full_path(self): + exe = self.held_object + if isinstance(exe, build.Executable): + return self.backend.get_target_filename_abs(exe) + return exe.get_path() def found(self): return isinstance(self.held_object, build.Executable) or self.held_object.found() @@ -533,9 +551,14 @@ class ExternalProgramHolder(InterpreterObject, ObjectHolder): return self.held_object.get_command() def get_name(self): - return self.held_object.get_name() + exe = self.held_object + if isinstance(exe, build.Executable): + return exe.name + return exe.get_name() def get_version(self, interpreter): + if isinstance(self.held_object, build.Executable): + return self.held_object.project_version if not self.cached_version: raw_cmd = self.get_command() + ['--version'] cmd = [self, '--version'] @@ -958,7 +981,7 @@ class Test(InterpreterObject): self.should_fail = should_fail self.timeout = timeout self.workdir = workdir - self.protocol = protocol + self.protocol = TestProtocol.from_str(protocol) self.priority = priority def get_exe(self): @@ -1780,6 +1803,11 @@ class ModuleHolder(InterpreterObject, ObjectHolder): target_machine=self.interpreter.builtin['target_machine'].held_object, current_node=self.current_node ) + # Many modules do for example self.interpreter.find_program_impl(), + # so we have to ensure they use the current interpreter and not the one + # that first imported that module, otherwise it will use outdated + # overrides. + self.held_object.interpreter = self.interpreter if self.held_object.is_snippet(method_name): value = fn(self.interpreter, state, args, kwargs) return self.interpreter.holderify(value) @@ -1846,6 +1874,7 @@ class MesonMain(InterpreterObject): self.methods.update({'get_compiler': self.get_compiler_method, 'is_cross_build': self.is_cross_build_method, 'has_exe_wrapper': self.has_exe_wrapper_method, + 'can_run_host_binaries': self.can_run_host_binaries_method, 'is_unity': self.is_unity_method, 'is_subproject': self.is_subproject_method, 'current_source_dir': self.current_source_dir_method, @@ -1867,48 +1896,101 @@ class MesonMain(InterpreterObject): 'backend': self.backend_method, }) - def _find_source_script(self, name, args): + def _find_source_script(self, prog: T.Union[str, ExecutableHolder], args): + if isinstance(prog, ExecutableHolder): + prog_path = self.interpreter.backend.get_target_filename(prog.held_object) + return build.RunScript([prog_path], args) + elif isinstance(prog, ExternalProgramHolder): + return build.RunScript(prog.get_command(), args) + # Prefer scripts in the current source directory search_dir = os.path.join(self.interpreter.environment.source_dir, self.interpreter.subdir) - key = (name, search_dir) + key = (prog, search_dir) if key in self._found_source_scripts: found = self._found_source_scripts[key] else: - found = dependencies.ExternalProgram(name, search_dir=search_dir) + found = dependencies.ExternalProgram(prog, search_dir=search_dir) if found.found(): self._found_source_scripts[key] = found else: m = 'Script or command {!r} not found or not executable' - raise InterpreterException(m.format(name)) + raise InterpreterException(m.format(prog)) return build.RunScript(found.get_command(), args) - @permittedKwargs({}) - def add_install_script_method(self, args, kwargs): + def _process_script_args( + self, name: str, args: T.List[T.Union[ + str, mesonlib.File, CustomTargetHolder, + CustomTargetIndexHolder, ConfigureFileHolder, + ExternalProgramHolder, ExecutableHolder, + ]], allow_built: bool = False) -> T.List[str]: + script_args = [] # T.List[str] + new = False + for a in args: + a = unholder(a) + if isinstance(a, str): + script_args.append(a) + elif isinstance(a, mesonlib.File): + new = True + script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir)) + elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)): + if not allow_built: + raise InterpreterException('Arguments to {} cannot be built'.format(name)) + new = True + script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()]) + + # This feels really hacky, but I'm not sure how else to fix + # this without completely rewriting install script handling. + # This is complicated by the fact that the install target + # depends on all. + if isinstance(a, build.CustomTargetIndex): + a.target.build_by_default = True + else: + a.build_by_default = True + elif isinstance(a, build.ConfigureFile): + new = True + script_args.append(os.path.join(a.subdir, a.targetname)) + elif isinstance(a, dependencies.ExternalProgram): + script_args.extend(a.command) + new = True + else: + raise InterpreterException( + 'Arguments to {} must be strings, Files, CustomTargets, ' + 'Indexes of CustomTargets, or ConfigureFiles'.format(name)) + if new: + FeatureNew.single_use( + 'Calling "{}" with File, CustomTaget, Index of CustomTarget, ' + 'ConfigureFile, Executable, or ExternalProgram'.format(name), + '0.55.0', self.interpreter.subproject) + return script_args + + @permittedKwargs(set()) + def add_install_script_method(self, args: 'T.Tuple[T.Union[str, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, ConfigureFileHolder], ...]', kwargs): if len(args) < 1: raise InterpreterException('add_install_script takes one or more arguments') - check_stringlist(args, 'add_install_script args must be strings') - script = self._find_source_script(args[0], args[1:]) + script_args = self._process_script_args('add_install_script', args[1:], allow_built=True) + script = self._find_source_script(args[0], script_args) self.build.install_scripts.append(script) - @permittedKwargs({}) + @permittedKwargs(set()) def add_postconf_script_method(self, args, kwargs): if len(args) < 1: raise InterpreterException('add_postconf_script takes one or more arguments') - check_stringlist(args, 'add_postconf_script arguments must be strings') - script = self._find_source_script(args[0], args[1:]) + script_args = self._process_script_args('add_postconf_script', args[1:], allow_built=True) + script = self._find_source_script(args[0], script_args) self.build.postconf_scripts.append(script) - @permittedKwargs({}) + @permittedKwargs(set()) def add_dist_script_method(self, args, kwargs): if len(args) < 1: raise InterpreterException('add_dist_script takes one or more arguments') if len(args) > 1: - FeatureNew('Calling "add_dist_script" with multiple arguments', '0.49.0').use(self.interpreter.subproject) - check_stringlist(args, 'add_dist_script argument must be a string') + FeatureNew.single_use('Calling "add_dist_script" with multiple arguments', + '0.49.0', self.interpreter.subproject) if self.interpreter.subproject != '': raise InterpreterException('add_dist_script may not be used in a subproject.') - script = self._find_source_script(args[0], args[1:]) + script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True) + script = self._find_source_script(args[0], script_args) self.build.dist_scripts.append(script) @noPosargs @@ -1946,9 +2028,16 @@ class MesonMain(InterpreterObject): @noPosargs @permittedKwargs({}) - def has_exe_wrapper_method(self, args, kwargs): - if self.is_cross_build_method(None, None) and \ - self.build.environment.need_exe_wrapper(): + @FeatureDeprecated('meson.has_exe_wrapper', '0.55.0', 'use meson.can_run_host_binaries instead.') + def has_exe_wrapper_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool: + return self.can_run_host_binaries_method(args, kwargs) + + @noPosargs + @permittedKwargs({}) + @FeatureNew('meson.can_run_host_binaries', '0.55.0') + def can_run_host_binaries_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool: + if (self.is_cross_build_method(None, None) and + self.build.environment.need_exe_wrapper()): if self.build.environment.exe_wrapper is None: return False # We return True when exe_wrap is defined, when it's not needed, and @@ -2366,7 +2455,7 @@ class Interpreter(InterpreterBase): elif isinstance(item, dependencies.Dependency): return DependencyHolder(item, self.subproject) elif isinstance(item, dependencies.ExternalProgram): - return ExternalProgramHolder(item) + return ExternalProgramHolder(item, self.subproject) elif hasattr(item, 'held_object'): return item else: @@ -2389,7 +2478,7 @@ class Interpreter(InterpreterBase): elif isinstance(v, build.Data): self.build.data.append(v) elif isinstance(v, dependencies.ExternalProgram): - return ExternalProgramHolder(v) + return ExternalProgramHolder(v, self.subproject) elif isinstance(v, dependencies.InternalDependency): # FIXME: This is special cased and not ideal: # The first source is our new VapiTarget, the rest are deps @@ -2520,7 +2609,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_assert(self, node, args, kwargs): if len(args) == 1: - FeatureNew('assert function without message argument', '0.53.0').use(self.subproject) + FeatureNew.single_use('assert function without message argument', '0.53.0', self.subproject) value = args[0] message = None elif len(args) == 2: @@ -2856,7 +2945,7 @@ external dependencies (including libraries) must go to "dependencies".''') if len(args) > 1: raise InterpreterException('configuration_data takes only one optional positional arguments') elif len(args) == 1: - FeatureNew('configuration_data dictionary', '0.49.0').use(self.subproject) + FeatureNew.single_use('configuration_data dictionary', '0.49.0', self.subproject) initial_values = args[0] if not isinstance(initial_values, dict): raise InterpreterException('configuration_data first argument must be a dictionary') @@ -2896,11 +2985,14 @@ external dependencies (including libraries) must go to "dependencies".''') if ':' in proj_name: raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name)) + # This needs to be evaluated as early as possible, as meson uses this + # for things like deprecation testing. if 'meson_version' in kwargs: cv = coredata.version pv = kwargs['meson_version'] if not mesonlib.version_compare(cv, pv): raise InterpreterException('Meson version is %s but project requires %s' % (cv, pv)) + mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] if os.path.exists(self.option_file): oi = optinterpreter.OptionInterpreter(self.subproject) @@ -2947,10 +3039,6 @@ external dependencies (including libraries) must go to "dependencies".''') self.build.subproject_dir = self.subproject_dir - mesonlib.project_meson_versions[self.subproject] = '' - if 'meson_version' in kwargs: - mesonlib.project_meson_versions[self.subproject] = kwargs['meson_version'] - self.build.projects[self.subproject] = proj_name mlog.log('Project name:', mlog.bold(proj_name)) mlog.log('Project version:', mlog.bold(self.project_version)) @@ -2997,7 +3085,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_message(self, node, args, kwargs): if len(args) > 1: - FeatureNew('message with more than one argument', '0.54.0').use(self.subproject) + FeatureNew.single_use('message with more than one argument', '0.54.0', self.subproject) args_str = [self.get_message_string_arg(i) for i in args] self.message_impl(args_str) @@ -3059,7 +3147,7 @@ external dependencies (including libraries) must go to "dependencies".''') @noKwargs def func_warning(self, node, args, kwargs): if len(args) > 1: - FeatureNew('warning with more than one argument', '0.54.0').use(self.subproject) + FeatureNew.single_use('warning with more than one argument', '0.54.0', self.subproject) args_str = [self.get_message_string_arg(i) for i in args] mlog.warning(*args_str, location=node) @@ -3091,6 +3179,12 @@ external dependencies (including libraries) must go to "dependencies".''') return should def add_languages_for(self, args, required, for_machine: MachineChoice): + langs = set(self.coredata.compilers[for_machine].keys()) + langs.update(args) + if 'vala' in langs: + if 'c' not in langs: + raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.') + success = True for lang in sorted(args, key=compilers.sort_clink): lang = lang.lower() @@ -3128,11 +3222,6 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version) self.build.ensure_static_linker(comp) - langs = self.coredata.compilers[for_machine].keys() - if 'vala' in langs: - if 'c' not in langs: - raise InterpreterException('Compiling Vala requires C. Add C to your project languages and rerun Meson.') - return success def program_from_file_for(self, for_machine, prognames, silent): @@ -3143,7 +3232,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Executable name must be a string') prog = ExternalProgram.from_bin_list(self.environment, for_machine, p) if prog.found(): - return ExternalProgramHolder(prog) + return ExternalProgramHolder(prog, self.subproject) return None def program_from_system(self, args, search_dirs, silent=False): @@ -3170,7 +3259,7 @@ external dependencies (including libraries) must go to "dependencies".''') extprog = dependencies.ExternalProgram(exename, search_dir=search_dir, extra_search_dirs=extra_search_dirs, silent=silent) - progobj = ExternalProgramHolder(extprog) + progobj = ExternalProgramHolder(extprog, self.subproject) if progobj.found(): return progobj @@ -3183,7 +3272,7 @@ external dependencies (including libraries) must go to "dependencies".''') if not silent: mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), '(overridden: %s)' % exe.description()) - return ExternalProgramHolder(exe) + return ExternalProgramHolder(exe, self.subproject, self.backend) return None def store_name_lookups(self, command_names): @@ -3214,11 +3303,11 @@ external dependencies (including libraries) must go to "dependencies".''') progobj = self.program_from_system(args, search_dirs, silent=silent) if progobj is None and args[0].endswith('python3'): prog = dependencies.ExternalProgram('python3', mesonlib.python_command, silent=True) - progobj = ExternalProgramHolder(prog) + progobj = ExternalProgramHolder(prog, self.subproject) if required and (progobj is None or not progobj.found()): raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args)) if progobj is None: - return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) # Only store successful lookups self.store_name_lookups(args) if wanted: @@ -3231,7 +3320,7 @@ external dependencies (including libraries) must go to "dependencies".''') if required: m = 'Invalid version of program, need {!r} {!r} found {!r}.' raise InvalidArguments(m.format(progobj.get_name(), not_found, version)) - return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) return progobj @FeatureNewKwargs('find_program', '0.53.0', ['dirs']) @@ -3246,7 +3335,7 @@ external dependencies (including libraries) must go to "dependencies".''') disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: mlog.log('Program', mlog.bold(' '.join(args)), 'skipped: feature', mlog.bold(feature), 'disabled') - return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args))) + return ExternalProgramHolder(dependencies.NonExistingExternalProgram(' '.join(args)), self.subproject) search_dirs = extract_search_dirs(kwargs) wanted = mesonlib.stringlistify(kwargs.get('version', [])) @@ -3261,7 +3350,7 @@ external dependencies (including libraries) must go to "dependencies".''') 'Look here for example: http://mesonbuild.com/howtox.html#add-math-library-lm-portably\n' ) - def _find_cached_dep(self, name, kwargs): + def _find_cached_dep(self, name, display_name, kwargs): # Check if we want this as a build-time / build machine or runt-time / # host machine dep. for_machine = self.machine_from_native_kwarg(kwargs) @@ -3276,7 +3365,7 @@ external dependencies (including libraries) must go to "dependencies".''') # have explicitly called meson.override_dependency() with a not-found # dep. if not cached_dep.found(): - mlog.log('Dependency', mlog.bold(name), + mlog.log('Dependency', mlog.bold(display_name), 'found:', mlog.red('NO'), *info) return identifier, cached_dep found_vers = cached_dep.get_version() @@ -3298,7 +3387,7 @@ external dependencies (including libraries) must go to "dependencies".''') if cached_dep: if found_vers: info = [mlog.normal_cyan(found_vers), *info] - mlog.log('Dependency', mlog.bold(name), + mlog.log('Dependency', mlog.bold(display_name), 'found:', mlog.green('YES'), *info) return identifier, cached_dep @@ -3321,7 +3410,7 @@ external dependencies (including libraries) must go to "dependencies".''') return dep = subi.get_variable_method([varname], {}) if dep.held_object != cached_dep: - m = 'Inconsistency: Subproject has overriden the dependency with another variable than {!r}' + m = 'Inconsistency: Subproject has overridden the dependency with another variable than {!r}' raise DependencyException(m.format(varname)) def get_subproject_dep(self, name, display_name, dirname, varname, kwargs): @@ -3331,9 +3420,9 @@ external dependencies (including libraries) must go to "dependencies".''') dep = self.notfound_dependency() try: subproject = self.subprojects[dirname] - _, cached_dep = self._find_cached_dep(name, kwargs) + _, cached_dep = self._find_cached_dep(name, display_name, kwargs) if varname is None: - # Assuming the subproject overriden the dependency we want + # Assuming the subproject overridden the dependency we want if cached_dep: if required and not cached_dep.found(): m = 'Dependency {!r} is not satisfied' @@ -3382,15 +3471,15 @@ external dependencies (including libraries) must go to "dependencies".''') def _handle_featurenew_dependencies(self, name): 'Do a feature check on dependencies used by this subproject' if name == 'mpi': - FeatureNew('MPI Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('MPI Dependency', '0.42.0', self.subproject) elif name == 'pcap': - FeatureNew('Pcap Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('Pcap Dependency', '0.42.0', self.subproject) elif name == 'vulkan': - FeatureNew('Vulkan Dependency', '0.42.0').use(self.subproject) + FeatureNew.single_use('Vulkan Dependency', '0.42.0', self.subproject) elif name == 'libwmf': - FeatureNew('LibWMF Dependency', '0.44.0').use(self.subproject) + FeatureNew.single_use('LibWMF Dependency', '0.44.0', self.subproject) elif name == 'openmp': - FeatureNew('OpenMP Dependency', '0.46.0').use(self.subproject) + FeatureNew.single_use('OpenMP Dependency', '0.46.0', self.subproject) @FeatureNewKwargs('dependency', '0.54.0', ['components']) @FeatureNewKwargs('dependency', '0.52.0', ['include_type']) @@ -3404,6 +3493,9 @@ external dependencies (including libraries) must go to "dependencies".''') self.validate_arguments(args, 1, [str]) name = args[0] display_name = name if name else '(anonymous)' + mods = extract_as_list(kwargs, 'modules') + if mods: + display_name += ' (modules: {})'.format(', '.join(str(i) for i in mods)) not_found_message = kwargs.get('not_found_message', '') if not isinstance(not_found_message, str): raise InvalidArguments('The not_found_message must be a string.') @@ -3445,7 +3537,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InvalidArguments('Characters <, > and = are forbidden in dependency names. To specify' 'version\n requirements use the \'version\' keyword argument instead.') - identifier, cached_dep = self._find_cached_dep(name, kwargs) + identifier, cached_dep = self._find_cached_dep(name, display_name, kwargs) if cached_dep: if has_fallback: dirname, varname = self.get_subproject_infos(kwargs) @@ -3509,7 +3601,7 @@ external dependencies (including libraries) must go to "dependencies".''') def get_subproject_infos(self, kwargs): fbinfo = mesonlib.stringlistify(kwargs['fallback']) if len(fbinfo) == 1: - FeatureNew('Fallback without variable name', '0.53.0').use(self.subproject) + FeatureNew.single_use('Fallback without variable name', '0.53.0', self.subproject) return fbinfo[0], None elif len(fbinfo) != 2: raise InterpreterException('Fallback info must have one or two items.') @@ -3597,11 +3689,13 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Unknown target_type.') @permittedKwargs(permitted_kwargs['vcs_tag']) + @FeatureDeprecatedKwargs('custom_target', '0.47.0', ['build_always'], + 'combine build_by_default and build_always_stale instead.') def func_vcs_tag(self, node, args, kwargs): if 'input' not in kwargs or 'output' not in kwargs: raise InterpreterException('Keyword arguments input and output must exist') if 'fallback' not in kwargs: - FeatureNew('Optional fallback in vcs_tag', '0.41.0').use(self.subproject) + FeatureNew.single_use('Optional fallback in vcs_tag', '0.41.0', self.subproject) fallback = kwargs.pop('fallback', self.project_version) if not isinstance(fallback, str): raise InterpreterException('Keyword argument fallback must be a string.') @@ -3654,7 +3748,7 @@ external dependencies (including libraries) must go to "dependencies".''') if len(args) != 1: raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name') if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']): - FeatureNew('substitutions in custom_target depfile', '0.47.0').use(self.subproject) + FeatureNew.single_use('substitutions in custom_target depfile', '0.47.0', self.subproject) return self._func_custom_target_impl(node, args, kwargs) def _func_custom_target_impl(self, node, args, kwargs): @@ -3742,6 +3836,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self @FeatureNewKwargs('test', '0.52.0', ['priority']) @permittedKwargs(permitted_kwargs['test']) def func_test(self, node, args, kwargs): + if kwargs.get('protocol') == 'gtest': + FeatureNew.single_use('"gtest" protocol for tests', '0.55.0', self.subproject) self.add_test(node, args, kwargs, True) def unpack_env_kwarg(self, kwargs) -> build.EnvironmentVariables: @@ -3749,7 +3845,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if isinstance(envlist, EnvironmentVariablesHolder): env = envlist.held_object elif isinstance(envlist, dict): - FeatureNew('environment dictionary', '0.52.0').use(self.subproject) + FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject) env = EnvironmentVariablesHolder(envlist) env = env.held_object else: @@ -3761,7 +3857,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self def add_test(self, node, args, kwargs, is_base_test): if len(args) != 2: - raise InterpreterException('Incorrect number of arguments') + raise InterpreterException('test expects 2 arguments, {} given'.format(len(args))) if not isinstance(args[0], str): raise InterpreterException('First argument of test must be a string.') exe = args[1] @@ -3793,8 +3889,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if not isinstance(timeout, int): raise InterpreterException('Timeout must be an integer.') protocol = kwargs.get('protocol', 'exitcode') - if protocol not in ('exitcode', 'tap'): - raise InterpreterException('Protocol must be "exitcode" or "tap".') + if protocol not in {'exitcode', 'tap', 'gtest'}: + raise InterpreterException('Protocol must be "exitcode", "tap", or "gtest".') suite = [] prj = self.subproject if self.is_subproject() else self.build.project_name for s in mesonlib.stringlistify(kwargs.get('suite', '')): @@ -3868,7 +3964,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self absname = os.path.join(self.environment.get_source_dir(), buildfilename) if not os.path.isfile(absname): self.subdir = prev_subdir - raise InterpreterException('Non-existent build file {!r}'.format(buildfilename)) + raise InterpreterException("Non-existent build file '{!s}'".format(buildfilename)) with open(absname, encoding='utf8') as f: code = f.read() assert(isinstance(code, str)) @@ -3916,7 +4012,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self elif isinstance(s, str): source_strings.append(s) else: - raise InvalidArguments('Argument {!r} must be string or file.'.format(s)) + raise InvalidArguments('Argument must be string or file.') sources += self.source_strings_to_files(source_strings) install_dir = kwargs.get('install_dir', None) if not isinstance(install_dir, (str, type(None))): @@ -4065,7 +4161,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if 'configuration' in kwargs: conf = kwargs['configuration'] if isinstance(conf, dict): - FeatureNew('configure_file.configuration dictionary', '0.49.0').use(self.subproject) + FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject) conf = ConfigurationDataHolder(self.subproject, conf) elif not isinstance(conf, ConfigurationDataHolder): raise InterpreterException('Argument "configuration" is not of type configuration_data') @@ -4095,7 +4191,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self conf.mark_used() elif 'command' in kwargs: if len(inputs) > 1: - FeatureNew('multiple inputs in configure_file()', '0.52.0').use(self.subproject) + FeatureNew.single_use('multiple inputs in configure_file()', '0.52.0', self.subproject) # We use absolute paths for input and output here because the cwd # that the command is run from is 'unspecified', so it could change. # Currently it's builddir/subdir for in_builddir else srcdir/subdir. @@ -4190,8 +4286,9 @@ This will become a hard error in the future.''' % kwargs['input'], location=self for a in incdir_strings: if a.startswith(src_root): - raise InvalidArguments('''Tried to form an absolute path to a source dir. You should not do that but use -relative paths instead. + raise InvalidArguments('Tried to form an absolute path to a source dir. ' + 'You should not do that but use relative paths instead.' + ''' To get include path to any directory relative to the current dir do @@ -4342,7 +4439,7 @@ different subdirectory. if len(args) > 1: raise InterpreterException('environment takes only one optional positional arguments') elif len(args) == 1: - FeatureNew('environment positional arguments', '0.52.0').use(self.subproject) + FeatureNew.single_use('environment positional arguments', '0.52.0', self.subproject) initial_values = args[0] if not isinstance(initial_values, dict) and not isinstance(initial_values, list): raise InterpreterException('environment first argument must be a dictionary or a list') @@ -4551,6 +4648,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s kwargs['include_directories'] = self.extract_incdirs(kwargs) target = targetclass(name, self.subdir, self.subproject, for_machine, sources, objs, self.environment, kwargs) + target.project_version = self.project_version if not self.environment.machines.matches_build_machine(for_machine): self.add_cross_stdlib_info(target) @@ -4631,6 +4729,8 @@ This will become a hard error in the future.''', location=self.current_node) if len(args) < 1 or len(args) > 2: raise InvalidCode('Get_variable takes one or two arguments.') varname = args[0] + if isinstance(varname, Disabler): + return varname if not isinstance(varname, str): raise InterpreterException('First argument must be a string.') try: |
