holders: Fix the remaining code to respect the holder changes

This commit is contained in:
Daniel Mensinger 2021-06-17 00:27:39 +02:00
parent 34c28dc92c
commit 7c757dff71
15 changed files with 510 additions and 499 deletions

View File

@ -76,6 +76,9 @@ class MockRunTarget(MesonInterpreterObject):
ADD_SOURCE = 0 ADD_SOURCE = 0
REMOVE_SOURCE = 1 REMOVE_SOURCE = 1
_T = T.TypeVar('_T')
_V = T.TypeVar('_V')
class AstInterpreter(InterpreterBase): class AstInterpreter(InterpreterBase):
def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None): def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None):
super().__init__(source_root, subdir, subproject) super().__init__(source_root, subdir, subproject)
@ -141,6 +144,12 @@ class AstInterpreter(InterpreterBase):
'range': self.func_do_nothing, '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: def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> bool:
return True return True

View File

@ -40,7 +40,7 @@ from .compilers import (
is_known_suffix is_known_suffix
) )
from .linkers import StaticLinker from .linkers import StaticLinker
from .interpreterbase import FeatureNew, TYPE_nkwargs, TYPE_nvar from .interpreterbase import FeatureNew
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ._typing import ImmutableListProtocol, ImmutableSetProtocol from ._typing import ImmutableListProtocol, ImmutableSetProtocol

View File

@ -1,7 +1,6 @@
import functools import functools
from .interpreterobjects import (IncludeDirsHolder, ExternalLibraryHolder, from .interpreterobjects import (extract_required_kwarg, extract_search_dirs)
extract_required_kwarg, extract_search_dirs)
from .. import mesonlib from .. import mesonlib
from .. import mlog from .. import mlog
@ -14,12 +13,12 @@ import typing as T
import os import os
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..environment import Environment from ..interpreter import Interpreter
from ..compilers import Compiler, RunResult from ..compilers import Compiler, RunResult
class TryRunResultHolder(ObjectHolder['RunResult']): class TryRunResultHolder(ObjectHolder['RunResult']):
def __init__(self, res: 'RunResult'): def __init__(self, res: 'RunResult', interpreter: 'Interpreter'):
super().__init__(res) super().__init__(res, interpreter)
self.methods.update({'returncode': self.returncode_method, self.methods.update({'returncode': self.returncode_method,
'compiled': self.compiled_method, 'compiled': self.compiled_method,
'stdout': self.stdout_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} find_library_permitted_kwargs |= {'header_' + k for k in header_permitted_kwargs}
class CompilerHolder(ObjectHolder['Compiler']): class CompilerHolder(ObjectHolder['Compiler']):
def __init__(self, compiler: 'Compiler', env: 'Environment', subproject: str): def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'):
super().__init__(compiler, subproject=subproject) super().__init__(compiler, interpreter)
self.environment = env self.environment = self.env
self.methods.update({'compiles': self.compiles_method, self.methods.update({'compiles': self.compiles_method,
'links': self.links_method, 'links': self.links_method,
'get_id': self.get_id_method, 'get_id': self.get_id_method,
@ -144,9 +143,10 @@ class CompilerHolder(ObjectHolder['Compiler']):
args = [] args = []
incdirs = mesonlib.extract_as_list(kwargs, 'include_directories') incdirs = mesonlib.extract_as_list(kwargs, 'include_directories')
for i in incdirs: 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.') 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) args += self.compiler.get_include_args(idir, False)
if not nobuiltins: if not nobuiltins:
opts = self.environment.coredata.options opts = self.environment.coredata.options
@ -162,7 +162,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
final_deps = [] final_deps = []
while deps: while deps:
next_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(): if not isinstance(d, dependencies.Dependency) or d.is_built():
raise InterpreterException('Dependencies must be external dependencies') raise InterpreterException('Dependencies must be external dependencies')
final_deps.append(d) final_deps.append(d)
@ -223,7 +223,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
else: else:
h = mlog.red('NO (%d)' % result.returncode) h = mlog.red('NO (%d)' % result.returncode)
mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h) mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h)
return TryRunResultHolder(result) return result
@noPosargs @noPosargs
@permittedKwargs({}) @permittedKwargs({})
@ -614,7 +614,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
self.environment, self.environment,
self.compiler.language, self.compiler.language,
silent=True) silent=True)
return ExternalLibraryHolder(lib, self.subproject) return lib
@FeatureNewKwargs('compiler.find_library', '0.51.0', ['static']) @FeatureNewKwargs('compiler.find_library', '0.51.0', ['static'])
@FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers']) @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
@ -659,7 +659,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
libtype, libname)) libtype, libname))
lib = dependencies.ExternalLibrary(libname, linkargs, self.environment, lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
self.compiler.language) self.compiler.language)
return ExternalLibraryHolder(lib, self.subproject) return lib
@permittedKwargs({}) @permittedKwargs({})
def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool: def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool:

View File

