diff options
-rw-r--r-- | mesonbuild/ast/interpreter.py | 9 | ||||
-rw-r--r-- | mesonbuild/build.py | 2 | ||||
-rw-r--r-- | mesonbuild/interpreter/compiler.py | 28 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreter.py | 193 | ||||
-rw-r--r-- | mesonbuild/interpreter/interpreterobjects.py | 548 | ||||
-rw-r--r-- | mesonbuild/interpreter/kwargs.py | 30 | ||||
-rw-r--r-- | mesonbuild/interpreter/mesonmain.py | 23 | ||||
-rw-r--r-- | mesonbuild/modules/python.py | 54 | ||||
-rw-r--r-- | mesonbuild/modules/qt.py | 73 | ||||
-rw-r--r-- | mesonbuild/modules/sourceset.py | 9 | ||||
-rw-r--r-- | mesonbuild/modules/unstable_external_project.py | 3 | ||||
-rw-r--r-- | mesonbuild/modules/unstable_rust.py | 29 | ||||
-rwxr-xr-x | run_mypy.py | 3 | ||||
-rw-r--r-- | test cases/common/178 bothlibraries/meson.build | 3 | ||||
-rw-r--r-- | test cases/native/9 override with exe/meson.build | 2 |
15 files changed, 510 insertions, 499 deletions
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py index 26447ce..b9dfc7b 100644 --- a/mesonbuild/ast/interpreter.py +++ b/mesonbuild/ast/interpreter.py @@ -76,6 +76,9 @@ class MockRunTarget(MesonInterpreterObject): ADD_SOURCE = 0 REMOVE_SOURCE = 1 +_T = T.TypeVar('_T') +_V = T.TypeVar('_V') + class AstInterpreter(InterpreterBase): def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None): super().__init__(source_root, subdir, subproject) @@ -141,6 +144,12 @@ class AstInterpreter(InterpreterBase): 'range': self.func_do_nothing, }) + def _unholder_args(self, args: _T, kwargs: _V) -> T.Tuple[_T, _V]: + return args, kwargs + + def _holderify(self, res: _T) -> _T: + return res + def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> bool: return True diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 47ac954..414a4f8 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -40,7 +40,7 @@ from .compilers import ( is_known_suffix ) from .linkers import StaticLinker -from .interpreterbase import FeatureNew, TYPE_nkwargs, TYPE_nvar +from .interpreterbase import FeatureNew if T.TYPE_CHECKING: from ._typing import ImmutableListProtocol, ImmutableSetProtocol diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py index b67cc88..bf32be3 100644 --- a/mesonbuild/interpreter/compiler.py +++ b/mesonbuild/interpreter/compiler.py @@ -1,7 +1,6 @@ import functools -from .interpreterobjects import (IncludeDirsHolder, ExternalLibraryHolder, - extract_required_kwarg, extract_search_dirs) +from .interpreterobjects import (extract_required_kwarg, extract_search_dirs) from .. import mesonlib from .. import mlog @@ -14,12 +13,12 @@ import typing as T import os if T.TYPE_CHECKING: - from ..environment import Environment + from ..interpreter import Interpreter from ..compilers import Compiler, RunResult class TryRunResultHolder(ObjectHolder['RunResult']): - def __init__(self, res: 'RunResult'): - super().__init__(res) + def __init__(self, res: 'RunResult', interpreter: 'Interpreter'): + super().__init__(res, interpreter) self.methods.update({'returncode': self.returncode_method, 'compiled': self.compiled_method, 'stdout': self.stdout_method, @@ -65,9 +64,9 @@ find_library_permitted_kwargs = { find_library_permitted_kwargs |= {'header_' + k for k in header_permitted_kwargs} class CompilerHolder(ObjectHolder['Compiler']): - def __init__(self, compiler: 'Compiler', env: 'Environment', subproject: str): - super().__init__(compiler, subproject=subproject) - self.environment = env + def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'): + super().__init__(compiler, interpreter) + self.environment = self.env self.methods.update({'compiles': self.compiles_method, 'links': self.links_method, 'get_id': self.get_id_method, @@ -144,9 +143,10 @@ class CompilerHolder(ObjectHolder['Compiler']): args = [] incdirs = mesonlib.extract_as_list(kwargs, 'include_directories') for i in incdirs: - if not isinstance(i, IncludeDirsHolder): + from ..build import IncludeDirs + if not isinstance(i, IncludeDirs): raise InterpreterException('Include directories argument must be an include_directories object.') - for idir in i.held_object.to_string_list(self.environment.get_source_dir()): + for idir in i.to_string_list(self.environment.get_source_dir()): args += self.compiler.get_include_args(idir, False) if not nobuiltins: opts = self.environment.coredata.options @@ -162,7 +162,7 @@ class CompilerHolder(ObjectHolder['Compiler']): final_deps = [] while deps: next_deps = [] - for d in mesonlib.unholder(mesonlib.listify(deps)): + for d in mesonlib.listify(deps): if not isinstance(d, dependencies.Dependency) or d.is_built(): raise InterpreterException('Dependencies must be external dependencies') final_deps.append(d) @@ -223,7 +223,7 @@ class CompilerHolder(ObjectHolder['Compiler']): else: h = mlog.red('NO (%d)' % result.returncode) mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h) - return TryRunResultHolder(result) + return result @noPosargs @permittedKwargs({}) @@ -614,7 +614,7 @@ class CompilerHolder(ObjectHolder['Compiler']): self.environment, self.compiler.language, silent=True) - return ExternalLibraryHolder(lib, self.subproject) + return lib @FeatureNewKwargs('compiler.find_library', '0.51.0', ['static']) @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers']) @@ -659,7 +659,7 @@ class CompilerHolder(ObjectHolder['Compiler']): libtype, libname)) lib = dependencies.ExternalLibrary(libname, linkargs, self.environment, self.compiler.language) - return ExternalLibraryHolder(lib, self.subproject) + return lib @permittedKwargs({}) def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool: diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 1f72d4c..be17c9a 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -66,11 +66,11 @@ if T.TYPE_CHECKING: from . import kwargs # Input source types passed to Targets - SourceInputs = T.Union[FileHolder, GeneratedListHolder, TargetHolder, - CustomTargetIndexHolder, GeneratedObjectsHolder, str] + SourceInputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget, + build.CustomTargetIndex, build.CustomTarget, build.GeneratedList, str] # Input source types passed to the build.Target5 classes SourceOutputs = T.Union[mesonlib.File, build.GeneratedList, - build.BuildTarget, build.CustomTargetIndex, + build.BuildTarget, build.CustomTargetIndex, build.CustomTarget, build.GeneratedList] @@ -191,7 +191,7 @@ known_build_target_kwargs = ( ) TEST_KWARGS: T.List[KwargInfo] = [ - KwargInfo('args', ContainerTypeInfo(list, (str, mesonlib.File, TargetHolder)), + KwargInfo('args', ContainerTypeInfo(list, (str, mesonlib.File, build.Target)), listify=True, default=[]), KwargInfo('should_fail', bool, default=False), KwargInfo('timeout', int, default=30), @@ -201,11 +201,11 @@ TEST_KWARGS: T.List[KwargInfo] = [ default='exitcode', validator=lambda x: 'value must be one of "exitcode", "tap", "gtest", "rust"' if x not in {'exitcode', 'tap', 'gtest', 'rust'} else None, since_values={'gtest': '0.55.0', 'rust': '0.57.0'}), - KwargInfo('depends', ContainerTypeInfo(list, (CustomTargetHolder, BuildTargetHolder)), + KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.BuildTarget)), listify=True, default=[], since='0.46.0'), KwargInfo('priority', int, default=0, since='0.52.0'), # TODO: env needs reworks of the way the environment variable holder itself works probably - KwargInfo('env', (EnvironmentVariablesHolder, list, dict, str)), + KwargInfo('env', (EnvironmentVariablesObject, list, dict, str)), KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=['']), # yes, a list of empty string ] @@ -235,7 +235,7 @@ class Interpreter(InterpreterBase, HoldableObject): def __init__( self, - build: build.Build, + _build: build.Build, backend: T.Optional[Backend] = None, subproject: str = '', subdir: str = '', @@ -246,10 +246,10 @@ class Interpreter(InterpreterBase, HoldableObject): ast: T.Optional[mparser.CodeBlockNode] = None, is_translated: bool = False, ) -> None: - super().__init__(build.environment.get_source_dir(), subdir, subproject) + super().__init__(_build.environment.get_source_dir(), subdir, subproject) self.an_unpicklable_object = mesonlib.an_unpicklable_object - self.build = build - self.environment = build.environment + self.build = _build + self.environment = self.build.environment self.coredata = self.environment.get_coredata() self.backend = backend self.summary = {} @@ -268,12 +268,12 @@ class Interpreter(InterpreterBase, HoldableObject): elif ast is not None: self.ast = ast self.sanity_check_ast() - self.builtin.update({'meson': MesonMain(build, self)}) - self.generators: T.List['GeneratorHolder'] = [] + self.builtin.update({'meson': MesonMain(self.build, self)}) + self.generators: T.List[build.Generator] = [] self.processed_buildfiles = set() # type: T.Set[str] self.project_args_frozen = False self.global_args_frozen = False # implies self.project_args_frozen - self.subprojects = {} + self.subprojects: T.Dict[str, SubprojectHolder] = {} self.subproject_stack = [] self.configure_file_outputs = {} # Passed from the outside, only used in subprojects. @@ -283,6 +283,7 @@ class Interpreter(InterpreterBase, HoldableObject): self.default_project_options = {} self.project_default_options = {} self.build_func_dict() + self.build_holder_map() # build_def_files needs to be defined before parse_project is called # @@ -309,11 +310,11 @@ class Interpreter(InterpreterBase, HoldableObject): assert self.build.environment.machines.target.cpu is not None self.builtin['build_machine'] = \ - MachineHolder(self.build.environment.machines.build) + OBJ.MachineHolder(self.build.environment.machines.build, self) self.builtin['host_machine'] = \ - MachineHolder(self.build.environment.machines.host) + OBJ.MachineHolder(self.build.environment.machines.host, self) self.builtin['target_machine'] = \ - MachineHolder(self.build.environment.machines.target) + OBJ.MachineHolder(self.build.environment.machines.target, self) # TODO: Why is this in interpreter.py and not CoreData or Environment? def get_non_matching_default_options(self) -> T.Iterator[T.Tuple[str, str, coredata.UserOption]]: @@ -564,7 +565,7 @@ class Interpreter(InterpreterBase, HoldableObject): mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node) modname = 'unstable_' + plainname self.import_module(modname) - return ModuleObjectHolder(self.modules[modname], self) + return self.modules[modname] @stringArgs @noKwargs @@ -628,7 +629,7 @@ external dependencies (including libraries) must go to "dependencies".''') dep = dependencies.InternalDependency(version, incs, compile_args, link_args, libs, libs_whole, sources, final_deps, variables) - return DependencyHolder(dep, self.subproject) + return dep @noKwargs def func_assert(self, node, args, kwargs): @@ -668,7 +669,11 @@ external dependencies (including libraries) must go to "dependencies".''') def func_run_command(self, node, args, kwargs): return self.run_command_impl(node, args, kwargs) - def run_command_impl(self, node, args, kwargs, in_builddir=False): + def run_command_impl(self, + node: mparser.BaseNode, + args: T.Sequence[TYPE_nvar], + kwargs: TYPE_nkwargs, + in_builddir: bool = False) -> RunProcess: if len(args) < 1: raise InterpreterException('Not enough arguments') cmd, *cargs = args @@ -748,10 +753,16 @@ external dependencies (including libraries) must go to "dependencies".''') if len(args) != 1: raise InterpreterException('Subproject takes exactly one argument') subp_name = args[0] - return self.do_subproject(subp_name, 'meson', kwargs) + subp = self.do_subproject(subp_name, 'meson', kwargs) + # Update the holder maps from the subproject. Additional entries to the + # holder maps can be added through imported Meson modules + if isinstance(subp.held_object, Interpreter): + self.holder_map.update(subp.held_object.holder_map) + self.bound_holder_map.update(subp.held_object.bound_holder_map) + return subp def disabled_subproject(self, subp_name, disabled_feature=None, exception=None): - sub = SubprojectHolder(None, os.path.join(self.subproject_dir, subp_name), + sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name), disabled_feature=disabled_feature, exception=exception) self.subprojects[subp_name] = sub self.coredata.initialized_subprojects.add(subp_name) @@ -836,6 +847,8 @@ external dependencies (including libraries) must go to "dependencies".''') subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir, self.modules, default_options, ast=ast, is_translated=is_translated) subi.subprojects = self.subprojects + subi.holder_map.update(self.holder_map) + subi.bound_holder_map.update(self.bound_holder_map) subi.subproject_stack = self.subproject_stack + [subp_name] current_active = self.active_projectname @@ -958,7 +971,8 @@ external dependencies (including libraries) must go to "dependencies".''') 'options of other subprojects.') opt = self.get_option_internal(optname) if isinstance(opt, coredata.UserFeatureOption): - return FeatureOptionHolder(self.environment, optname, opt) + opt.name = optname + return opt elif isinstance(opt, coredata.UserOption): return opt.value return opt @@ -974,7 +988,7 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('configuration_data first argument must be a dictionary') else: initial_values = {} - return ConfigurationDataHolder(self.subproject, initial_values) + return ConfigurationDataObject(self.subproject, initial_values) def set_backend(self): # The backend is already set when parsing subprojects @@ -1318,10 +1332,9 @@ external dependencies (including libraries) must go to "dependencies".''') extprog = ExternalProgram(exename, search_dir=search_dir, extra_search_dirs=extra_search_dirs, silent=True) - progobj = ExternalProgramHolder(extprog, self.subproject) - if progobj.found(): - extra_info.append(f"({' '.join(progobj.get_command())})") - return progobj + if extprog.found(): + extra_info.append(f"({' '.join(extprog.get_command())})") + return extprog def program_from_overrides(self, command_names, extra_info): for name in command_names: @@ -1330,7 +1343,7 @@ external dependencies (including libraries) must go to "dependencies".''') if name in self.build.find_overrides: exe = self.build.find_overrides[name] extra_info.append(mlog.blue('(overridden)')) - return ExternalProgramHolder(exe, self.subproject, self.backend) + return exe return None def store_name_lookups(self, command_names): @@ -1348,7 +1361,7 @@ external dependencies (including libraries) must go to "dependencies".''') self.build.find_overrides[name] = exe def notfound_program(self, args): - return ExternalProgramHolder(NonExistingExternalProgram(' '.join(args)), self.subproject) + return NonExistingExternalProgram(' '.join(args)) # TODO update modules to always pass `for_machine`. It is bad-form to assume # the host machine. @@ -1372,22 +1385,28 @@ external dependencies (including libraries) must go to "dependencies".''') if wanted: if version_func: version = version_func(progobj) - else: + elif isinstance(progobj, build.Executable): + interp = self + if progobj.subproject: + interp = self.subprojects[progobj.subproject].held_object + assert isinstance(interp, Interpreter) + version = interp.project_version + elif isinstance(progobj, ExternalProgram): version = progobj.get_version(self) is_found, not_found, found = mesonlib.version_compare_many(version, wanted) if not is_found: - mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'), + mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.red('NO'), 'found', mlog.normal_cyan(version), 'but need:', mlog.bold(', '.join([f"'{e}'" for e in not_found])), *extra_info) if required: m = 'Invalid version of program, need {!r} {!r} found {!r}.' - raise InterpreterException(m.format(progobj.get_name(), not_found, version)) + raise InterpreterException(m.format(progobj.name, not_found, version)) return self.notfound_program(args) extra_info.insert(0, mlog.normal_cyan(version)) # Only store successful lookups self.store_name_lookups(args) - mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.green('YES'), *extra_info) + mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info) return progobj def program_lookup(self, args, for_machine, required, search_dirs, extra_info): @@ -1407,7 +1426,7 @@ external dependencies (including libraries) must go to "dependencies".''') progobj = self.program_from_system(args, search_dirs, extra_info) if progobj is None and args[0].endswith('python3'): prog = ExternalProgram('python3', mesonlib.python_command, silent=True) - progobj = ExternalProgramHolder(prog, self.subproject) if prog.found() else None + progobj = prog if prog.found() else None if progobj is None and fallback and required: progobj = self.find_program_fallback(fallback, args, required, extra_info) @@ -1485,10 +1504,12 @@ external dependencies (including libraries) must go to "dependencies".''') # Ensure the correct include type if 'include_type' in kwargs: wanted = kwargs['include_type'] - actual = d.include_type_method([], {}) + if not isinstance(wanted, str): + raise InvalidArguments('The `include_type` kwarg must be a string') + actual = d.get_include_type() if wanted != actual: mlog.debug(f'Current include type of {names[0]} is {actual}. Converting to requested {wanted}') - d = d.as_system_method([wanted], {}) + d = d.generate_system_dependency(wanted) return d @FeatureNew('disabler', '0.44.0') @@ -1502,16 +1523,16 @@ external dependencies (including libraries) must go to "dependencies".''') @FeatureDeprecatedKwargs('executable', '0.56.0', ['gui_app'], extra_message="Use 'win_subsystem' instead.") @permittedKwargs(build.known_exe_kwargs) def func_executable(self, node, args, kwargs): - return self.build_target(node, args, kwargs, ExecutableHolder) + return self.build_target(node, args, kwargs, build.Executable) @permittedKwargs(build.known_stlib_kwargs) def func_static_lib(self, node, args, kwargs): - return self.build_target(node, args, kwargs, StaticLibraryHolder) + return self.build_target(node, args, kwargs, build.StaticLibrary) @permittedKwargs(build.known_shlib_kwargs) def func_shared_lib(self, node, args, kwargs): - holder = self.build_target(node, args, kwargs, SharedLibraryHolder) - holder.held_object.shared_library_only = True + holder = self.build_target(node, args, kwargs, build.SharedLibrary) + holder.shared_library_only = True return holder @permittedKwargs(known_library_kwargs) @@ -1521,7 +1542,7 @@ external dependencies (including libraries) must go to "dependencies".''') @FeatureNew('shared_module', '0.37.0') @permittedKwargs(build.known_shmod_kwargs) def func_shared_module(self, node, args, kwargs): - return self.build_target(node, args, kwargs, SharedModuleHolder) + return self.build_target(node, args, kwargs, build.SharedModule) @permittedKwargs(known_library_kwargs) def func_library(self, node, args, kwargs): @@ -1529,7 +1550,7 @@ external dependencies (including libraries) must go to "dependencies".''') @permittedKwargs(build.known_jar_kwargs) def func_jar(self, node, args, kwargs): - return self.build_target(node, args, kwargs, JarHolder) + return self.build_target(node, args, kwargs, build.Jar) @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options']) @permittedKwargs(known_build_target_kwargs) @@ -1538,21 +1559,21 @@ external dependencies (including libraries) must go to "dependencies".''') raise InterpreterException('Missing target_type keyword argument') target_type = kwargs.pop('target_type') if target_type == 'executable': - return self.build_target(node, args, kwargs, ExecutableHolder) + return self.build_target(node, args, kwargs, build.Executable) elif target_type == 'shared_library': - return self.build_target(node, args, kwargs, SharedLibraryHolder) + return self.build_target(node, args, kwargs, build.SharedLibrary) elif target_type == 'shared_module': FeatureNew('build_target(target_type: \'shared_module\')', '0.51.0').use(self.subproject) - return self.build_target(node, args, kwargs, SharedModuleHolder) + return self.build_target(node, args, kwargs, build.SharedModule) elif target_type == 'static_library': - return self.build_target(node, args, kwargs, StaticLibraryHolder) + return self.build_target(node, args, kwargs, build.StaticLibrary) elif target_type == 'both_libraries': return self.build_both_libraries(node, args, kwargs) elif target_type == 'library': return self.build_library(node, args, kwargs) elif target_type == 'jar': - return self.build_target(node, args, kwargs, JarHolder) + return self.build_target(node, args, kwargs, build.Jar) else: raise InterpreterException('Unknown target_type.') @@ -1634,8 +1655,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']: if isinstance(kwargs['command'][0], str): kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {}) - tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend), self) - self.add_target(name, tg.held_object) + tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend) + self.add_target(name, tg) return tg @FeatureNewKwargs('run_target', '0.57.0', ['env']) @@ -1721,36 +1742,36 @@ This will become a hard error in the future.''' % kwargs['input'], location=self self.generators.append(gen) return gen - @typed_pos_args('benchmark', str, (ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File)) + @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File)) @typed_kwargs('benchmark', *TEST_KWARGS) def func_benchmark(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]], kwargs: 'kwargs.FuncBenchmark') -> None: self.add_test(node, args, kwargs, False) - @typed_pos_args('test', str, (ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File)) + @typed_pos_args('test', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File)) @typed_kwargs('benchmark', *TEST_KWARGS, KwargInfo('is_parallel', bool, default=True)) def func_test(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]], kwargs: 'kwargs.FuncTest') -> None: self.add_test(node, args, kwargs, True) - def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesHolder, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables: - envlist = kwargs.get('env', EnvironmentVariablesHolder()) - if isinstance(envlist, EnvironmentVariablesHolder): - env = envlist.held_object + def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesObject, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables: + envlist = kwargs.get('env', EnvironmentVariablesObject()) + if isinstance(envlist, EnvironmentVariablesObject): + env = envlist.vars elif isinstance(envlist, dict): FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject) - env = EnvironmentVariablesHolder(envlist) - env = env.held_object + env = EnvironmentVariablesObject(envlist) + env = env.vars else: # Convert from array to environment object - env = EnvironmentVariablesHolder(envlist) - env = env.held_object + env = EnvironmentVariablesObject(envlist) + env = env.vars return env def make_test(self, node: mparser.BaseNode, - args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]], + args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]], kwargs: 'kwargs.BaseTest') -> Test: name = args[0] if ':' in name: @@ -1816,7 +1837,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self h = build.Headers(source_files, install_subdir, install_dir, install_mode, self.subproject) self.build.headers.append(h) - return HeadersHolder(h) + return h @FeatureNewKwargs('install_man', '0.47.0', ['install_mode']) @FeatureNewKwargs('install_man', '0.58.0', ['locale']) @@ -1839,7 +1860,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self m = build.Man(sources, custom_install_dir, custom_install_mode, self.subproject, locale) self.build.man.append(m) - return ManHolder(m) + return m @FeatureNewKwargs('subdir', '0.44.0', ['if_found']) @permittedKwargs({'if_found'}) @@ -1937,8 +1958,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self '"rename" and "sources" argument lists must be the same length if "rename" is given. ' f'Rename has {len(rename)} elements and sources has {len(sources)}.') - data = DataHolder(build.Data(sources, install_dir, install_mode, self.subproject, rename)) - self.build.data.append(data.held_object) + data = build.Data(sources, install_dir, install_mode, self.subproject, rename) + self.build.data.append(data) return data @FeatureNewKwargs('install_subdir', '0.42.0', ['exclude_files', 'exclude_directories']) @@ -1986,7 +2007,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self install_mode = self._get_kwarg_install_mode(kwargs) idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject) self.build.install_dirs.append(idir) - return InstallDirHolder(idir) + return idir @FeatureNewKwargs('configure_file', '0.47.0', ['copy', 'output_format', 'install_mode', 'encoding']) @FeatureNewKwargs('configure_file', '0.46.0', ['format']) @@ -2083,8 +2104,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self conf = kwargs['configuration'] if isinstance(conf, dict): FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject) - conf = ConfigurationDataHolder(self.subproject, conf) - elif not isinstance(conf, ConfigurationDataHolder): + conf = ConfigurationDataObject(self.subproject, conf) + elif not isinstance(conf, ConfigurationDataObject): raise InterpreterException('Argument "configuration" is not of type configuration_data') mlog.log('Configuring', mlog.bold(output), 'using configuration') if len(inputs) > 1: @@ -2180,13 +2201,13 @@ This will become a hard error in the future.''' % kwargs['input'], location=self return mesonlib.File.from_built_file(self.subdir, output) def extract_incdirs(self, kwargs): - prospectives = unholder(extract_as_list(kwargs, 'include_directories')) + prospectives = extract_as_list(kwargs, 'include_directories') result = [] for p in prospectives: if isinstance(p, build.IncludeDirs): result.append(p) elif isinstance(p, str): - result.append(self.build_incdir_object([p]).held_object) + result.append(self.build_incdir_object([p])) else: raise InterpreterException('Include directory objects can only be created from strings or include directories.') return result @@ -2196,7 +2217,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self def func_include_directories(self, node, args, kwargs): return self.build_incdir_object(args, kwargs.get('is_system', False)) - def build_incdir_object(self, incdir_strings: T.List[str], is_system: bool = False) -> IncludeDirsHolder: + def build_incdir_object(self, incdir_strings: T.List[str], is_system: bool = False) -> build.IncludeDirs: if not isinstance(is_system, bool): raise InvalidArguments('Is_system must be boolean.') src_root = self.environment.get_source_dir() @@ -2251,7 +2272,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self absdir_build = os.path.join(absbase_build, a) if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build): raise InvalidArguments('Include dir %s does not exist.' % a) - i = IncludeDirsHolder(build.IncludeDirs(self.subdir, incdir_strings, is_system)) + i = build.IncludeDirs(self.subdir, incdir_strings, is_system) return i @permittedKwargs({'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default', @@ -2392,7 +2413,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self raise InterpreterException('environment first argument must be a dictionary or a list') else: initial_values = {} - return EnvironmentVariablesHolder(initial_values, self.subproject) + return EnvironmentVariablesObject(initial_values, self.subproject) @stringArgs @noKwargs @@ -2477,10 +2498,10 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s)) elif isinstance(s, mesonlib.File): results.append(s) - elif isinstance(s, (GeneratedListHolder, TargetHolder, - CustomTargetIndexHolder, - GeneratedObjectsHolder)): - results.append(unholder(s)) + elif isinstance(s, (build.GeneratedList, build.BuildTarget, + build.CustomTargetIndex, build.CustomTarget, + build.GeneratedList)): + results.append(s) else: raise InterpreterException(f'Source item is {s!r} instead of ' 'string or File-type object') @@ -2554,7 +2575,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey else: raise InterpreterException('Unknown default_library value: %s.', default_library) - def build_target(self, node, args, kwargs, targetholder): + def build_target(self, node, args, kwargs, targetclass): @FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories']) @FeatureNewKwargs('build target', '0.41.0', ['rust_args']) @FeatureNewKwargs('build target', '0.40.0', ['build_by_default']) @@ -2578,18 +2599,8 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey ef = extract_as_list(kwargs, 'extra_files') kwargs['extra_files'] = self.source_strings_to_files(ef) self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) - if targetholder == ExecutableHolder: - targetclass = build.Executable - elif targetholder == SharedLibraryHolder: - targetclass = build.SharedLibrary - elif targetholder == SharedModuleHolder: - targetclass = build.SharedModule - elif targetholder == StaticLibraryHolder: - targetclass = build.StaticLibrary - elif targetholder == JarHolder: - targetclass = build.Jar - else: - mlog.debug('Unknown target type:', str(targetholder)) + if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}: + mlog.debug('Unknown target type:', str(targetclass)) raise RuntimeError('Unreachable code') self.kwarg_strings_to_includedirs(kwargs) diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index c0eecca..7c4e75e 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1,8 +1,8 @@ import os import shlex import subprocess -import re import copy +import textwrap from pathlib import Path, PurePath @@ -33,22 +33,22 @@ if T.TYPE_CHECKING: from ..envconfig import MachineInfo -def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str, - feature_check: T.Optional['FeatureCheckBase'] = None, +def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', + subproject: str, + feature_check: T.Optional[FeatureCheckBase] = None, default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]: val = kwargs.get('required', default) disabled = False required = False feature: T.Optional[str] = None - if isinstance(val, FeatureOptionHolder): + if isinstance(val, coredata.UserFeatureOption): if not feature_check: feature_check = FeatureNew('User option "feature"', '0.47.0') feature_check.use(subproject) - option = val.held_object feature = val.name - if option.is_disabled(): + if val.is_disabled(): disabled = True - elif option.is_enabled(): + elif val.is_enabled(): required = True elif isinstance(val, bool): required = val @@ -62,9 +62,9 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str, return disabled, required, feature -def extract_search_dirs(kwargs): - search_dirs = mesonlib.stringlistify(kwargs.get('dirs', [])) - search_dirs = [Path(d).expanduser() for d in search_dirs] +def extract_search_dirs(kwargs: T.Dict[str, T.Any]) -> T.List[str]: + search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', [])) + search_dirs = [Path(d).expanduser() for d in search_dirs_str] for d in search_dirs: if mesonlib.is_windows() and d.root.startswith('\\'): # a Unix-path starting with `/` that is not absolute on Windows. @@ -75,12 +75,12 @@ def extract_search_dirs(kwargs): return list(map(str, search_dirs)) class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): - def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption): - super().__init__(option) + def __init__(self, option: coredata.UserFeatureOption, interpreter: 'Interpreter'): + super().__init__(option, interpreter) if option and option.is_auto(): # TODO: we need to case here because options is not a TypedDict - self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')]) - self.name = name + self.held_object = T.cast(coredata.UserFeatureOption, self.env.coredata.options[OptionKey('auto_features')]) + self.held_object.name = option.name self.methods.update({'enabled': self.enabled_method, 'disabled': self.disabled_method, 'allowed': self.allowed_method, @@ -90,34 +90,36 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): }) @property - def value(self): + def value(self) -> str: return 'disabled' if not self.held_object else self.held_object.value - def as_disabled(self): - return FeatureOptionHolder(None, self.name, None) + def as_disabled(self) -> coredata.UserFeatureOption: + disabled = copy.deepcopy(self.held_object) + disabled.value = 'disabled' + return disabled @noPosargs - @permittedKwargs({}) - def enabled_method(self, args, kwargs): + @noKwargs + def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.value == 'enabled' @noPosargs - @permittedKwargs({}) - def disabled_method(self, args, kwargs): + @noKwargs + def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.value == 'disabled' @noPosargs - @permittedKwargs({}) - def allowed_method(self, args, kwargs): + @noKwargs + def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return not self.value == 'disabled' @noPosargs - @permittedKwargs({}) - def auto_method(self, args, kwargs): + @noKwargs + def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.value == 'auto' @permittedKwargs({'error_message'}) - def require_method(self, args, kwargs): + def require_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption: if len(args) != 1: raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), )) if not isinstance(args[0], bool): @@ -126,38 +128,57 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): if error_message and not isinstance(error_message, str): raise InterpreterException("Error message must be a string.") if args[0]: - return self + return copy.deepcopy(self.held_object) + assert isinstance(error_message, str) if self.value == 'enabled': - prefix = 'Feature {} cannot be enabled'.format(self.name) + prefix = 'Feature {} cannot be enabled'.format(self.held_object.name) prefix = prefix + ': ' if error_message else '' raise InterpreterException(prefix + error_message) return self.as_disabled() - @permittedKwargs({}) - def disable_auto_if_method(self, args, kwargs): + @noKwargs + def disable_auto_if_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption: if len(args) != 1: raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), )) if not isinstance(args[0], bool): raise InvalidArguments('boolean argument expected.') - return self if self.value != 'auto' or not args[0] else self.as_disabled() + return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled() class RunProcess(MesonInterpreterObject): - def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True): + def __init__(self, + cmd: ExternalProgram, + args: T.List[str], + env: build.EnvironmentVariables, + source_dir: str, + build_dir: str, + subdir: str, + mesonintrospect: T.List[str], + in_builddir: bool = False, + check: bool = False, + capture: bool = True) -> None: super().__init__() if not isinstance(cmd, ExternalProgram): raise AssertionError('BUG: RunProcess must be passed an ExternalProgram') self.capture = capture - pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check) - self.returncode = pc.returncode + self.returncode, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check) self.methods.update({'returncode': self.returncode_method, 'stdout': self.stdout_method, 'stderr': self.stderr_method, }) - def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False): + def run_command(self, + cmd: ExternalProgram, + args: T.List[str], + env: build.EnvironmentVariables, + source_dir: str, + build_dir: str, + subdir: str, + mesonintrospect: T.List[str], + in_builddir: bool, + check: bool = False) -> T.Tuple[int, str, str]: command_array = cmd.get_command() + args menv = {'MESON_SOURCE_ROOT': source_dir, 'MESON_BUILD_ROOT': build_dir, @@ -188,28 +209,33 @@ class RunProcess(MesonInterpreterObject): if check and p.returncode != 0: raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode)) - return p, o, e + return p.returncode, o, e except FileNotFoundError: raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array)) @noPosargs - @permittedKwargs({}) - def returncode_method(self, args, kwargs): + @noKwargs + def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int: return self.returncode @noPosargs - @permittedKwargs({}) - def stdout_method(self, args, kwargs): + @noKwargs + def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.stdout @noPosargs - @permittedKwargs({}) - def stderr_method(self, args, kwargs): + @noKwargs + def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.stderr -class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]): - def __init__(self, initial_values=None, subproject: str = ''): - super().__init__(build.EnvironmentVariables(), subproject=subproject) +# TODO: Parsing the initial values should be either done directly in the +# `Interpreter` or in `build.EnvironmentVariables`. This way, this class +# can be converted into a pure object holder. +class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObject): + # TODO: Move the type cheking for initial_values out of this class and replace T.Any + def __init__(self, initial_values: T.Optional[T.Any] = None, subproject: str = ''): + super().__init__(subproject=subproject) + self.vars = build.EnvironmentVariables() self.methods.update({'set': self.set_method, 'append': self.append_method, 'prepend': self.prepend_method, @@ -232,18 +258,18 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En def __repr__(self) -> str: repr_str = "<{0}: {1}>" - return repr_str.format(self.__class__.__name__, self.held_object.envvars) + return repr_str.format(self.__class__.__name__, self.vars.envvars) def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str: separator = kwargs.get('separator', os.pathsep) if not isinstance(separator, str): - raise InterpreterException("EnvironmentVariablesHolder methods 'separator'" + raise InterpreterException("EnvironmentVariablesObject methods 'separator'" " argument needs to be a string.") return separator def warn_if_has_name(self, name: str) -> None: # Multiple append/prepend operations was not supported until 0.58.0. - if self.held_object.has_name(name): + if self.vars.has_name(name): m = f'Overriding previous value of environment variable {name!r} with a new one' FeatureNew('0.58.0', m).use(self.subproject) @@ -253,7 +279,7 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None: name, values = args separator = self.unpack_separator(kwargs) - self.held_object.set(name, values, separator) + self.vars.set(name, values, separator) @stringArgs @permittedKwargs({'separator'}) @@ -262,7 +288,7 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En name, values = args separator = self.unpack_separator(kwargs) self.warn_if_has_name(name) - self.held_object.append(name, values, separator) + self.vars.append(name, values, separator) @stringArgs @permittedKwargs({'separator'}) @@ -271,13 +297,14 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En name, values = args separator = self.unpack_separator(kwargs) self.warn_if_has_name(name) - self.held_object.prepend(name, values, separator) + self.vars.prepend(name, values, separator) -class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.ConfigurationData]): - def __init__(self, subproject: str, initial_values=None): +class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject): + def __init__(self, subproject: str, initial_values: T.Optional[T.Dict[str, T.Any]] = None) -> None: self.used = False # These objects become immutable after use in configure_file. - super().__init__(build.ConfigurationData(), subproject=subproject) + super().__init__(subproject=subproject) + self.conf_data = build.ConfigurationData() self.methods.update({'set': self.set_method, 'set10': self.set10_method, 'set_quoted': self.set_quoted_method, @@ -291,15 +318,15 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.Confi for k, v in initial_values.items(): self.set_method([k, v], {}) elif initial_values: - raise AssertionError('Unsupported ConfigurationDataHolder initial_values') + raise AssertionError('Unsupported ConfigurationDataObject initial_values') - def is_used(self): + def is_used(self) -> bool: return self.used - def mark_used(self): + def mark_used(self) -> None: self.used = True - def validate_args(self, args, kwargs): + def validate_args(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[str, T.Union[str, int, bool], T.Optional[str]]: if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2: mlog.deprecation('Passing a list as the single argument to ' 'configuration_data.set is deprecated. This will ' @@ -323,85 +350,101 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.Confi if desc is not None and not isinstance(desc, str): raise InterpreterException('Description must be a string.') - return name, val, desc + # TODO: Remove the cast once we get rid of the deprecation + return name, T.cast(T.Union[str, bool, int], val), desc @noArgsFlattening - def set_method(self, args, kwargs): + def set_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: (name, val, desc) = self.validate_args(args, kwargs) - self.held_object.values[name] = (val, desc) + self.conf_data.values[name] = (val, desc) - def set_quoted_method(self, args, kwargs): + def set_quoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: (name, val, desc) = self.validate_args(args, kwargs) if not isinstance(val, str): raise InterpreterException("Second argument to set_quoted must be a string.") escaped_val = '\\"'.join(val.split('"')) - self.held_object.values[name] = ('"' + escaped_val + '"', desc) + self.conf_data.values[name] = ('"' + escaped_val + '"', desc) - def set10_method(self, args, kwargs): + def set10_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: (name, val, desc) = self.validate_args(args, kwargs) if val: - self.held_object.values[name] = (1, desc) + self.conf_data.values[name] = (1, desc) else: - self.held_object.values[name] = (0, desc) + self.conf_data.values[name] = (0, desc) - def has_method(self, args, kwargs): - return args[0] in self.held_object.values + def has_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: + return args[0] in self.conf_data.values @FeatureNew('configuration_data.get()', '0.38.0') @noArgsFlattening - def get_method(self, args, kwargs): + def get_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: if len(args) < 1 or len(args) > 2: raise InterpreterException('Get method takes one or two arguments.') + if not isinstance(args[0], str): + raise InterpreterException('The variable name must be a string.') name = args[0] - if name in self.held_object: - return self.held_object.get(name)[0] + if name in self.conf_data: + return self.conf_data.get(name)[0] if len(args) > 1: - return args[1] + # Assertion does not work because setting other values is still + # supported, but deprecated. Use T.cast in the meantime (even though + # this is a lie). + # TODO: Fix this once the deprecation is removed + # assert isinstance(args[1], (int, str, bool)) + return T.cast(T.Union[str, int, bool], args[1]) raise InterpreterException('Entry %s not in configuration data.' % name) @FeatureNew('configuration_data.get_unquoted()', '0.44.0') - def get_unquoted_method(self, args, kwargs): + def get_unquoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]: if len(args) < 1 or len(args) > 2: raise InterpreterException('Get method takes one or two arguments.') + if not isinstance(args[0], str): + raise InterpreterException('The variable name must be a string.') name = args[0] - if name in self.held_object: - val = self.held_object.get(name)[0] + if name in self.conf_data: + val = self.conf_data.get(name)[0] elif len(args) > 1: + assert isinstance(args[1], (str, int, bool)) val = args[1] else: raise InterpreterException('Entry %s not in configuration data.' % name) - if val[0] == '"' and val[-1] == '"': + if isinstance(val, str) and val[0] == '"' and val[-1] == '"': return val[1:-1] return val - def get(self, name): - return self.held_object.values[name] # (val, desc) + def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]: + return self.conf_data.values[name] @FeatureNew('configuration_data.keys()', '0.57.0') @noPosargs - def keys_method(self, args, kwargs): + def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]: return sorted(self.keys()) - def keys(self): - return self.held_object.values.keys() + def keys(self) -> T.List[str]: + return list(self.conf_data.values.keys()) - def merge_from_method(self, args, kwargs): + def merge_from_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None: if len(args) != 1: raise InterpreterException('Merge_from takes one positional argument.') - from_object = args[0] - if not isinstance(from_object, ConfigurationDataHolder): + from_object_holder = args[0] + if not isinstance(from_object_holder, ConfigurationDataObject): raise InterpreterException('Merge_from argument must be a configuration data object.') - from_object = from_object.held_object + from_object = from_object_holder.conf_data for k, v in from_object.values.items(): - self.held_object.values[k] = v + self.conf_data.values[k] = v + -permitted_partial_dependency_kwargs = { - 'compile_args', 'link_args', 'links', 'includes', 'sources' -} +_PARTIAL_DEP_KWARGS = [ + KwargInfo('compile_args', bool, default=False), + KwargInfo('link_args', bool, default=False), + KwargInfo('links', bool, default=False), + KwargInfo('includes', bool, default=False), + KwargInfo('sources', bool, default=False), +] class DependencyHolder(ObjectHolder[Dependency]): - def __init__(self, dep: Dependency, subproject: str): - super().__init__(dep, subproject=subproject) + def __init__(self, dep: Dependency, interpreter: 'Interpreter'): + super().__init__(dep, interpreter) self.methods.update({'found': self.found_method, 'type_name': self.type_name_method, 'version': self.version_method, @@ -415,35 +458,35 @@ class DependencyHolder(ObjectHolder[Dependency]): 'as_link_whole': self.as_link_whole_method, }) - def found(self): + def found(self) -> bool: return self.found_method([], {}) @noPosargs - @permittedKwargs({}) - def type_name_method(self, args, kwargs): + @noKwargs + def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.type_name @noPosargs - @permittedKwargs({}) - def found_method(self, args, kwargs): + @noKwargs + def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: if self.held_object.type_name == 'internal': return True return self.held_object.found() @noPosargs - @permittedKwargs({}) - def version_method(self, args, kwargs): + @noKwargs + def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_version() @noPosargs - @permittedKwargs({}) - def name_method(self, args, kwargs): + @noKwargs + def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_name() @FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0', 'use Dependency.get_variable(pkgconfig : ...) instead') @permittedKwargs({'define_variable', 'default'}) - def pkgconfig_method(self, args, kwargs): + def pkgconfig_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: args = listify(args) if len(args) != 1: raise InterpreterException('get_pkgconfig_variable takes exactly one argument.') @@ -455,8 +498,8 @@ class DependencyHolder(ObjectHolder[Dependency]): @FeatureNew('dep.get_configtool_variable', '0.44.0') @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0', 'use Dependency.get_variable(configtool : ...) instead') - @permittedKwargs({}) - def configtool_method(self, args, kwargs): + @noKwargs + def configtool_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: args = listify(args) if len(args) != 1: raise InterpreterException('get_configtool_variable takes exactly one argument.') @@ -467,16 +510,16 @@ class DependencyHolder(ObjectHolder[Dependency]): @FeatureNew('dep.partial_dependency', '0.46.0') @noPosargs - @permittedKwargs(permitted_partial_dependency_kwargs) - def partial_dependency_method(self, args, kwargs): + @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS) + def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency: pdep = self.held_object.get_partial_dependency(**kwargs) - return DependencyHolder(pdep, self.subproject) + return pdep @FeatureNew('dep.get_variable', '0.51.0') @typed_pos_args('dep.get_variable', optargs=[str]) @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'}) @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal']) - def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> str: + def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> T.Union[str, T.List[str]]: default_varname = args[0] if default_varname is not None: FeatureNew('0.58.0', 'Positional argument to dep.get_variable()').use(self.subproject) @@ -486,129 +529,102 @@ class DependencyHolder(ObjectHolder[Dependency]): @FeatureNew('dep.include_type', '0.52.0') @noPosargs - @permittedKwargs({}) - def include_type_method(self, args, kwargs): + @noKwargs + def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.get_include_type() @FeatureNew('dep.as_system', '0.52.0') - @permittedKwargs({}) - def as_system_method(self, args, kwargs): + @noKwargs + def as_system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency: args = listify(args) new_is_system = 'system' if len(args) > 1: raise InterpreterException('as_system takes only one optional value') if len(args) == 1: + if not isinstance(args[0], str): + raise InterpreterException('as_system takes exactly one string parameter') new_is_system = args[0] new_dep = self.held_object.generate_system_dependency(new_is_system) - return DependencyHolder(new_dep, self.subproject) + return new_dep @FeatureNew('dep.as_link_whole', '0.56.0') - @permittedKwargs({}) + @noKwargs @noPosargs - def as_link_whole_method(self, args, kwargs): + def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency: if not isinstance(self.held_object, InternalDependency): raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects') new_dep = self.held_object.generate_link_whole_dependency() - return DependencyHolder(new_dep, self.subproject) + return new_dep class ExternalProgramHolder(ObjectHolder[ExternalProgram]): - def __init__(self, ep: ExternalProgram, subproject: str, backend=None): - super().__init__(ep, subproject=subproject) - self.backend = backend + def __init__(self, ep: ExternalProgram, interpreter: 'Interpreter') -> None: + super().__init__(ep, interpreter) self.methods.update({'found': self.found_method, 'path': self.path_method, 'full_path': self.full_path_method}) @noPosargs - @permittedKwargs({}) - def found_method(self, args, kwargs): + @noKwargs + def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.found() @noPosargs - @permittedKwargs({}) + @noKwargs @FeatureDeprecated('ExternalProgram.path', '0.55.0', 'use ExternalProgram.full_path() instead') - def path_method(self, args, kwargs): + def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self._full_path() @noPosargs - @permittedKwargs({}) + @noKwargs @FeatureNew('ExternalProgram.full_path', '0.55.0') - def full_path_method(self, args, kwargs): + def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self._full_path() - def _full_path(self): + def _full_path(self) -> str: exe = self.held_object + # TODO: How is this case even possible? Why can this hold a build.Executable? if isinstance(exe, build.Executable): - return self.backend.get_target_filename_abs(exe) + assert self.interpreter.backend is not None + return self.interpreter.backend.get_target_filename_abs(exe) + if not self.found(): + raise InterpreterException('Unable to get the path of a not-found external program') + path = exe.get_path() + assert path is not None return exe.get_path() - def found(self): + def found(self) -> bool: return isinstance(self.held_object, build.Executable) or self.held_object.found() - def get_command(self): - return self.held_object.get_command() - - def get_name(self): - exe = self.held_object - if isinstance(exe, build.Executable): - return exe.name - return exe.get_name() - class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]): - def __init__(self, el: ExternalLibrary, subproject: str): - super().__init__(el, subproject=subproject) + def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'): + super().__init__(el, interpreter) self.methods.update({'found': self.found_method, 'type_name': self.type_name_method, 'partial_dependency': self.partial_dependency_method, }) - def found(self): - return self.held_object.found() - @noPosargs - @permittedKwargs({}) - def type_name_method(self, args, kwargs): + @noKwargs + def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.type_name @noPosargs - @permittedKwargs({}) - def found_method(self, args, kwargs): - return self.found() - - def get_name(self): - return self.held_object.name - - def get_compile_args(self): - return self.held_object.get_compile_args() - - def get_link_args(self): - return self.held_object.get_link_args() - - def get_exe_args(self): - return self.held_object.get_exe_args() + @noKwargs + def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: + return self.held_object.found() @FeatureNew('dep.partial_dependency', '0.46.0') @noPosargs - @permittedKwargs(permitted_partial_dependency_kwargs) - def partial_dependency_method(self, args, kwargs): + @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS) + def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency: pdep = self.held_object.get_partial_dependency(**kwargs) - return DependencyHolder(pdep, self.subproject) - - -class GeneratedListHolder(ObjectHolder[build.GeneratedList]): - def __init__(self, arg1: 'build.GeneratedList'): - super().__init__(arg1) - - def __repr__(self) -> str: - r = '<{}: {!r}>' - return r.format(self.__class__.__name__, self.held_object.get_outputs()) - + return pdep # A machine that's statically known from the cross file class MachineHolder(ObjectHolder['MachineInfo']): - def __init__(self, machine_info: 'MachineInfo'): - super().__init__(machine_info) + def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'): + super().__init__(machine_info, interpreter) self.methods.update({'system': self.system_method, 'cpu': self.cpu_method, 'cpu_family': self.cpu_family_method, @@ -616,88 +632,45 @@ class MachineHolder(ObjectHolder['MachineInfo']): }) @noPosargs - @permittedKwargs({}) - def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: + @noKwargs + def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.cpu_family @noPosargs - @permittedKwargs({}) - def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: + @noKwargs + def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.cpu @noPosargs - @permittedKwargs({}) - def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: + @noKwargs + def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.system @noPosargs - @permittedKwargs({}) - def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: + @noKwargs + def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.held_object.endian class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]): - def __init__(self, idobj: build.IncludeDirs): - super().__init__(idobj) + pass class FileHolder(ObjectHolder[mesonlib.File]): - def __init__(self, fobj: mesonlib.File): - super().__init__(fobj) + pass class HeadersHolder(ObjectHolder[build.Headers]): - def __init__(self, obj: build.Headers): - super().__init__(obj) - - def set_install_subdir(self, subdir): - self.held_object.install_subdir = subdir - - def get_install_subdir(self): - return self.held_object.install_subdir - - def get_sources(self): - return self.held_object.sources - - def get_custom_install_dir(self): - return self.held_object.custom_install_dir - - def get_custom_install_mode(self): - return self.held_object.custom_install_mode + pass class DataHolder(ObjectHolder[build.Data]): - def __init__(self, data: build.Data): - super().__init__(data) - - def get_source_subdir(self): - return self.held_object.source_subdir - - def get_sources(self): - return self.held_object.sources - - def get_install_dir(self): - return self.held_object.install_dir + pass -class InstallDirHolder(ObjectHolder[build.IncludeDirs]): - def __init__(self, obj: build.InstallDir): - super().__init__(obj) +class InstallDirHolder(ObjectHolder[build.InstallDir]): + pass class ManHolder(ObjectHolder[build.Man]): - def __init__(self, obj: build.Man): - super().__init__(obj) - - def get_custom_install_dir(self) -> T.Optional[str]: - return self.held_object.custom_install_dir - - def get_custom_install_mode(self) -> T.Optional[FileMode]: - return self.held_object.custom_install_mode - - def locale(self) -> T.Optional[str]: - return self.held_object.locale - - def get_sources(self) -> T.List[mesonlib.File]: - return self.held_object.sources + pass class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): - def __init__(self, held_object: build.ExtractedObjects): - super().__init__(held_object) + pass class Test(MesonInterpreterObject): def __init__(self, name: str, project: str, suite: T.List[str], exe: build.Executable, @@ -720,17 +693,27 @@ class Test(MesonInterpreterObject): self.protocol = TestProtocol.from_str(protocol) self.priority = priority - def get_exe(self): + def get_exe(self) -> build.Executable: return self.exe - def get_name(self): + def get_name(self) -> str: return self.name -class SubprojectHolder(ObjectHolder[T.Optional['Interpreter']]): +class NullSubprojectInterpreter(HoldableObject): + pass - def __init__(self, subinterpreter: T.Optional['Interpreter'], subdir: str, warnings=0, disabled_feature=None, - exception=None): - super().__init__(subinterpreter) +# TODO: This should really be an `ObjectHolder`, but the additional stuff in this +# class prevents this. Thus, this class should be split into a pure +# `ObjectHolder` and a class specifically for stroing in `Interpreter`. +class SubprojectHolder(MesonInterpreterObject): + + def __init__(self, subinterpreter: T.Union['Interpreter', NullSubprojectInterpreter], + subdir: str, + warnings: int = 0, + disabled_feature: T.Optional[str] = None, + exception: T.Optional[MesonException] = None) -> None: + super().__init__() + self.held_object = subinterpreter self.warnings = warnings self.disabled_feature = disabled_feature self.exception = exception @@ -740,20 +723,20 @@ class SubprojectHolder(ObjectHolder[T.Optional['Interpreter']]): }) @noPosargs - @permittedKwargs({}) - def found_method(self, args, kwargs): + @noKwargs + def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool: return self.found() - def found(self): - return self.held_object is not None + def found(self) -> bool: + return not isinstance(self.held_object, NullSubprojectInterpreter) - @permittedKwargs({}) + @noKwargs @noArgsFlattening @unholder_return def get_variable_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]: if len(args) < 1 or len(args) > 2: raise InterpreterException('Get_variable takes one or two arguments.') - if not self.found(): + if isinstance(self.held_object, NullSubprojectInterpreter): # == not self.found() raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir)) varname = args[0] if not isinstance(varname, str): @@ -768,12 +751,8 @@ class SubprojectHolder(ObjectHolder[T.Optional['Interpreter']]): raise InvalidArguments(f'Requested variable "{varname}" not found.') -class ModuleObjectHolder(ObjectHolder['ModuleObject']): - def __init__(self, modobj: 'ModuleObject', interpreter: 'Interpreter'): - super().__init__(modobj) - self.interpreter = interpreter - - def method_call(self, method_name, args, kwargs): +class ModuleObjectHolder(ObjectHolder[ModuleObject]): + def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var: modobj = self.held_object method = modobj.methods.get(method_name) if not method: @@ -791,10 +770,10 @@ class ModuleObjectHolder(ObjectHolder['ModuleObject']): if isinstance(ret, ModuleReturnValue): self.interpreter.process_new_values(ret.new_objects) ret = ret.return_value - return self.interpreter.holderify(ret) + return ret class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject): - def __deepcopy__(self, memo): + def __deepcopy__(self, memo: T.Dict[int, T.Any]) -> 'MutableModuleObjectHolder': # Deepcopy only held object, not interpreter modobj = copy.deepcopy(self.held_object, memo) return MutableModuleObjectHolder(modobj, self.interpreter) @@ -916,7 +895,7 @@ class SharedModuleHolder(BuildTargetHolder[build.SharedModule]): class JarHolder(BuildTargetHolder[build.Jar]): pass -class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]): +class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]): def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'): super().__init__(target, interp) self.methods.update({'full_path': self.full_path_method, @@ -924,74 +903,69 @@ class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]): @FeatureNew('custom_target[i].full_path', '0.54.0') @noPosargs - @permittedKwargs({}) - def full_path_method(self, args, kwargs): + @noKwargs + def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: + assert self.interpreter.backend is not None return self.interpreter.backend.get_target_filename_abs(self.held_object) -class CustomTargetHolder(TargetHolder[build.CustomTarget]): +class CustomTargetHolder(ObjectHolder[build.CustomTarget]): def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'): super().__init__(target, interp) self.methods.update({'full_path': self.full_path_method, 'to_list': self.to_list_method, }) - def __repr__(self): + def __repr__(self) -> str: r = '<{} {}: {}>' h = self.held_object return r.format(self.__class__.__name__, h.get_id(), h.command) @noPosargs - @permittedKwargs({}) - def full_path_method(self, args, kwargs): + @noKwargs + def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str: return self.interpreter.backend.get_target_filename_abs(self.held_object) @FeatureNew('custom_target.to_list', '0.54.0') @noPosargs - @permittedKwargs({}) - def to_list_method(self, args, kwargs): + @noKwargs + def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]: result = [] for i in self.held_object: - result.append(CustomTargetIndexHolder(i, self.interpreter)) + result.append(i) return result - def __getitem__(self, index): - return CustomTargetIndexHolder(self.held_object[index], self.interpreter) + def __getitem__(self, index: int) -> build.CustomTargetIndex: + return self.held_object[index] - def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method] + def __setitem__(self, index: int, value: T.Any) -> None: # lgtm[py/unexpected-raise-in-special-method] raise InterpreterException('Cannot set a member of a CustomTarget') - def __delitem__(self, index): # lgtm[py/unexpected-raise-in-special-method] + def __delitem__(self, index: int) -> None: # lgtm[py/unexpected-raise-in-special-method] raise InterpreterException('Cannot delete a member of a CustomTarget') - def outdir_include(self): - return IncludeDirsHolder(build.IncludeDirs('', [], False, - [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))])) - -class RunTargetHolder(TargetHolder): - def __init__(self, target, interp): - super().__init__(target, interp) +class RunTargetHolder(ObjectHolder[build.RunTarget]): + pass - def __repr__(self): - r = '<{} {}: {}>' - h = self.held_object - return r.format(self.__class__.__name__, h.get_id(), h.command) +class AliasTargetHolder(ObjectHolder[build.AliasTarget]): + pass +class GeneratedListHolder(ObjectHolder[build.GeneratedList]): + pass class GeneratorHolder(ObjectHolder[build.Generator]): - - def __init__(self, gen: 'build.Generator', interpreter: 'Interpreter'): - self().__init__(self, gen, interpreter.subproject) - self.interpreter = interpreter + def __init__(self, gen: build.Generator, interpreter: 'Interpreter'): + super().__init__(gen, interpreter) self.methods.update({'process': self.process_method}) - @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder)) + @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) @typed_kwargs( 'generator.process', KwargInfo('preserve_path_from', str, since='0.45.0'), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), ) - def process_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder]]], - kwargs: 'kwargs.GeneratorProcess') -> GeneratedListHolder: + def process_method(self, + args: T.Tuple[T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]], + kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList: preserve_path_from = kwargs['preserve_path_from'] if preserve_path_from is not None: preserve_path_from = os.path.normpath(preserve_path_from) @@ -999,12 +973,12 @@ class GeneratorHolder(ObjectHolder[build.Generator]): # This is a bit of a hack. Fix properly before merging. raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.') - if any(isinstance(a, (CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder)) for a in args[0]): + if any(isinstance(a, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for a in args[0]): FeatureNew.single_use( f'Calling generator.process with CustomTaget or Index of CustomTarget.', '0.57.0', self.interpreter.subproject) - gl = self.held_object.process_files(mesonlib.unholder(args[0]), self.interpreter, + gl = self.held_object.process_files(args[0], self.interpreter, preserve_path_from, extra_args=kwargs['extra_args']) - return GeneratedListHolder(gl) + return gl diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index 9734caa..1cc2082 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -4,15 +4,14 @@ """Keyword Argument type annotations.""" +from mesonbuild import coredata import typing as T from typing_extensions import TypedDict, Literal +from .. import build from ..mesonlib import MachineChoice, File -from .interpreterobjects import ( - BuildTargetHolder, CustomTargetHolder, EnvironmentVariablesHolder, - FeatureOptionHolder, TargetHolder -) +from .interpreterobjects import EnvironmentVariablesObject class FuncAddProjectArgs(TypedDict): @@ -34,13 +33,13 @@ class BaseTest(TypedDict): """Shared base for the Rust module.""" - args: T.List[T.Union[str, File, TargetHolder]] + args: T.List[T.Union[str, File, build.Target]] should_fail: bool timeout: int workdir: T.Optional[str] - depends: T.List[T.Union[CustomTargetHolder, BuildTargetHolder]] + depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]] priority: int - env: T.Union[EnvironmentVariablesHolder, T.List[str], T.Dict[str, str], str] + env: T.Union[EnvironmentVariablesObject, T.List[str], T.Dict[str, str], str] suite: T.List[str] @@ -70,7 +69,7 @@ class ExtractRequired(TypedDict): a boolean or a feature option should inherit it's arguments from this class. """ - required: T.Union[bool, 'FeatureOptionHolder'] + required: T.Union[bool, coredata.UserFeatureOption] class FuncGenerator(TypedDict): @@ -81,7 +80,7 @@ class FuncGenerator(TypedDict): output: T.List[str] depfile: bool capture: bool - depends: T.List[T.Union['BuildTargetHolder', 'CustomTargetHolder']] + depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]] class GeneratorProcess(TypedDict): @@ -90,3 +89,16 @@ class GeneratorProcess(TypedDict): preserve_path_from: T.Optional[str] extra_args: T.List[str] + +class DependencyMethodPartialDependency(TypedDict): + + """ Keyword Arguments for the dep.partial_dependency methods """ + + compile_args: bool + link_args: bool + links: bool + includes: bool + sources: bool + +class BuildTargeMethodExtractAllObjects(TypedDict): + recursive: bool diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py index dbd0ade..c3cc0d2 100644 --- a/mesonbuild/interpreter/mesonmain.py +++ b/mesonbuild/interpreter/mesonmain.py @@ -5,16 +5,15 @@ from .. import dependencies from .. import build from .. import mlog -from ..mesonlib import unholder, MachineChoice, OptionKey +from ..mesonlib import MachineChoice, OptionKey from ..programs import OverrideProgram, ExternalProgram from ..interpreterbase import (MesonInterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated, typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs, MesonVersionString, InterpreterException) -from .compiler import CompilerHolder from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder, CustomTargetHolder, CustomTargetIndexHolder, - EnvironmentVariablesHolder) + EnvironmentVariablesObject) import typing as T @@ -57,11 +56,11 @@ class MesonMain(MesonInterpreterObject): 'add_devenv': self.add_devenv_method, }) - def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args): + def _find_source_script(self, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], args): - if isinstance(prog, (ExecutableHolder, ExternalProgramHolder)): - return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args) - found = self.interpreter.func_find_program({}, prog, {}).held_object + if isinstance(prog, (build.Executable, ExternalProgram)): + return self.interpreter.backend.get_executable_serialisation([prog] + args) + found = self.interpreter.func_find_program({}, prog, {}) es = self.interpreter.backend.get_executable_serialisation([found] + args) es.subproject = self.interpreter.subproject return es @@ -254,7 +253,7 @@ class MesonMain(MesonInterpreterObject): for_machine = self.interpreter.machine_from_native_kwarg(kwargs) clist = self.interpreter.coredata.compilers[for_machine] if cname in clist: - return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject) + return clist[cname] raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.') @noPosargs @@ -375,9 +374,9 @@ class MesonMain(MesonInterpreterObject): @FeatureNew('add_devenv', '0.58.0') @noKwargs - @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder)) - def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None: + @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesObject)) + def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesObject], kwargs: T.Dict[str, T.Any]) -> None: env = args[0] if isinstance(env, (str, list, dict)): - env = EnvironmentVariablesHolder(env) - self.build.devenv.append(env.held_object) + env = EnvironmentVariablesObject(env) + self.build.devenv.append(env.vars) diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index f3bcfab..46fd27b 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -50,6 +50,7 @@ class PythonDependency(SystemDependency): self.variables = python_holder.variables self.paths = python_holder.paths self.link_libpython = python_holder.link_libpython + self.info: T.Optional[T.Dict[str, str]] = None if mesonlib.version_compare(self.version, '>= 3.0'): self.major_version = 3 else: @@ -278,12 +279,20 @@ print (json.dumps ({ })) ''' +class PythonExternalProgram(ExternalProgram): + def __init__(self, name: str, command: T.Optional[T.List[str]] = None, ext_prog: T.Optional[ExternalProgram] = None): + if ext_prog is None: + super().__init__(name, command=command, silent=True) + else: + self.name = ext_prog.name + self.command = ext_prog.command + self.path = ext_prog.path + self.info: T.Dict[str, str] = {} class PythonInstallation(ExternalProgramHolder): - def __init__(self, interpreter, python, info): - ExternalProgramHolder.__init__(self, python, interpreter.subproject) - self.interpreter = interpreter - self.subproject = self.interpreter.subproject + def __init__(self, python, interpreter): + ExternalProgramHolder.__init__(self, python, interpreter) + info = python.info prefix = self.interpreter.environment.coredata.get_option(mesonlib.OptionKey('prefix')) self.variables = info['variables'] self.paths = info['paths'] @@ -325,11 +334,10 @@ class PythonInstallation(ExternalProgramHolder): # behavior. See https://github.com/mesonbuild/meson/issues/4117 if not self.link_libpython: new_deps = [] - for holder in mesonlib.extract_as_list(kwargs, 'dependencies'): - dep = holder.held_object + for dep in mesonlib.extract_as_list(kwargs, 'dependencies'): if isinstance(dep, PythonDependency): - holder = self.interpreter.holderify(dep.get_partial_dependency(compile_args=True)) - new_deps.append(holder) + dep = dep.get_partial_dependency(compile_args=True) + new_deps.append(dep) kwargs['dependencies'] = new_deps suffix = self.variables.get('EXT_SUFFIX') or self.variables.get('SO') or self.variables.get('.so') @@ -360,7 +368,7 @@ class PythonInstallation(ExternalProgramHolder): dep = PythonDependency(self, self.interpreter.environment, kwargs) if required and not dep.found(): raise mesonlib.MesonException('Python dependency not found') - return self.interpreter.holderify(dep) + return dep @permittedKwargs(['pure', 'subdir']) def install_sources_method(self, args, kwargs): @@ -377,7 +385,7 @@ class PythonInstallation(ExternalProgramHolder): else: kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir) - return self.interpreter.holderify(self.interpreter.func_install_data(None, args, kwargs)) + return self.interpreter.func_install_data(None, args, kwargs) @noPosargs @permittedKwargs(['pure', 'subdir']) @@ -519,25 +527,26 @@ class PythonModule(ExtensionModule): if disabled: mlog.log('Program', name_or_path or 'python', 'found:', mlog.red('NO'), '(disabled by:', mlog.bold(feature), ')') - return ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) + return NonExistingExternalProgram() if not name_or_path: - python = ExternalProgram('python3', mesonlib.python_command, silent=True) + python = PythonExternalProgram('python3', mesonlib.python_command) else: - python = ExternalProgram.from_entry('python3', name_or_path) + tmp_python = ExternalProgram.from_entry('python3', name_or_path) + python = PythonExternalProgram('python3', ext_prog=tmp_python) if not python.found() and mesonlib.is_windows(): pythonpath = self._get_win_pythonpath(name_or_path) if pythonpath is not None: name_or_path = pythonpath - python = ExternalProgram(name_or_path, silent=True) + python = PythonExternalProgram(name_or_path) # Last ditch effort, python2 or python3 can be named python # on various platforms, let's not give up just yet, if an executable # named python is available and has a compatible version, let's use # it if not python.found() and name_or_path in ['python2', 'python3']: - python = ExternalProgram('python', silent=True) + python = PythonExternalProgram('python') if python.found() and want_modules: for mod in want_modules: @@ -566,11 +575,11 @@ class PythonModule(ExtensionModule): if not python.found(): if required: raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python')) - res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) + return NonExistingExternalProgram() elif missing_modules: if required: raise mesonlib.MesonException('{} is missing modules: {}'.format(name_or_path or 'python', ', '.join(missing_modules))) - res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) + return NonExistingExternalProgram() else: # Sanity check, we expect to have something that at least quacks in tune try: @@ -586,14 +595,17 @@ class PythonModule(ExtensionModule): mlog.debug(stderr) if isinstance(info, dict) and 'version' in info and self._check_version(name_or_path, info['version']): - res = PythonInstallation(self.interpreter, python, info) + python.info = info + return python else: - res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) if required: raise mesonlib.MesonException(f'{python} is not a valid python or it is missing setuptools') + return NonExistingExternalProgram() - return res + raise mesonlib.MesonBugException('Unreachable code was reached (PythonModule.find_installation).') def initialize(*args, **kwargs): - return PythonModule(*args, **kwargs) + mod = PythonModule(*args, **kwargs) + mod.interpreter.append_holder_map(PythonExternalProgram, PythonInstallation) + return mod diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 8c673de..ae45e4d 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from mesonbuild import coredata import os import shutil import typing as T @@ -20,21 +21,19 @@ import xml.etree.ElementTree as ET from . import ModuleReturnValue, ExtensionModule from .. import build -from .. import mesonlib from .. import mlog -from ..dependencies import find_external_dependency +from ..dependencies import find_external_dependency, Dependency, ExternalLibrary +from ..mesonlib import MesonException, File, FileOrString, version_compare, Popen_safe +from . import ModuleReturnValue, ExtensionModule from ..interpreter import extract_required_kwarg -from ..interpreter.interpreterobjects import DependencyHolder, ExternalLibraryHolder, IncludeDirsHolder, FeatureOptionHolder, GeneratedListHolder from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs -from ..mesonlib import MesonException, File -from ..programs import NonExistingExternalProgram +from ..programs import ExternalProgram, NonExistingExternalProgram if T.TYPE_CHECKING: from . import ModuleState from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency from ..interpreter import Interpreter from ..interpreter import kwargs - from ..programs import ExternalProgram QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency] @@ -45,7 +44,7 @@ if T.TYPE_CHECKING: """Keyword arguments for the Resource Compiler method.""" name: T.Optional[str] - sources: T.List[mesonlib.FileOrString] + sources: T.List[FileOrString] extra_args: T.List[str] method: str @@ -53,7 +52,7 @@ if T.TYPE_CHECKING: """Keyword arguments for the Ui Compiler method.""" - sources: T.List[mesonlib.FileOrString] + sources: T.List[FileOrString] extra_args: T.List[str] method: str @@ -61,25 +60,25 @@ if T.TYPE_CHECKING: """Keyword arguments for the Moc Compiler method.""" - sources: T.List[mesonlib.FileOrString] - headers: T.List[mesonlib.FileOrString] + sources: T.List[FileOrString] + headers: T.List[FileOrString] extra_args: T.List[str] method: str - include_directories: T.List[T.Union[str, IncludeDirsHolder]] - dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] + include_directories: T.List[T.Union[str, build.IncludeDirs]] + dependencies: T.List[T.Union[Dependency, ExternalLibrary]] class PreprocessKwArgs(TypedDict): - sources: T.List[mesonlib.FileOrString] - moc_sources: T.List[mesonlib.FileOrString] - moc_headers: T.List[mesonlib.FileOrString] - qresources: T.List[mesonlib.FileOrString] - ui_files: T.List[mesonlib.FileOrString] + sources: T.List[FileOrString] + moc_sources: T.List[FileOrString] + moc_headers: T.List[FileOrString] + qresources: T.List[FileOrString] + ui_files: T.List[FileOrString] moc_extra_arguments: T.List[str] rcc_extra_arguments: T.List[str] uic_extra_arguments: T.List[str] - include_directories: T.List[T.Union[str, IncludeDirsHolder]] - dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] + include_directories: T.List[T.Union[str, build.IncludeDirs]] + dependencies: T.List[T.Union[Dependency, ExternalLibrary]] method: str class HasToolKwArgs(kwargs.ExtractRequired): @@ -104,10 +103,10 @@ class QtBaseModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter', qt_version: int = 5): ExtensionModule.__init__(self, interpreter) self.qt_version = qt_version - self.moc: 'ExternalProgram' = NonExistingExternalProgram('moc') - self.uic: 'ExternalProgram' = NonExistingExternalProgram('uic') - self.rcc: 'ExternalProgram' = NonExistingExternalProgram('rcc') - self.lrelease: 'ExternalProgram' = NonExistingExternalProgram('lrelease') + self.moc: ExternalProgram = NonExistingExternalProgram('moc') + self.uic: ExternalProgram = NonExistingExternalProgram('uic') + self.rcc: ExternalProgram = NonExistingExternalProgram('rcc') + self.lrelease: ExternalProgram = NonExistingExternalProgram('lrelease') self.methods.update({ 'has_tools': self.has_tools, 'preprocess': self.preprocess, @@ -141,14 +140,14 @@ class QtBaseModule(ExtensionModule): if name == 'lrelease': arg = ['-version'] - elif mesonlib.version_compare(qt_dep.version, '>= 5'): + elif version_compare(qt_dep.version, '>= 5'): arg = ['--version'] else: arg = ['-v'] # Ensure that the version of qt and each tool are the same - def get_version(p: 'ExternalProgram') -> str: - _, out, err = mesonlib.Popen_safe(p.get_command() + arg) + def get_version(p: ExternalProgram) -> str: + _, out, err = Popen_safe(p.get_command() + arg) if b.startswith('lrelease') or not qt_dep.version.startswith('4'): care = out else: @@ -157,7 +156,7 @@ class QtBaseModule(ExtensionModule): p = state.find_program(b, required=False, version_func=get_version, - wanted=wanted).held_object + wanted=wanted) if p.found(): setattr(self, name, p) @@ -172,7 +171,7 @@ class QtBaseModule(ExtensionModule): if qt.found(): # Get all tools and then make sure that they are the right version self.compilers_detect(state, qt) - if mesonlib.version_compare(qt.version, '>=5.14.0'): + if version_compare(qt.version, '>=5.14.0'): self._rcc_supports_depfiles = True else: mlog.warning('rcc dependencies will not work properly until you move to Qt >= 5.14:', @@ -185,7 +184,7 @@ class QtBaseModule(ExtensionModule): self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix) @staticmethod - def _qrc_nodes(state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.Tuple[str, T.List[str]]: + def _qrc_nodes(state: 'ModuleState', rcc_file: 'FileOrString') -> T.Tuple[str, T.List[str]]: abspath: str if isinstance(rcc_file, str): abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file) @@ -210,7 +209,7 @@ class QtBaseModule(ExtensionModule): except Exception: raise MesonException(f'Unable to parse resource file {abspath}') - def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.List[File]: + def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'FileOrString') -> T.List[File]: rcc_dirname, nodes = self._qrc_nodes(state, rcc_file) result: T.List[File] = [] for resource_path in nodes: @@ -243,7 +242,7 @@ class QtBaseModule(ExtensionModule): @noPosargs @typed_kwargs( 'qt.has_tools', - KwargInfo('required', (bool, FeatureOptionHolder), default=False), + KwargInfo('required', (bool, coredata.UserFeatureOption), default=False), KwargInfo('method', str, default='auto'), ) def has_tools(self, state: 'ModuleState', args: T.Tuple, kwargs: 'HasToolKwArgs') -> bool: @@ -351,7 +350,7 @@ class QtBaseModule(ExtensionModule): kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], ['ui_@BASENAME@.h'], name=f'Qt{self.qt_version} ui') - out = GeneratedListHolder(gen.process_files(kwargs['sources'], state)) + out = gen.process_files(kwargs['sources'], state) return ModuleReturnValue(out, [out]) @FeatureNew('qt.compile_moc', '0.59.0') @@ -362,8 +361,8 @@ class QtBaseModule(ExtensionModule): KwargInfo('headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('method', str, default='auto'), - KwargInfo('include_directories', ContainerTypeInfo(list, (IncludeDirsHolder, str)), listify=True, default=[]), - KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]), + KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]), + KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), ) def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue: self._detect_tools(state, kwargs['method']) @@ -378,7 +377,7 @@ class QtBaseModule(ExtensionModule): inc = state.get_include_args(include_dirs=kwargs['include_directories']) compile_args: T.List[str] = [] for dep in kwargs['dependencies']: - compile_args.extend([a for a in dep.held_object.get_all_compile_args() if a.startswith(('-I', '-D'))]) + compile_args.extend([a for a in dep.get_all_compile_args() if a.startswith(('-I', '-D'))]) output: T.List[build.GeneratedList] = [] @@ -408,8 +407,8 @@ class QtBaseModule(ExtensionModule): KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), KwargInfo('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'), KwargInfo('method', str, default='auto'), - KwargInfo('include_directories', ContainerTypeInfo(list, (IncludeDirsHolder, str)), listify=True, default=[]), - KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]), + KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]), + KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), ) def preprocess(self, state: 'ModuleState', args: T.List[T.Union[str, File]], kwargs: 'PreprocessKwArgs') -> ModuleReturnValue: _sources = args[1:] diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py index eea3dbd..ba8b300 100644 --- a/mesonbuild/modules/sourceset.py +++ b/mesonbuild/modules/sourceset.py @@ -14,16 +14,13 @@ from collections import namedtuple from .. import mesonlib +from .. import build from ..mesonlib import listify, OrderedSet from . import ExtensionModule, ModuleObject, MutableModuleObject from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, InterpreterException, InvalidArguments, InvalidCode, FeatureNew, ) -from ..interpreter import ( - GeneratedListHolder, CustomTargetHolder, - CustomTargetIndexHolder -) SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps') SourceFiles = namedtuple('SourceFiles', 'sources dependencies') @@ -49,8 +46,8 @@ class SourceSet(MutableModuleObject): deps = [] for x in arg: if isinstance(x, (str, mesonlib.File, - GeneratedListHolder, CustomTargetHolder, - CustomTargetIndexHolder)): + build.GeneratedList, build.CustomTarget, + build.CustomTargetIndex)): sources.append(x) elif hasattr(x, 'found'): if not allow_deps: diff --git a/mesonbuild/modules/unstable_external_project.py b/mesonbuild/modules/unstable_external_project.py index f10c7aa..e997f6a 100644 --- a/mesonbuild/modules/unstable_external_project.py +++ b/mesonbuild/modules/unstable_external_project.py @@ -22,7 +22,6 @@ from ..mesonlib import (MesonException, Popen_safe, MachineChoice, get_variable_regex, do_replacement, extract_as_list) from ..interpreterbase import InterpreterException, FeatureNew from ..interpreterbase import permittedKwargs, typed_pos_args -from ..interpreter import DependencyHolder from ..compilers.compilers import CFLAGS_MAPPING, CEXE_MAPPING from ..dependencies import InternalDependency, PkgConfigDependency from ..mesonlib import OptionKey @@ -237,7 +236,7 @@ class ExternalProject(ModuleObject): variables = [] dep = InternalDependency(version, incdir, compile_args, link_args, libs, libs_whole, sources, final_deps, variables) - return DependencyHolder(dep, self.subproject) + return dep class ExternalProjectModule(ExtensionModule): diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py index e5af2d7..995370a 100644 --- a/mesonbuild/modules/unstable_rust.py +++ b/mesonbuild/modules/unstable_rust.py @@ -19,15 +19,11 @@ from . import ExtensionModule, ModuleReturnValue from .. import mlog from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget from ..interpreter.interpreter import TEST_KWARGS -from ..interpreter.interpreterobjects import ( - BuildTargetHolder, - CustomTargetHolder, - DependencyHolder, - ExecutableHolder, - ExternalLibraryHolder, -) from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, permittedKwargs, FeatureNew, typed_kwargs, typed_pos_args, noPosargs -from ..mesonlib import stringlistify, unholder, listify, typeslistify, File +from ..mesonlib import stringlistify, listify, typeslistify, File +from ..dependencies import Dependency, ExternalLibrary +from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew, typed_pos_args, noPosargs +from ..mesonlib import stringlistify, listify, typeslistify, File if T.TYPE_CHECKING: from . import ModuleState @@ -38,7 +34,7 @@ if T.TYPE_CHECKING: class FuncTest(_kwargs.BaseTest): - dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] + dependencies: T.List[T.Union[Dependency, ExternalLibrary]] is_parallel: bool @@ -55,18 +51,18 @@ class RustModule(ExtensionModule): 'bindgen': self.bindgen, }) - @typed_pos_args('rust.test', str, BuildTargetHolder) + @typed_pos_args('rust.test', str, BuildTarget) @typed_kwargs( 'rust.test', *TEST_KWARGS, KwargInfo('is_parallel', bool, default=False), KwargInfo( 'dependencies', - ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), + ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]), ) - def test(self, state: 'ModuleState', args: T.Tuple[str, BuildTargetHolder], kwargs: 'FuncTest') -> ModuleReturnValue: + def test(self, state: 'ModuleState', args: T.Tuple[str, BuildTarget], kwargs: 'FuncTest') -> ModuleReturnValue: """Generate a rust test target from a given rust target. Rust puts it's unitests inside it's main source files, unlike most @@ -151,11 +147,10 @@ class RustModule(ExtensionModule): new_target_kwargs ) - e = ExecutableHolder(new_target, self.interpreter) test = self.interpreter.make_test( - self.interpreter.current_node, (name, e), tkwargs) + self.interpreter.current_node, (name, new_target), tkwargs) - return ModuleReturnValue(None, [e, test]) + return ModuleReturnValue(None, [new_target, test]) @noPosargs @permittedKwargs({'input', 'output', 'include_directories', 'c_args', 'args'}) @@ -184,7 +179,7 @@ class RustModule(ExtensionModule): bind_args: T.List[str] = stringlistify(listify(kwargs.get('args', []))) # Split File and Target dependencies to add pass to CustomTarget - depends: T.List[T.Union[GeneratedList, BuildTarget, CustomTargetIndex]] = [] + depends: T.List[T.Union[GeneratedList, BuildTarget, CustomTargetIndex, CustomTarget]] = [] depend_files: T.List[File] = [] for d in _deps: if isinstance(d, File): @@ -225,7 +220,7 @@ class RustModule(ExtensionModule): backend=state.backend, ) - return ModuleReturnValue([target], [CustomTargetHolder(target, self.interpreter)]) + return ModuleReturnValue([target], [target]) def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule: diff --git a/run_mypy.py b/run_mypy.py index 982a3ae..e780adf 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -11,7 +11,7 @@ from mesonbuild.mesonlib import version_compare modules = [ # fully typed submodules - 'mesonbuild/ast', + # 'mesonbuild/ast', 'mesonbuild/cmake', 'mesonbuild/compilers', 'mesonbuild/dependencies', @@ -23,6 +23,7 @@ modules = [ 'mesonbuild/arglist.py', # 'mesonbuild/coredata.py', 'mesonbuild/envconfig.py', + 'mesonbuild/interpreter/interpreterobjects.py', 'mesonbuild/linkers.py', 'mesonbuild/mcompile.py', 'mesonbuild/mdevenv.py', diff --git a/test cases/common/178 bothlibraries/meson.build b/test cases/common/178 bothlibraries/meson.build index 0bfba76..d52158d 100644 --- a/test cases/common/178 bothlibraries/meson.build +++ b/test cases/common/178 bothlibraries/meson.build @@ -20,6 +20,9 @@ exe_static2 = executable('prog-static2', 'main.c', link_with : both_libs2.get_static_lib()) exe_both2 = executable('prog-both2', 'main.c', link_with : both_libs2) +# Ensure that calling the build target methods also works +assert(both_libs.name() == 'mylib') + test('runtest-shared-2', exe_shared2) test('runtest-static-2', exe_static2) test('runtest-both-2', exe_both2) diff --git a/test cases/native/9 override with exe/meson.build b/test cases/native/9 override with exe/meson.build index 62d2f32..5275532 100644 --- a/test cases/native/9 override with exe/meson.build +++ b/test cases/native/9 override with exe/meson.build @@ -1,4 +1,4 @@ -project('myexe', 'c') +project('myexe', 'c', version: '0.1') sub = subproject('sub') prog = find_program('foobar', version : '>= 2.0', required : false) |