@ -66,11 +66,11 @@ if T.TYPE_CHECKING:
from . import kwargs from . import kwargs
# Input source types passed to Targets # Input source types passed to Targets
SourceInputs = T.Union[FileHolder, GeneratedListHolder, TargetHolder, SourceInputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget,
CustomTargetIndexHolder, GeneratedObjectsHolder, str] build.CustomTargetIndex, build.CustomTarget, build.GeneratedList, str]
# Input source types passed to the build.Target5 classes # Input source types passed to the build.Target5 classes
SourceOutputs = T.Union[mesonlib.File, build.GeneratedList, SourceOutputs = T.Union[mesonlib.File, build.GeneratedList,
build.BuildTarget, build.CustomTargetIndex, build.BuildTarget, build.CustomTargetIndex, build.CustomTarget,
build.GeneratedList] build.GeneratedList]
@ -191,7 +191,7 @@ known_build_target_kwargs = (
) )
TEST_KWARGS: T.List[KwargInfo] = [ 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=[]), listify=True, default=[]),
KwargInfo('should_fail', bool, default=False), KwargInfo('should_fail', bool, default=False),
KwargInfo('timeout', int, default=30), KwargInfo('timeout', int, default=30),
@ -201,11 +201,11 @@ TEST_KWARGS: T.List[KwargInfo] = [
default='exitcode', default='exitcode',
validator=lambda x: 'value must be one of "exitcode", "tap", "gtest", "rust"' if x not in {'exitcode', 'tap', 'gtest', 'rust'} else None, 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'}), 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'), listify=True, default=[], since='0.46.0'),
KwargInfo('priority', int, default=0, since='0.52.0'), KwargInfo('priority', int, default=0, since='0.52.0'),
# TODO: env needs reworks of the way the environment variable holder itself works probably # 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 KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=['']), # yes, a list of empty string
] ]
@ -235,7 +235,7 @@ class Interpreter(InterpreterBase, HoldableObject):
def __init__( def __init__(
self, self,
build: build.Build, _build: build.Build,
backend: T.Optional[Backend] = None, backend: T.Optional[Backend] = None,
subproject: str = '', subproject: str = '',
subdir: str = '', subdir: str = '',
@ -246,10 +246,10 @@ class Interpreter(InterpreterBase, HoldableObject):
ast: T.Optional[mparser.CodeBlockNode] = None, ast: T.Optional[mparser.CodeBlockNode] = None,
is_translated: bool = False, is_translated: bool = False,
) -> None: ) -> 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.an_unpicklable_object = mesonlib.an_unpicklable_object
self.build = build self.build = _build
self.environment = build.environment self.environment = self.build.environment
self.coredata = self.environment.get_coredata() self.coredata = self.environment.get_coredata()
self.backend = backend self.backend = backend
self.summary = {} self.summary = {}
@ -268,12 +268,12 @@ class Interpreter(InterpreterBase, HoldableObject):
elif ast is not None: elif ast is not None:
self.ast = ast self.ast = ast
self.sanity_check_ast() self.sanity_check_ast()
self.builtin.update({'meson': MesonMain(build, self)}) self.builtin.update({'meson': MesonMain(self.build, self)})
self.generators: T.List['GeneratorHolder'] = [] self.generators: T.List[build.Generator] = []
self.processed_buildfiles = set() # type: T.Set[str] self.processed_buildfiles = set() # type: T.Set[str]
self.project_args_frozen = False self.project_args_frozen = False
self.global_args_frozen = False # implies self.project_args_frozen self.global_args_frozen = False # implies self.project_args_frozen
self.subprojects = {} self.subprojects: T.Dict[str, SubprojectHolder] = {}
self.subproject_stack = [] self.subproject_stack = []
self.configure_file_outputs = {} self.configure_file_outputs = {}
# Passed from the outside, only used in subprojects. # Passed from the outside, only used in subprojects.
@ -283,6 +283,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.default_project_options = {} self.default_project_options = {}
self.project_default_options = {} self.project_default_options = {}
self.build_func_dict() self.build_func_dict()
self.build_holder_map()
# build_def_files needs to be defined before parse_project is called # 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 assert self.build.environment.machines.target.cpu is not None
self.builtin['build_machine'] = \ self.builtin['build_machine'] = \
MachineHolder(self.build.environment.machines.build) OBJ.MachineHolder(self.build.environment.machines.build, self)
self.builtin['host_machine'] = \ self.builtin['host_machine'] = \
MachineHolder(self.build.environment.machines.host) OBJ.MachineHolder(self.build.environment.machines.host, self)
self.builtin['target_machine'] = \ 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? # 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]]: 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) mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
modname = 'unstable_' + plainname modname = 'unstable_' + plainname
self.import_module(modname) self.import_module(modname)
return ModuleObjectHolder(self.modules[modname], self) return self.modules[modname]
@stringArgs @stringArgs
@noKwargs @noKwargs
@ -628,7 +629,7 @@ external dependencies (including libraries) must go to "dependencies".''')
dep = dependencies.InternalDependency(version, incs, compile_args, dep = dependencies.InternalDependency(version, incs, compile_args,
link_args, libs, libs_whole, sources, final_deps, link_args, libs, libs_whole, sources, final_deps,
variables) variables)
return DependencyHolder(dep, self.subproject) return dep
@noKwargs @noKwargs
def func_assert(self, node, args, kwargs): 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): def func_run_command(self, node, args, kwargs):
return self.run_command_impl(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: if len(args) < 1:
raise InterpreterException('Not enough arguments') raise InterpreterException('Not enough arguments')
cmd, *cargs = args cmd, *cargs = args
@ -748,10 +753,16 @@ external dependencies (including libraries) must go to "dependencies".''')
if len(args) != 1: if len(args) != 1:
raise InterpreterException('Subproject takes exactly one argument') raise InterpreterException('Subproject takes exactly one argument')
subp_name = args[0] 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): 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) disabled_feature=disabled_feature, exception=exception)
self.subprojects[subp_name] = sub self.subprojects[subp_name] = sub
self.coredata.initialized_subprojects.add(subp_name) 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, subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir,
self.modules, default_options, ast=ast, is_translated=is_translated) self.modules, default_options, ast=ast, is_translated=is_translated)
subi.subprojects = self.subprojects 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] subi.subproject_stack = self.subproject_stack + [subp_name]
current_active = self.active_projectname current_active = self.active_projectname
@ -958,7 +971,8 @@ external dependencies (including libraries) must go to "dependencies".''')
'options of other subprojects.') 'options of other subprojects.')
opt = self.get_option_internal(optname) opt = self.get_option_internal(optname)
if isinstance(opt, coredata.UserFeatureOption): if isinstance(opt, coredata.UserFeatureOption):
return FeatureOptionHolder(self.environment, optname, opt) opt.name = optname
return opt
elif isinstance(opt, coredata.UserOption): elif isinstance(opt, coredata.UserOption):
return opt.value return opt.value
return opt return opt
@ -974,7 +988,7 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('configuration_data first argument must be a dictionary') raise InterpreterException('configuration_data first argument must be a dictionary')
else: else:
initial_values = {} initial_values = {}
return ConfigurationDataHolder(self.subproject, initial_values) return ConfigurationDataObject(self.subproject, initial_values)
def set_backend(self): def set_backend(self):
# The backend is already set when parsing subprojects # 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, extprog = ExternalProgram(exename, search_dir=search_dir,
extra_search_dirs=extra_search_dirs, extra_search_dirs=extra_search_dirs,
silent=True) silent=True)
progobj = ExternalProgramHolder(extprog, self.subproject) if extprog.found():
if progobj.found(): extra_info.append(f"({' '.join(extprog.get_command())})")
extra_info.append(f"({' '.join(progobj.get_command())})") return extprog
return progobj
def program_from_overrides(self, command_names, extra_info): def program_from_overrides(self, command_names, extra_info):
for name in command_names: for name in command_names:
@ -1330,7 +1343,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if name in self.build.find_overrides: if name in self.build.find_overrides:
exe = self.build.find_overrides[name] exe = self.build.find_overrides[name]
extra_info.append(mlog.blue('(overridden)')) extra_info.append(mlog.blue('(overridden)'))
return ExternalProgramHolder(exe, self.subproject, self.backend) return exe
return None return None
def store_name_lookups(self, command_names): 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 self.build.find_overrides[name] = exe
def notfound_program(self, args): 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 # TODO update modules to always pass `for_machine`. It is bad-form to assume
# the host machine. # the host machine.
@ -1372,22 +1385,28 @@ external dependencies (including libraries) must go to "dependencies".''')
if wanted: if wanted:
if version_func: if version_func:
version = version_func(progobj) 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) version = progobj.get_version(self)
is_found, not_found, found = mesonlib.version_compare_many(version, wanted) is_found, not_found, found = mesonlib.version_compare_many(version, wanted)
if not is_found: 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:', 'found', mlog.normal_cyan(version), 'but need:',
mlog.bold(', '.join([f"'{e}'" for e in not_found])), *extra_info) mlog.bold(', '.join([f"'{e}'" for e in not_found])), *extra_info)
if required: if required:
m = 'Invalid version of program, need {!r} {!r} found {!r}.' 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) return self.notfound_program(args)
extra_info.insert(0, mlog.normal_cyan(version)) extra_info.insert(0, mlog.normal_cyan(version))
# Only store successful lookups # Only store successful lookups
self.store_name_lookups(args) 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 return progobj
def program_lookup(self, args, for_machine, required, search_dirs, extra_info): 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) progobj = self.program_from_system(args, search_dirs, extra_info)
if progobj is None and args[0].endswith('python3'): if progobj is None and args[0].endswith('python3'):
prog = ExternalProgram('python3', mesonlib.python_command, silent=True) 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: if progobj is None and fallback and required:
progobj = self.find_program_fallback(fallback, args, required, extra_info) 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 # Ensure the correct include type
if 'include_type' in kwargs: if 'include_type' in kwargs:
wanted = kwargs['include_type'] 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: if wanted != actual:
mlog.debug(f'Current include type of {names[0]} is {actual}. Converting to requested {wanted}') 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 return d
@FeatureNew('disabler', '0.44.0') @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.") @FeatureDeprecatedKwargs('executable', '0.56.0', ['gui_app'], extra_message="Use 'win_subsystem' instead.")
@permittedKwargs(build.known_exe_kwargs) @permittedKwargs(build.known_exe_kwargs)
def func_executable(self, node, args, 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) @permittedKwargs(build.known_stlib_kwargs)
def func_static_lib(self, node, args, 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) @permittedKwargs(build.known_shlib_kwargs)
def func_shared_lib(self, node, args, kwargs): def func_shared_lib(self, node, args, kwargs):
holder = self.build_target(node, args, kwargs, SharedLibraryHolder) holder = self.build_target(node, args, kwargs, build.SharedLibrary)
holder.held_object.shared_library_only = True holder.shared_library_only = True
return holder return holder
@permittedKwargs(known_library_kwargs) @permittedKwargs(known_library_kwargs)
@ -1521,7 +1542,7 @@ external dependencies (including libraries) must go to "dependencies".''')
@FeatureNew('shared_module', '0.37.0') @FeatureNew('shared_module', '0.37.0')
@permittedKwargs(build.known_shmod_kwargs) @permittedKwargs(build.known_shmod_kwargs)
def func_shared_module(self, node, args, 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) @permittedKwargs(known_library_kwargs)
def func_library(self, node, args, 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) @permittedKwargs(build.known_jar_kwargs)
def func_jar(self, node, args, 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']) @FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options'])
@permittedKwargs(known_build_target_kwargs) @permittedKwargs(known_build_target_kwargs)
@ -1538,21 +1559,21 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('Missing target_type keyword argument') raise InterpreterException('Missing target_type keyword argument')
target_type = kwargs.pop('target_type') target_type = kwargs.pop('target_type')
if target_type == 'executable': 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': 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': elif target_type == 'shared_module':
FeatureNew('build_target(target_type: \'shared_module\')', FeatureNew('build_target(target_type: \'shared_module\')',
'0.51.0').use(self.subproject) '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': 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': elif target_type == 'both_libraries':
return self.build_both_libraries(node, args, kwargs) return self.build_both_libraries(node, args, kwargs)
elif target_type == 'library': elif target_type == 'library':
return self.build_library(node, args, kwargs) return self.build_library(node, args, kwargs)
elif target_type == 'jar': elif target_type == 'jar':
return self.build_target(node, args, kwargs, JarHolder) return self.build_target(node, args, kwargs, build.Jar)
else: else:
raise InterpreterException('Unknown target_type.') 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 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']:
if isinstance(kwargs['command'][0], str): if isinstance(kwargs['command'][0], str):
kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {}) 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) tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend)
self.add_target(name, tg.held_object) self.add_target(name, tg)
return tg return tg
@FeatureNewKwargs('run_target', '0.57.0', ['env']) @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) self.generators.append(gen)
return 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) @typed_kwargs('benchmark', *TEST_KWARGS)
def func_benchmark(self, node: mparser.BaseNode, 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: kwargs: 'kwargs.FuncBenchmark') -> None:
self.add_test(node, args, kwargs, False) 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)) @typed_kwargs('benchmark', *TEST_KWARGS, KwargInfo('is_parallel', bool, default=True))
def func_test(self, node: mparser.BaseNode, 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: kwargs: 'kwargs.FuncTest') -> None:
self.add_test(node, args, kwargs, True) 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: def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesObject, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables:
envlist = kwargs.get('env', EnvironmentVariablesHolder()) envlist = kwargs.get('env', EnvironmentVariablesObject())
if isinstance(envlist, EnvironmentVariablesHolder): if isinstance(envlist, EnvironmentVariablesObject):
env = envlist.held_object env = envlist.vars
elif isinstance(envlist, dict): elif isinstance(envlist, dict):
FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject) FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject)
env = EnvironmentVariablesHolder(envlist) env = EnvironmentVariablesObject(envlist)
env = env.held_object env = env.vars
else: else:
# Convert from array to environment object # Convert from array to environment object
env = EnvironmentVariablesHolder(envlist) env = EnvironmentVariablesObject(envlist)
env = env.held_object env = env.vars
return env return env
def make_test(self, node: mparser.BaseNode, 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: kwargs: 'kwargs.BaseTest') -> Test:
name = args[0] name = args[0]
if ':' in name: 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) h = build.Headers(source_files, install_subdir, install_dir, install_mode, self.subproject)
self.build.headers.append(h) self.build.headers.append(h)
return HeadersHolder(h) return h
@FeatureNewKwargs('install_man', '0.47.0', ['install_mode']) @FeatureNewKwargs('install_man', '0.47.0', ['install_mode'])
@FeatureNewKwargs('install_man', '0.58.0', ['locale']) @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) m = build.Man(sources, custom_install_dir, custom_install_mode, self.subproject, locale)
self.build.man.append(m) self.build.man.append(m)
return ManHolder(m) return m
@FeatureNewKwargs('subdir', '0.44.0', ['if_found']) @FeatureNewKwargs('subdir', '0.44.0', ['if_found'])
@permittedKwargs({'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. ' '"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)}.') f'Rename has {len(rename)} elements and sources has {len(sources)}.')
data = DataHolder(build.Data(sources, install_dir, install_mode, self.subproject, rename)) data = build.Data(sources, install_dir, install_mode, self.subproject, rename)
self.build.data.append(data.held_object) self.build.data.append(data)
return data return data
@FeatureNewKwargs('install_subdir', '0.42.0', ['exclude_files', 'exclude_directories']) @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) install_mode = self._get_kwarg_install_mode(kwargs)
idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject) idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject)
self.build.install_dirs.append(idir) 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.47.0', ['copy', 'output_format', 'install_mode', 'encoding'])
@FeatureNewKwargs('configure_file', '0.46.0', ['format']) @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'] conf = kwargs['configuration']
if isinstance(conf, dict): if isinstance(conf, dict):
FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject) FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject)
conf = ConfigurationDataHolder(self.subproject, conf) conf = ConfigurationDataObject(self.subproject, conf)
elif not isinstance(conf, ConfigurationDataHolder): elif not isinstance(conf, ConfigurationDataObject):
raise InterpreterException('Argument "configuration" is not of type configuration_data') raise InterpreterException('Argument "configuration" is not of type configuration_data')
mlog.log('Configuring', mlog.bold(output), 'using configuration') mlog.log('Configuring', mlog.bold(output), 'using configuration')
if len(inputs) > 1: 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) return mesonlib.File.from_built_file(self.subdir, output)
def extract_incdirs(self, kwargs): def extract_incdirs(self, kwargs):
prospectives = unholder(extract_as_list(kwargs, 'include_directories')) prospectives = extract_as_list(kwargs, 'include_directories')
result = [] result = []
for p in prospectives: for p in prospectives:
if isinstance(p, build.IncludeDirs): if isinstance(p, build.IncludeDirs):
result.append(p) result.append(p)
elif isinstance(p, str): elif isinstance(p, str):
result.append(self.build_incdir_object([p]).held_object) result.append(self.build_incdir_object([p]))
else: else:
raise InterpreterException('Include directory objects can only be created from strings or include directories.') raise InterpreterException('Include directory objects can only be created from strings or include directories.')
return result 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): def func_include_directories(self, node, args, kwargs):
return self.build_incdir_object(args, kwargs.get('is_system', False)) 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): if not isinstance(is_system, bool):
raise InvalidArguments('Is_system must be boolean.') raise InvalidArguments('Is_system must be boolean.')
src_root = self.environment.get_source_dir() 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) absdir_build = os.path.join(absbase_build, a)
if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build): if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build):
raise InvalidArguments('Include dir %s does not exist.' % a) 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 return i
@permittedKwargs({'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default', @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') raise InterpreterException('environment first argument must be a dictionary or a list')
else: else:
initial_values = {} initial_values = {}
return EnvironmentVariablesHolder(initial_values, self.subproject) return EnvironmentVariablesObject(initial_values, self.subproject)
@stringArgs @stringArgs
@noKwargs @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)) results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s))
elif isinstance(s, mesonlib.File): elif isinstance(s, mesonlib.File):
results.append(s) results.append(s)
elif isinstance(s, (GeneratedListHolder, TargetHolder, elif isinstance(s, (build.GeneratedList, build.BuildTarget,
CustomTargetIndexHolder, build.CustomTargetIndex, build.CustomTarget,
GeneratedObjectsHolder)): build.GeneratedList)):
results.append(unholder(s)) results.append(s)
else: else:
raise InterpreterException(f'Source item is {s!r} instead of ' raise InterpreterException(f'Source item is {s!r} instead of '
'string or File-type object') 'string or File-type object')
@ -2554,7 +2575,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
else: else:
raise InterpreterException('Unknown default_library value: %s.', default_library) 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.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories'])
@FeatureNewKwargs('build target', '0.41.0', ['rust_args']) @FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
@FeatureNewKwargs('build target', '0.40.0', ['build_by_default']) @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') ef = extract_as_list(kwargs, 'extra_files')
kwargs['extra_files'] = self.source_strings_to_files(ef) kwargs['extra_files'] = self.source_strings_to_files(ef)
self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
if targetholder == ExecutableHolder: if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}:
targetclass = build.Executable mlog.debug('Unknown target type:', str(targetclass))
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))
raise RuntimeError('Unreachable code') raise RuntimeError('Unreachable code')
self.kwarg_strings_to_includedirs(kwargs) self.kwarg_strings_to_includedirs(kwargs)

View File

@ -1,8 +1,8 @@
import os import os
import shlex import shlex
import subprocess import subprocess
import re
import copy import copy
import textwrap
from pathlib import Path, PurePath from pathlib import Path, PurePath
@ -33,22 +33,22 @@ if T.TYPE_CHECKING:
from ..envconfig import MachineInfo from ..envconfig import MachineInfo
def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str, def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
feature_check: T.Optional['FeatureCheckBase'] = None, subproject: str,
feature_check: T.Optional[FeatureCheckBase] = None,
default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]: default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]:
val = kwargs.get('required', default) val = kwargs.get('required', default)
disabled = False disabled = False
required = False required = False
feature: T.Optional[str] = None feature: T.Optional[str] = None
if isinstance(val, FeatureOptionHolder): if isinstance(val, coredata.UserFeatureOption):
if not feature_check: if not feature_check:
feature_check = FeatureNew('User option "feature"', '0.47.0') feature_check = FeatureNew('User option "feature"', '0.47.0')
feature_check.use(subproject) feature_check.use(subproject)
option = val.held_object
feature = val.name feature = val.name
if option.is_disabled(): if val.is_disabled():
disabled = True disabled = True
elif option.is_enabled(): elif val.is_enabled():
required = True required = True
elif isinstance(val, bool): elif isinstance(val, bool):
required = val required = val
@ -62,9 +62,9 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str,
return disabled, required, feature return disabled, required, feature
def extract_search_dirs(kwargs): def extract_search_dirs(kwargs: T.Dict[str, T.Any]) -> T.List[str]:
search_dirs = mesonlib.stringlistify(kwargs.get('dirs', [])) search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', []))
search_dirs = [Path(d).expanduser() for d in search_dirs] search_dirs = [Path(d).expanduser() for d in search_dirs_str]
for d in search_dirs: for d in search_dirs:
if mesonlib.is_windows() and d.root.startswith('\\'): if mesonlib.is_windows() and d.root.startswith('\\'):
# a Unix-path starting with `/` that is not absolute on Windows. # 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)) return list(map(str, search_dirs))
class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]): class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption): def __init__(self, option: coredata.UserFeatureOption, interpreter: 'Interpreter'):
super().__init__(option) super().__init__(option, interpreter)
if option and option.is_auto(): if option and option.is_auto():
# TODO: we need to case here because options is not a TypedDict # 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.held_object = T.cast(coredata.UserFeatureOption, self.env.coredata.options[OptionKey('auto_features')])
self.name = name self.held_object.name = option.name
self.methods.update({'enabled': self.enabled_method, self.methods.update({'enabled': self.enabled_method,
'disabled': self.disabled_method, 'disabled': self.disabled_method,
'allowed': self.allowed_method, 'allowed': self.allowed_method,
@ -90,34 +90,36 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
}) })
@property @property
def value(self): def value(self) -> str:
return 'disabled' if not self.held_object else self.held_object.value return 'disabled' if not self.held_object else self.held_object.value
def as_disabled(self): def as_disabled(self) -> coredata.UserFeatureOption:
return FeatureOptionHolder(None, self.name, None) disabled = copy.deepcopy(self.held_object)
disabled.value = 'disabled'
return disabled
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def enabled_method(self, args, kwargs): def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'enabled' return self.value == 'enabled'
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def disabled_method(self, args, kwargs): def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'disabled' return self.value == 'disabled'
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def allowed_method(self, args, kwargs): def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return not self.value == 'disabled' return not self.value == 'disabled'
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def auto_method(self, args, kwargs): def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'auto' return self.value == 'auto'
@permittedKwargs({'error_message'}) @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: if len(args) != 1:
raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), )) raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), ))
if not isinstance(args[0], bool): if not isinstance(args[0], bool):
@ -126,38 +128,57 @@ class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
if error_message and not isinstance(error_message, str): if error_message and not isinstance(error_message, str):
raise InterpreterException("Error message must be a string.") raise InterpreterException("Error message must be a string.")
if args[0]: if args[0]:
return self return copy.deepcopy(self.held_object)
assert isinstance(error_message, str)
if self.value == 'enabled': 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 '' prefix = prefix + ': ' if error_message else ''
raise InterpreterException(prefix + error_message) raise InterpreterException(prefix + error_message)
return self.as_disabled() return self.as_disabled()
@permittedKwargs({}) @noKwargs
def disable_auto_if_method(self, args, kwargs): def disable_auto_if_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
if len(args) != 1: if len(args) != 1:
raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), )) raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), ))
if not isinstance(args[0], bool): if not isinstance(args[0], bool):
raise InvalidArguments('boolean argument expected.') 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): 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__() super().__init__()
if not isinstance(cmd, ExternalProgram): if not isinstance(cmd, ExternalProgram):
raise AssertionError('BUG: RunProcess must be passed an ExternalProgram') raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
self.capture = capture 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, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
self.returncode = pc.returncode
self.methods.update({'returncode': self.returncode_method, self.methods.update({'returncode': self.returncode_method,
'stdout': self.stdout_method, 'stdout': self.stdout_method,
'stderr': self.stderr_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 command_array = cmd.get_command() + args
menv = {'MESON_SOURCE_ROOT': source_dir, menv = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir, 'MESON_BUILD_ROOT': build_dir,
@ -188,28 +209,33 @@ class RunProcess(MesonInterpreterObject):
if check and p.returncode != 0: if check and p.returncode != 0:
raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode)) raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
return p, o, e return p.returncode, o, e
except FileNotFoundError: except FileNotFoundError:
raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array)) raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def returncode_method(self, args, kwargs): def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
return self.returncode return self.returncode
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def stdout_method(self, args, kwargs): def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stdout return self.stdout
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def stderr_method(self, args, kwargs): def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stderr return self.stderr
class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]): # TODO: Parsing the initial values should be either done directly in the
def __init__(self, initial_values=None, subproject: str = ''): # `Interpreter` or in `build.EnvironmentVariables`. This way, this class
super().__init__(build.EnvironmentVariables(), subproject=subproject) # 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, self.methods.update({'set': self.set_method,
'append': self.append_method, 'append': self.append_method,
'prepend': self.prepend_method, 'prepend': self.prepend_method,
@ -232,18 +258,18 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
def __repr__(self) -> str: def __repr__(self) -> str:
repr_str = "<{0}: {1}>" 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: def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str:
separator = kwargs.get('separator', os.pathsep) separator = kwargs.get('separator', os.pathsep)
if not isinstance(separator, str): if not isinstance(separator, str):
raise InterpreterException("EnvironmentVariablesHolder methods 'separator'" raise InterpreterException("EnvironmentVariablesObject methods 'separator'"
" argument needs to be a string.") " argument needs to be a string.")
return separator return separator
def warn_if_has_name(self, name: str) -> None: def warn_if_has_name(self, name: str) -> None:
# Multiple append/prepend operations was not supported until 0.58.0. # 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' m = f'Overriding previous value of environment variable {name!r} with a new one'
FeatureNew('0.58.0', m).use(self.subproject) 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: def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.held_object.set(name, values, separator) self.vars.set(name, values, separator)
@stringArgs @stringArgs
@permittedKwargs({'separator'}) @permittedKwargs({'separator'})
@ -262,7 +288,7 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name) self.warn_if_has_name(name)
self.held_object.append(name, values, separator) self.vars.append(name, values, separator)
@stringArgs @stringArgs
@permittedKwargs({'separator'}) @permittedKwargs({'separator'})
@ -271,13 +297,14 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
name, values = args name, values = args
separator = self.unpack_separator(kwargs) separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name) 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]): class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):
def __init__(self, subproject: str, initial_values=None): 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. 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, self.methods.update({'set': self.set_method,
'set10': self.set10_method, 'set10': self.set10_method,
'set_quoted': self.set_quoted_method, 'set_quoted': self.set_quoted_method,
@ -291,15 +318,15 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.Confi
for k, v in initial_values.items(): for k, v in initial_values.items():
self.set_method([k, v], {}) self.set_method([k, v], {})
elif initial_values: 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 return self.used
def mark_used(self): def mark_used(self) -> None:
self.used = True 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: if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
mlog.deprecation('Passing a list as the single argument to ' mlog.deprecation('Passing a list as the single argument to '
'configuration_data.set is deprecated. This will ' '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): if desc is not None and not isinstance(desc, str):
raise InterpreterException('Description must be a string.') 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 @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) (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) (name, val, desc) = self.validate_args(args, kwargs)
if not isinstance(val, str): if not isinstance(val, str):
raise InterpreterException("Second argument to set_quoted must be a string.") raise InterpreterException("Second argument to set_quoted must be a string.")
escaped_val = '\\"'.join(val.split('"')) 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) (name, val, desc) = self.validate_args(args, kwargs)
if val: if val:
self.held_object.values[name] = (1, desc) self.conf_data.values[name] = (1, desc)
else: else:
self.held_object.values[name] = (0, desc) self.conf_data.values[name] = (0, desc)
def has_method(self, args, kwargs): def has_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return args[0] in self.held_object.values return args[0] in self.conf_data.values
@FeatureNew('configuration_data.get()', '0.38.0') @FeatureNew('configuration_data.get()', '0.38.0')
@noArgsFlattening @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: if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get method takes one or two arguments.') 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] name = args[0]
if name in self.held_object: if name in self.conf_data:
return self.held_object.get(name)[0] return self.conf_data.get(name)[0]
if len(args) > 1: 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) raise InterpreterException('Entry %s not in configuration data.' % name)
@FeatureNew('configuration_data.get_unquoted()', '0.44.0') @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: if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get method takes one or two arguments.') 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] name = args[0]
if name in self.held_object: if name in self.conf_data:
val = self.held_object.get(name)[0] val = self.conf_data.get(name)[0]
elif len(args) > 1: elif len(args) > 1:
assert isinstance(args[1], (str, int, bool))
val = args[1] val = args[1]
else: else:
raise InterpreterException('Entry %s not in configuration data.' % name) 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[1:-1]
return val return val
def get(self, name): def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
return self.held_object.values[name] # (val, desc) return self.conf_data.values[name]
@FeatureNew('configuration_data.keys()', '0.57.0') @FeatureNew('configuration_data.keys()', '0.57.0')
@noPosargs @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()) return sorted(self.keys())
def keys(self): def keys(self) -> T.List[str]:
return self.held_object.values.keys() 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: if len(args) != 1:
raise InterpreterException('Merge_from takes one positional argument.') raise InterpreterException('Merge_from takes one positional argument.')
from_object = args[0] from_object_holder = args[0]
if not isinstance(from_object, ConfigurationDataHolder): if not isinstance(from_object_holder, ConfigurationDataObject):
raise InterpreterException('Merge_from argument must be a configuration data object.') 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(): 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]): class DependencyHolder(ObjectHolder[Dependency]):
def __init__(self, dep: Dependency, subproject: str): def __init__(self, dep: Dependency, interpreter: 'Interpreter'):
super().__init__(dep, subproject=subproject) super().__init__(dep, interpreter)
self.methods.update({'found': self.found_method, self.methods.update({'found': self.found_method,
'type_name': self.type_name_method, 'type_name': self.type_name_method,
'version': self.version_method, 'version': self.version_method,
@ -415,35 +458,35 @@ class DependencyHolder(ObjectHolder[Dependency]):
'as_link_whole': self.as_link_whole_method, 'as_link_whole': self.as_link_whole_method,
}) })
def found(self): def found(self) -> bool:
return self.found_method([], {}) return self.found_method([], {})
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def type_name_method(self, args, kwargs): def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name return self.held_object.type_name
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def found_method(self, args, kwargs): def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
if self.held_object.type_name == 'internal': if self.held_object.type_name == 'internal':
return True return True
return self.held_object.found() return self.held_object.found()
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def version_method(self, args, kwargs): def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_version() return self.held_object.get_version()
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def name_method(self, args, kwargs): def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_name() return self.held_object.get_name()
@FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0', @FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0',
'use Dependency.get_variable(pkgconfig : ...) instead') 'use Dependency.get_variable(pkgconfig : ...) instead')
@permittedKwargs({'define_variable', 'default'}) @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) args = listify(args)
if len(args) != 1: if len(args) != 1:
raise InterpreterException('get_pkgconfig_variable takes exactly one argument.') 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') @FeatureNew('dep.get_configtool_variable', '0.44.0')
@FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0', @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0',
'use Dependency.get_variable(configtool : ...) instead') 'use Dependency.get_variable(configtool : ...) instead')
@permittedKwargs({}) @noKwargs
def configtool_method(self, args, kwargs): def configtool_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
args = listify(args) args = listify(args)
if len(args) != 1: if len(args) != 1:
raise InterpreterException('get_configtool_variable takes exactly one argument.') raise InterpreterException('get_configtool_variable takes exactly one argument.')
@ -467,16 +510,16 @@ class DependencyHolder(ObjectHolder[Dependency]):
@FeatureNew('dep.partial_dependency', '0.46.0') @FeatureNew('dep.partial_dependency', '0.46.0')
@noPosargs @noPosargs
@permittedKwargs(permitted_partial_dependency_kwargs) @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
def partial_dependency_method(self, args, kwargs): def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs) pdep = self.held_object.get_partial_dependency(**kwargs)
return DependencyHolder(pdep, self.subproject) return pdep
@FeatureNew('dep.get_variable', '0.51.0') @FeatureNew('dep.get_variable', '0.51.0')
@typed_pos_args('dep.get_variable', optargs=[str]) @typed_pos_args('dep.get_variable', optargs=[str])
@permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'}) @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
@FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal']) @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] default_varname = args[0]
if default_varname is not None: if default_varname is not None:
FeatureNew('0.58.0', 'Positional argument to dep.get_variable()').use(self.subproject) 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') @FeatureNew('dep.include_type', '0.52.0')
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def include_type_method(self, args, kwargs): def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_include_type() return self.held_object.get_include_type()
@FeatureNew('dep.as_system', '0.52.0') @FeatureNew('dep.as_system', '0.52.0')
@permittedKwargs({}) @noKwargs
def as_system_method(self, args, kwargs): def as_system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
args = listify(args) args = listify(args)
new_is_system = 'system' new_is_system = 'system'
if len(args) > 1: if len(args) > 1:
raise InterpreterException('as_system takes only one optional value') raise InterpreterException('as_system takes only one optional value')
if len(args) == 1: 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_is_system = args[0]
new_dep = self.held_object.generate_system_dependency(new_is_system) 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') @FeatureNew('dep.as_link_whole', '0.56.0')
@permittedKwargs({}) @noKwargs
@noPosargs @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): if not isinstance(self.held_object, InternalDependency):
raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects') raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
new_dep = self.held_object.generate_link_whole_dependency() new_dep = self.held_object.generate_link_whole_dependency()
return DependencyHolder(new_dep, self.subproject) return new_dep
class ExternalProgramHolder(ObjectHolder[ExternalProgram]): class ExternalProgramHolder(ObjectHolder[ExternalProgram]):
def __init__(self, ep: ExternalProgram, subproject: str, backend=None): def __init__(self, ep: ExternalProgram, interpreter: 'Interpreter') -> None:
super().__init__(ep, subproject=subproject) super().__init__(ep, interpreter)
self.backend = backend
self.methods.update({'found': self.found_method, self.methods.update({'found': self.found_method,
'path': self.path_method, 'path': self.path_method,
'full_path': self.full_path_method}) 'full_path': self.full_path_method})
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def found_method(self, args, kwargs): def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found() return self.found()
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
@FeatureDeprecated('ExternalProgram.path', '0.55.0', @FeatureDeprecated('ExternalProgram.path', '0.55.0',
'use ExternalProgram.full_path() instead') '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() return self._full_path()
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
@FeatureNew('ExternalProgram.full_path', '0.55.0') @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() return self._full_path()
def _full_path(self): def _full_path(self) -> str:
exe = self.held_object exe = self.held_object
# TODO: How is this case even possible? Why can this hold a build.Executable?
if isinstance(exe, 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() return exe.get_path()
def found(self): def found(self) -> bool:
return isinstance(self.held_object, build.Executable) or self.held_object.found() 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]): class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
def __init__(self, el: ExternalLibrary, subproject: str): def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
super().__init__(el, subproject=subproject) super().__init__(el, interpreter)
self.methods.update({'found': self.found_method, self.methods.update({'found': self.found_method,
'type_name': self.type_name_method, 'type_name': self.type_name_method,
'partial_dependency': self.partial_dependency_method, 'partial_dependency': self.partial_dependency_method,
}) })
def found(self):
return self.held_object.found()
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def type_name_method(self, args, kwargs): def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name return self.held_object.type_name
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def found_method(self, args, kwargs): def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found() return self.held_object.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()
@FeatureNew('dep.partial_dependency', '0.46.0') @FeatureNew('dep.partial_dependency', '0.46.0')
@noPosargs @noPosargs
@permittedKwargs(permitted_partial_dependency_kwargs) @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
def partial_dependency_method(self, args, kwargs): def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs) pdep = self.held_object.get_partial_dependency(**kwargs)
return DependencyHolder(pdep, self.subproject) return pdep
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())
# A machine that's statically known from the cross file # A machine that's statically known from the cross file
class MachineHolder(ObjectHolder['MachineInfo']): class MachineHolder(ObjectHolder['MachineInfo']):
def __init__(self, machine_info: 'MachineInfo'): def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'):
super().__init__(machine_info) super().__init__(machine_info, interpreter)
self.methods.update({'system': self.system_method, self.methods.update({'system': self.system_method,
'cpu': self.cpu_method, 'cpu': self.cpu_method,
'cpu_family': self.cpu_family_method, 'cpu_family': self.cpu_family_method,
@ -616,88 +632,45 @@ class MachineHolder(ObjectHolder['MachineInfo']):
}) })
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu_family return self.held_object.cpu_family
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu return self.held_object.cpu
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.system return self.held_object.system
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str: def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.endian return self.held_object.endian
class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]): class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
def __init__(self, idobj: build.IncludeDirs): pass
super().__init__(idobj)
class FileHolder(ObjectHolder[mesonlib.File]): class FileHolder(ObjectHolder[mesonlib.File]):
def __init__(self, fobj: mesonlib.File): pass
super().__init__(fobj)
class HeadersHolder(ObjectHolder[build.Headers]): class HeadersHolder(ObjectHolder[build.Headers]):
def __init__(self, obj: build.Headers): pass
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
class DataHolder(ObjectHolder[build.Data]): class DataHolder(ObjectHolder[build.Data]):
def __init__(self, data: build.Data): pass
super().__init__(data)
def get_source_subdir(self): class InstallDirHolder(ObjectHolder[build.InstallDir]):
return self.held_object.source_subdir pass
def get_sources(self):
return self.held_object.sources
def get_install_dir(self):
return self.held_object.install_dir
class InstallDirHolder(ObjectHolder[build.IncludeDirs]):
def __init__(self, obj: build.InstallDir):
super().__init__(obj)
class ManHolder(ObjectHolder[build.Man]): class ManHolder(ObjectHolder[build.Man]):
def __init__(self, obj: build.Man): pass
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
class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]): class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]):
def __init__(self, held_object: build.ExtractedObjects): pass
super().__init__(held_object)
class Test(MesonInterpreterObject): class Test(MesonInterpreterObject):
def __init__(self, name: str, project: str, suite: T.List[str], exe: build.Executable, 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.protocol = TestProtocol.from_str(protocol)
self.priority = priority self.priority = priority
def get_exe(self): def get_exe(self) -> build.Executable:
return self.exe return self.exe
def get_name(self): def get_name(self) -> str:
return self.name 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, # TODO: This should really be an `ObjectHolder`, but the additional stuff in this
exception=None): # class prevents this. Thus, this class should be split into a pure
super().__init__(subinterpreter) # `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.warnings = warnings
self.disabled_feature = disabled_feature self.disabled_feature = disabled_feature
self.exception = exception self.exception = exception
@ -740,20 +723,20 @@ class SubprojectHolder(ObjectHolder[T.Optional['Interpreter']]):
}) })
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def found_method(self, args, kwargs): def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found() return self.found()
def found(self): def found(self) -> bool:
return self.held_object is not None return not isinstance(self.held_object, NullSubprojectInterpreter)
@permittedKwargs({}) @noKwargs
@noArgsFlattening @noArgsFlattening
@unholder_return @unholder_return
def get_variable_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]: 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: if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get_variable takes one or two arguments.') 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)) raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir))
varname = args[0] varname = args[0]
if not isinstance(varname, str): if not isinstance(varname, str):
@ -768,12 +751,8 @@ class SubprojectHolder(ObjectHolder[T.Optional['Interpreter']]):
raise InvalidArguments(f'Requested variable "{varname}" not found.') raise InvalidArguments(f'Requested variable "{varname}" not found.')
class ModuleObjectHolder(ObjectHolder['ModuleObject']): class ModuleObjectHolder(ObjectHolder[ModuleObject]):
def __init__(self, modobj: 'ModuleObject', interpreter: 'Interpreter'): def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
super().__init__(modobj)
self.interpreter = interpreter
def method_call(self, method_name, args, kwargs):
modobj = self.held_object modobj = self.held_object
method = modobj.methods.get(method_name) method = modobj.methods.get(method_name)
if not method: if not method:
@ -791,10 +770,10 @@ class ModuleObjectHolder(ObjectHolder['ModuleObject']):
if isinstance(ret, ModuleReturnValue): if isinstance(ret, ModuleReturnValue):
self.interpreter.process_new_values(ret.new_objects) self.interpreter.process_new_values(ret.new_objects)
ret = ret.return_value ret = ret.return_value
return self.interpreter.holderify(ret) return ret
class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject): class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject):
def __deepcopy__(self, memo): def __deepcopy__(self, memo: T.Dict[int, T.Any]) -> 'MutableModuleObjectHolder':
# Deepcopy only held object, not interpreter # Deepcopy only held object, not interpreter
modobj = copy.deepcopy(self.held_object, memo) modobj = copy.deepcopy(self.held_object, memo)
return MutableModuleObjectHolder(modobj, self.interpreter) return MutableModuleObjectHolder(modobj, self.interpreter)
@ -916,7 +895,7 @@ class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
class JarHolder(BuildTargetHolder[build.Jar]): class JarHolder(BuildTargetHolder[build.Jar]):
pass pass
class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]): class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]):
def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'): def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
super().__init__(target, interp) super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method, 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') @FeatureNew('custom_target[i].full_path', '0.54.0')
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def full_path_method(self, args, kwargs): 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) 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'): def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'):
super().__init__(target, interp) super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method, self.methods.update({'full_path': self.full_path_method,
'to_list': self.to_list_method, 'to_list': self.to_list_method,
}) })
def __repr__(self): def __repr__(self) -> str:
r = '<{} {}: {}>' r = '<{} {}: {}>'
h = self.held_object h = self.held_object
return r.format(self.__class__.__name__, h.get_id(), h.command) return r.format(self.__class__.__name__, h.get_id(), h.command)
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def full_path_method(self, args, kwargs): 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) return self.interpreter.backend.get_target_filename_abs(self.held_object)
@FeatureNew('custom_target.to_list', '0.54.0') @FeatureNew('custom_target.to_list', '0.54.0')
@noPosargs @noPosargs
@permittedKwargs({}) @noKwargs
def to_list_method(self, args, kwargs): def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]:
result = [] result = []
for i in self.held_object: for i in self.held_object:
result.append(CustomTargetIndexHolder(i, self.interpreter)) result.append(i)
return result return result
def __getitem__(self, index): def __getitem__(self, index: int) -> build.CustomTargetIndex:
return CustomTargetIndexHolder(self.held_object[index], self.interpreter) 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') 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') raise InterpreterException('Cannot delete a member of a CustomTarget')
def outdir_include(self): class RunTargetHolder(ObjectHolder[build.RunTarget]):
return IncludeDirsHolder(build.IncludeDirs('', [], False, pass
[os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
class RunTargetHolder(TargetHolder): class AliasTargetHolder(ObjectHolder[build.AliasTarget]):
def __init__(self, target, interp): pass
super().__init__(target, interp)
def __repr__(self):
r = '<{} {}: {}>'
h = self.held_object
return r.format(self.__class__.__name__, h.get_id(), h.command)
class GeneratedListHolder(ObjectHolder[build.GeneratedList]):
pass
class GeneratorHolder(ObjectHolder[build.Generator]): class GeneratorHolder(ObjectHolder[build.Generator]):
def __init__(self, gen: build.Generator, interpreter: 'Interpreter'):
def __init__(self, gen: 'build.Generator', interpreter: 'Interpreter'): super().__init__(gen, interpreter)
self().__init__(self, gen, interpreter.subproject)
self.interpreter = interpreter
self.methods.update({'process': self.process_method}) 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( @typed_kwargs(
'generator.process', 'generator.process',
KwargInfo('preserve_path_from', str, since='0.45.0'), KwargInfo('preserve_path_from', str, since='0.45.0'),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), 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]]], def process_method(self,
kwargs: 'kwargs.GeneratorProcess') -> GeneratedListHolder: 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'] preserve_path_from = kwargs['preserve_path_from']
if preserve_path_from is not None: if preserve_path_from is not None:
preserve_path_from = os.path.normpath(preserve_path_from) 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. # This is a bit of a hack. Fix properly before merging.
raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.') 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( FeatureNew.single_use(
f'Calling generator.process with CustomTaget or Index of CustomTarget.', f'Calling generator.process with CustomTaget or Index of CustomTarget.',
'0.57.0', self.interpreter.subproject) '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']) preserve_path_from, extra_args=kwargs['extra_args'])
return GeneratedListHolder(gl) return gl

View File

@ -4,15 +4,14 @@
"""Keyword Argument type annotations.""" """Keyword Argument type annotations."""
from mesonbuild import coredata
import typing as T import typing as T
from typing_extensions import TypedDict, Literal from typing_extensions import TypedDict, Literal
from .. import build
from ..mesonlib import MachineChoice, File from ..mesonlib import MachineChoice, File
from .interpreterobjects import ( from .interpreterobjects import EnvironmentVariablesObject
BuildTargetHolder, CustomTargetHolder, EnvironmentVariablesHolder,
FeatureOptionHolder, TargetHolder
)
class FuncAddProjectArgs(TypedDict): class FuncAddProjectArgs(TypedDict):
@ -34,13 +33,13 @@ class BaseTest(TypedDict):
"""Shared base for the Rust module.""" """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 should_fail: bool
timeout: int timeout: int
workdir: T.Optional[str] workdir: T.Optional[str]
depends: T.List[T.Union[CustomTargetHolder, BuildTargetHolder]] depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]]
priority: int 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] 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. 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): class FuncGenerator(TypedDict):
@ -81,7 +80,7 @@ class FuncGenerator(TypedDict):
output: T.List[str] output: T.List[str]
depfile: bool depfile: bool
capture: bool capture: bool
depends: T.List[T.Union['BuildTargetHolder', 'CustomTargetHolder']] depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
class GeneratorProcess(TypedDict): class GeneratorProcess(TypedDict):
@ -90,3 +89,16 @@ class GeneratorProcess(TypedDict):
preserve_path_from: T.Optional[str] preserve_path_from: T.Optional[str]
extra_args: T.List[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

View File

@ -5,16 +5,15 @@ from .. import dependencies
from .. import build from .. import build
from .. import mlog from .. import mlog
from ..mesonlib import unholder, MachineChoice, OptionKey from ..mesonlib import MachineChoice, OptionKey
from ..programs import OverrideProgram, ExternalProgram from ..programs import OverrideProgram, ExternalProgram
from ..interpreterbase import (MesonInterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated, from ..interpreterbase import (MesonInterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs, typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs,
MesonVersionString, InterpreterException) MesonVersionString, InterpreterException)
from .compiler import CompilerHolder
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder, from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder, CustomTargetHolder, CustomTargetIndexHolder,
EnvironmentVariablesHolder) EnvironmentVariablesObject)
import typing as T import typing as T
@ -57,11 +56,11 @@ class MesonMain(MesonInterpreterObject):
'add_devenv': self.add_devenv_method, '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)): if isinstance(prog, (build.Executable, ExternalProgram)):
return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args) return self.interpreter.backend.get_executable_serialisation([prog] + args)
found = self.interpreter.func_find_program({}, prog, {}).held_object found = self.interpreter.func_find_program({}, prog, {})
es = self.interpreter.backend.get_executable_serialisation([found] + args) es = self.interpreter.backend.get_executable_serialisation([found] + args)
es.subproject = self.interpreter.subproject es.subproject = self.interpreter.subproject
return es return es
@ -254,7 +253,7 @@ class MesonMain(MesonInterpreterObject):
for_machine = self.interpreter.machine_from_native_kwarg(kwargs) for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
clist = self.interpreter.coredata.compilers[for_machine] clist = self.interpreter.coredata.compilers[for_machine]
if cname in clist: 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.') raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.')
@noPosargs @noPosargs
@ -375,9 +374,9 @@ class MesonMain(MesonInterpreterObject):
@FeatureNew('add_devenv', '0.58.0') @FeatureNew('add_devenv', '0.58.0')
@noKwargs @noKwargs
@typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder)) @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesObject))
def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None: def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesObject], kwargs: T.Dict[str, T.Any]) -> None:
env = args[0] env = args[0]
if isinstance(env, (str, list, dict)): if isinstance(env, (str, list, dict)):
env = EnvironmentVariablesHolder(env) env = EnvironmentVariablesObject(env)
self.build.devenv.append(env.held_object) self.build.devenv.append(env.vars)

View File

@ -50,6 +50,7 @@ class PythonDependency(SystemDependency):
self.variables = python_holder.variables self.variables = python_holder.variables
self.paths = python_holder.paths self.paths = python_holder.paths
self.link_libpython = python_holder.link_libpython self.link_libpython = python_holder.link_libpython
self.info: T.Optional[T.Dict[str, str]] = None
if mesonlib.version_compare(self.version, '>= 3.0'): if mesonlib.version_compare(self.version, '>= 3.0'):
self.major_version = 3 self.major_version = 3
else: 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): class PythonInstallation(ExternalProgramHolder):
def __init__(self, interpreter, python, info): def __init__(self, python, interpreter):
ExternalProgramHolder.__init__(self, python, interpreter.subproject) ExternalProgramHolder.__init__(self, python, interpreter)
self.interpreter = interpreter info = python.info
self.subproject = self.interpreter.subproject
prefix = self.interpreter.environment.coredata.get_option(mesonlib.OptionKey('prefix')) prefix = self.interpreter.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
self.variables = info['variables'] self.variables = info['variables']
self.paths = info['paths'] self.paths = info['paths']
@ -325,11 +334,10 @@ class PythonInstallation(ExternalProgramHolder):
# behavior. See https://github.com/mesonbuild/meson/issues/4117 # behavior. See https://github.com/mesonbuild/meson/issues/4117
if not self.link_libpython: if not self.link_libpython:
new_deps = [] new_deps = []
for holder in mesonlib.extract_as_list(kwargs, 'dependencies'): for dep in mesonlib.extract_as_list(kwargs, 'dependencies'):
dep = holder.held_object
if isinstance(dep, PythonDependency): if isinstance(dep, PythonDependency):
holder = self.interpreter.holderify(dep.get_partial_dependency(compile_args=True)) dep = dep.get_partial_dependency(compile_args=True)
new_deps.append(holder) new_deps.append(dep)
kwargs['dependencies'] = new_deps kwargs['dependencies'] = new_deps
suffix = self.variables.get('EXT_SUFFIX') or self.variables.get('SO') or self.variables.get('.so') 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) dep = PythonDependency(self, self.interpreter.environment, kwargs)
if required and not dep.found(): if required and not dep.found():
raise mesonlib.MesonException('Python dependency not found') raise mesonlib.MesonException('Python dependency not found')
return self.interpreter.holderify(dep) return dep
@permittedKwargs(['pure', 'subdir']) @permittedKwargs(['pure', 'subdir'])
def install_sources_method(self, args, kwargs): def install_sources_method(self, args, kwargs):
@ -377,7 +385,7 @@ class PythonInstallation(ExternalProgramHolder):
else: else:
kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir) 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 @noPosargs
@permittedKwargs(['pure', 'subdir']) @permittedKwargs(['pure', 'subdir'])
@ -519,25 +527,26 @@ class PythonModule(ExtensionModule):
if disabled: if disabled:
mlog.log('Program', name_or_path or 'python', 'found:', mlog.red('NO'), '(disabled by:', mlog.bold(feature), ')') 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: if not name_or_path:
python = ExternalProgram('python3', mesonlib.python_command, silent=True) python = PythonExternalProgram('python3', mesonlib.python_command)
else: 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(): if not python.found() and mesonlib.is_windows():
pythonpath = self._get_win_pythonpath(name_or_path) pythonpath = self._get_win_pythonpath(name_or_path)
if pythonpath is not None: if pythonpath is not None:
name_or_path = pythonpath 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 # Last ditch effort, python2 or python3 can be named python
# on various platforms, let's not give up just yet, if an executable # 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 # named python is available and has a compatible version, let's use
# it # it
if not python.found() and name_or_path in ['python2', 'python3']: 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: if python.found() and want_modules:
for mod in want_modules: for mod in want_modules:
@ -566,11 +575,11 @@ class PythonModule(ExtensionModule):
if not python.found(): if not python.found():
if required: if required:
raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python')) raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python'))
res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) return NonExistingExternalProgram()
elif missing_modules: elif missing_modules:
if required: if required:
raise mesonlib.MesonException('{} is missing modules: {}'.format(name_or_path or 'python', ', '.join(missing_modules))) raise mesonlib.MesonException('{} is missing modules: {}'.format(name_or_path or 'python', ', '.join(missing_modules)))
res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject) return NonExistingExternalProgram()
else: else:
# Sanity check, we expect to have something that at least quacks in tune # Sanity check, we expect to have something that at least quacks in tune
try: try:
@ -586,14 +595,17 @@ class PythonModule(ExtensionModule):
mlog.debug(stderr) mlog.debug(stderr)
if isinstance(info, dict) and 'version' in info and self._check_version(name_or_path, info['version']): 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: else:
res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject)
if required: if required:
raise mesonlib.MesonException(f'{python} is not a valid python or it is missing setuptools') 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): def initialize(*args, **kwargs):
return PythonModule(*args, **kwargs) mod = PythonModule(*args, **kwargs)
mod.interpreter.append_holder_map(PythonExternalProgram, PythonInstallation)
return mod

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from mesonbuild import coredata
import os import os
import shutil import shutil
import typing as T import typing as T
@ -20,21 +21,19 @@ import xml.etree.ElementTree as ET
from . import ModuleReturnValue, ExtensionModule from . import ModuleReturnValue, ExtensionModule
from .. import build from .. import build
from .. import mesonlib
from .. import mlog 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 import extract_required_kwarg
from ..interpreter.interpreterobjects import DependencyHolder, ExternalLibraryHolder, IncludeDirsHolder, FeatureOptionHolder, GeneratedListHolder
from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs
from ..mesonlib import MesonException, File from ..programs import ExternalProgram, NonExistingExternalProgram
from ..programs import NonExistingExternalProgram
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from . import ModuleState from . import ModuleState
from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency
from ..interpreter import Interpreter from ..interpreter import Interpreter
from ..interpreter import kwargs from ..interpreter import kwargs
from ..programs import ExternalProgram
QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency] QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency]
@ -45,7 +44,7 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Resource Compiler method.""" """Keyword arguments for the Resource Compiler method."""
name: T.Optional[str] name: T.Optional[str]
sources: T.List[mesonlib.FileOrString] sources: T.List[FileOrString]
extra_args: T.List[str] extra_args: T.List[str]
method: str method: str
@ -53,7 +52,7 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Ui Compiler method.""" """Keyword arguments for the Ui Compiler method."""
sources: T.List[mesonlib.FileOrString] sources: T.List[FileOrString]
extra_args: T.List[str] extra_args: T.List[str]
method: str method: str
@ -61,25 +60,25 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Moc Compiler method.""" """Keyword arguments for the Moc Compiler method."""
sources: T.List[mesonlib.FileOrString] sources: T.List[FileOrString]
headers: T.List[mesonlib.FileOrString] headers: T.List[FileOrString]
extra_args: T.List[str] extra_args: T.List[str]
method: str method: str
include_directories: T.List[T.Union[str, IncludeDirsHolder]] include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
class PreprocessKwArgs(TypedDict): class PreprocessKwArgs(TypedDict):
sources: T.List[mesonlib.FileOrString] sources: T.List[FileOrString]
moc_sources: T.List[mesonlib.FileOrString] moc_sources: T.List[FileOrString]
moc_headers: T.List[mesonlib.FileOrString] moc_headers: T.List[FileOrString]
qresources: T.List[mesonlib.FileOrString] qresources: T.List[FileOrString]
ui_files: T.List[mesonlib.FileOrString] ui_files: T.List[FileOrString]
moc_extra_arguments: T.List[str] moc_extra_arguments: T.List[str]
rcc_extra_arguments: T.List[str] rcc_extra_arguments: T.List[str]
uic_extra_arguments: T.List[str] uic_extra_arguments: T.List[str]
include_directories: T.List[T.Union[str, IncludeDirsHolder]] include_directories: T.List[T.Union[str, build.IncludeDirs]]
dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
method: str method: str
class HasToolKwArgs(kwargs.ExtractRequired): class HasToolKwArgs(kwargs.ExtractRequired):
@ -104,10 +103,10 @@ class QtBaseModule(ExtensionModule):
def __init__(self, interpreter: 'Interpreter', qt_version: int = 5): def __init__(self, interpreter: 'Interpreter', qt_version: int = 5):
ExtensionModule.__init__(self, interpreter) ExtensionModule.__init__(self, interpreter)
self.qt_version = qt_version self.qt_version = qt_version
self.moc: 'ExternalProgram' = NonExistingExternalProgram('moc') self.moc: ExternalProgram = NonExistingExternalProgram('moc')
self.uic: 'ExternalProgram' = NonExistingExternalProgram('uic') self.uic: ExternalProgram = NonExistingExternalProgram('uic')
self.rcc: 'ExternalProgram' = NonExistingExternalProgram('rcc') self.rcc: ExternalProgram = NonExistingExternalProgram('rcc')
self.lrelease: 'ExternalProgram' = NonExistingExternalProgram('lrelease') self.lrelease: ExternalProgram = NonExistingExternalProgram('lrelease')
self.methods.update({ self.methods.update({
'has_tools': self.has_tools, 'has_tools': self.has_tools,
'preprocess': self.preprocess, 'preprocess': self.preprocess,
@ -141,14 +140,14 @@ class QtBaseModule(ExtensionModule):
if name == 'lrelease': if name == 'lrelease':
arg = ['-version'] arg = ['-version']
elif mesonlib.version_compare(qt_dep.version, '>= 5'): elif version_compare(qt_dep.version, '>= 5'):
arg = ['--version'] arg = ['--version']
else: else:
arg = ['-v'] arg = ['-v']
# Ensure that the version of qt and each tool are the same # Ensure that the version of qt and each tool are the same
def get_version(p: 'ExternalProgram') -> str: def get_version(p: ExternalProgram) -> str:
_, out, err = mesonlib.Popen_safe(p.get_command() + arg) _, out, err = Popen_safe(p.get_command() + arg)
if b.startswith('lrelease') or not qt_dep.version.startswith('4'): if b.startswith('lrelease') or not qt_dep.version.startswith('4'):
care = out care = out
else: else:
@ -157,7 +156,7 @@ class QtBaseModule(ExtensionModule):
p = state.find_program(b, required=False, p = state.find_program(b, required=False,
version_func=get_version, version_func=get_version,
wanted=wanted).held_object wanted=wanted)
if p.found(): if p.found():
setattr(self, name, p) setattr(self, name, p)
@ -172,7 +171,7 @@ class QtBaseModule(ExtensionModule):
if qt.found(): if qt.found():
# Get all tools and then make sure that they are the right version # Get all tools and then make sure that they are the right version
self.compilers_detect(state, qt) 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 self._rcc_supports_depfiles = True
else: else:
mlog.warning('rcc dependencies will not work properly until you move to Qt >= 5.14:', 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) self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix)
@staticmethod @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 abspath: str
if isinstance(rcc_file, str): if isinstance(rcc_file, str):
abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file) abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file)
@ -210,7 +209,7 @@ class QtBaseModule(ExtensionModule):
except Exception: except Exception:
raise MesonException(f'Unable to parse resource file {abspath}') 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) rcc_dirname, nodes = self._qrc_nodes(state, rcc_file)
result: T.List[File] = [] result: T.List[File] = []
for resource_path in nodes: for resource_path in nodes:
@ -243,7 +242,7 @@ class QtBaseModule(ExtensionModule):
@noPosargs @noPosargs
@typed_kwargs( @typed_kwargs(
'qt.has_tools', 'qt.has_tools',
KwargInfo('required', (bool, FeatureOptionHolder), default=False), KwargInfo('required', (bool, coredata.UserFeatureOption), default=False),
KwargInfo('method', str, default='auto'), KwargInfo('method', str, default='auto'),
) )
def has_tools(self, state: 'ModuleState', args: T.Tuple, kwargs: 'HasToolKwArgs') -> bool: 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@'], kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'],
['ui_@BASENAME@.h'], ['ui_@BASENAME@.h'],
name=f'Qt{self.qt_version} ui') 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]) return ModuleReturnValue(out, [out])
@FeatureNew('qt.compile_moc', '0.59.0') @FeatureNew('qt.compile_moc', '0.59.0')
@ -362,8 +361,8 @@ class QtBaseModule(ExtensionModule):
KwargInfo('headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]), KwargInfo('headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('method', str, default='auto'), KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (IncludeDirsHolder, str)), listify=True, default=[]), KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]), KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
) )
def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue: def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue:
self._detect_tools(state, kwargs['method']) self._detect_tools(state, kwargs['method'])
@ -378,7 +377,7 @@ class QtBaseModule(ExtensionModule):
inc = state.get_include_args(include_dirs=kwargs['include_directories']) inc = state.get_include_args(include_dirs=kwargs['include_directories'])
compile_args: T.List[str] = [] compile_args: T.List[str] = []
for dep in kwargs['dependencies']: 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] = [] 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('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('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'),
KwargInfo('method', str, default='auto'), KwargInfo('method', str, default='auto'),
KwargInfo('include_directories', ContainerTypeInfo(list, (IncludeDirsHolder, str)), listify=True, default=[]), KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), 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: def preprocess(self, state: 'ModuleState', args: T.List[T.Union[str, File]], kwargs: 'PreprocessKwArgs') -> ModuleReturnValue:
_sources = args[1:] _sources = args[1:]

View File

@ -14,16 +14,13 @@
from collections import namedtuple from collections import namedtuple
from .. import mesonlib from .. import mesonlib
from .. import build
from ..mesonlib import listify, OrderedSet from ..mesonlib import listify, OrderedSet
from . import ExtensionModule, ModuleObject, MutableModuleObject from . import ExtensionModule, ModuleObject, MutableModuleObject
from ..interpreterbase import ( from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs, noPosargs, noKwargs, permittedKwargs,
InterpreterException, InvalidArguments, InvalidCode, FeatureNew, InterpreterException, InvalidArguments, InvalidCode, FeatureNew,
) )
from ..interpreter import (
GeneratedListHolder, CustomTargetHolder,
CustomTargetIndexHolder
)
SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps') SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps')
SourceFiles = namedtuple('SourceFiles', 'sources dependencies') SourceFiles = namedtuple('SourceFiles', 'sources dependencies')
@ -49,8 +46,8 @@ class SourceSet(MutableModuleObject):
deps = [] deps = []
for x in arg: for x in arg:
if isinstance(x, (str, mesonlib.File, if isinstance(x, (str, mesonlib.File,
GeneratedListHolder, CustomTargetHolder, build.GeneratedList, build.CustomTarget,
CustomTargetIndexHolder)): build.CustomTargetIndex)):
sources.append(x) sources.append(x)
elif hasattr(x, 'found'): elif hasattr(x, 'found'):
if not allow_deps: if not allow_deps:

View File

@ -22,7 +22,6 @@ from ..mesonlib import (MesonException, Popen_safe, MachineChoice,
get_variable_regex, do_replacement, extract_as_list) get_variable_regex, do_replacement, extract_as_list)
from ..interpreterbase import InterpreterException, FeatureNew from ..interpreterbase import InterpreterException, FeatureNew
from ..interpreterbase import permittedKwargs, typed_pos_args from ..interpreterbase import permittedKwargs, typed_pos_args
from ..interpreter import DependencyHolder
from ..compilers.compilers import CFLAGS_MAPPING, CEXE_MAPPING from ..compilers.compilers import CFLAGS_MAPPING, CEXE_MAPPING
from ..dependencies import InternalDependency, PkgConfigDependency from ..dependencies import InternalDependency, PkgConfigDependency
from ..mesonlib import OptionKey from ..mesonlib import OptionKey
@ -237,7 +236,7 @@ class ExternalProject(ModuleObject):
variables = [] variables = []
dep = InternalDependency(version, incdir, compile_args, link_args, libs, dep = InternalDependency(version, incdir, compile_args, link_args, libs,
libs_whole, sources, final_deps, variables) libs_whole, sources, final_deps, variables)
return DependencyHolder(dep, self.subproject) return dep
class ExternalProjectModule(ExtensionModule): class ExternalProjectModule(ExtensionModule):

View File

@ -19,15 +19,11 @@ from . import ExtensionModule, ModuleReturnValue
from .. import mlog from .. import mlog
from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget
from ..interpreter.interpreter import TEST_KWARGS 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 ..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: if T.TYPE_CHECKING:
from . import ModuleState from . import ModuleState
@ -38,7 +34,7 @@ if T.TYPE_CHECKING:
class FuncTest(_kwargs.BaseTest): class FuncTest(_kwargs.BaseTest):
dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]] dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
is_parallel: bool is_parallel: bool
@ -55,18 +51,18 @@ class RustModule(ExtensionModule):
'bindgen': self.bindgen, 'bindgen': self.bindgen,
}) })
@typed_pos_args('rust.test', str, BuildTargetHolder) @typed_pos_args('rust.test', str, BuildTarget)
@typed_kwargs( @typed_kwargs(
'rust.test', 'rust.test',
*TEST_KWARGS, *TEST_KWARGS,
KwargInfo('is_parallel', bool, default=False), KwargInfo('is_parallel', bool, default=False),
KwargInfo( KwargInfo(
'dependencies', 'dependencies',
ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), ContainerTypeInfo(list, (Dependency, ExternalLibrary)),
listify=True, listify=True,
default=[]), 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. """Generate a rust test target from a given rust target.
Rust puts it's unitests inside it's main source files, unlike most Rust puts it's unitests inside it's main source files, unlike most
@ -151,11 +147,10 @@ class RustModule(ExtensionModule):
new_target_kwargs new_target_kwargs
) )
e = ExecutableHolder(new_target, self.interpreter)
test = self.interpreter.make_test( 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 @noPosargs
@permittedKwargs({'input', 'output', 'include_directories', 'c_args', 'args'}) @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', []))) bind_args: T.List[str] = stringlistify(listify(kwargs.get('args', [])))
# Split File and Target dependencies to add pass to CustomTarget # 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] = [] depend_files: T.List[File] = []
for d in _deps: for d in _deps:
if isinstance(d, File): if isinstance(d, File):
@ -225,7 +220,7 @@ class RustModule(ExtensionModule):
backend=state.backend, backend=state.backend,
) )
return ModuleReturnValue([target], [CustomTargetHolder(target, self.interpreter)]) return ModuleReturnValue([target], [target])
def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule: def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule:

View File

@ -11,7 +11,7 @@ from mesonbuild.mesonlib import version_compare
modules = [ modules = [
# fully typed submodules # fully typed submodules
'mesonbuild/ast', # 'mesonbuild/ast',
'mesonbuild/cmake', 'mesonbuild/cmake',
'mesonbuild/compilers', 'mesonbuild/compilers',
'mesonbuild/dependencies', 'mesonbuild/dependencies',
@ -23,6 +23,7 @@ modules = [
'mesonbuild/arglist.py', 'mesonbuild/arglist.py',
# 'mesonbuild/coredata.py', # 'mesonbuild/coredata.py',
'mesonbuild/envconfig.py', 'mesonbuild/envconfig.py',
'mesonbuild/interpreter/interpreterobjects.py',
'mesonbuild/linkers.py', 'mesonbuild/linkers.py',
'mesonbuild/mcompile.py', 'mesonbuild/mcompile.py',
'mesonbuild/mdevenv.py', 'mesonbuild/mdevenv.py',

View File

@ -20,6 +20,9 @@ exe_static2 = executable('prog-static2', 'main.c',
link_with : both_libs2.get_static_lib()) link_with : both_libs2.get_static_lib())
exe_both2 = executable('prog-both2', 'main.c', link_with : both_libs2) 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-shared-2', exe_shared2)
test('runtest-static-2', exe_static2) test('runtest-static-2', exe_static2)
test('runtest-both-2', exe_both2) test('runtest-both-2', exe_both2)

View File

@ -1,4 +1,4 @@
project('myexe', 'c') project('myexe', 'c', version: '0.1')
sub = subproject('sub') sub = subproject('sub')
prog = find_program('foobar', version : '>= 2.0', required : false) prog = find_program('foobar', version : '>= 2.0', required : false)