aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2021-04-01 08:18:44 -0400
committerXavier Claessens <xclaesse@gmail.com>2021-04-01 14:26:33 -0400
commit558a7bc6ff875f233b2ab7531e59e296b98032bd (patch)
tree6f84e2788819c9decaa0b1d72edd35c0eb8e77ac
parent2cd0723c42ae2076c7ba2b888fd7a5235d5cbf17 (diff)
downloadmeson-558a7bc6ff875f233b2ab7531e59e296b98032bd.zip
meson-558a7bc6ff875f233b2ab7531e59e296b98032bd.tar.gz
meson-558a7bc6ff875f233b2ab7531e59e296b98032bd.tar.bz2
interpreter: Move to its own folder and split it
-rw-r--r--mesonbuild/interpreter/__init__.py25
-rw-r--r--mesonbuild/interpreter/compiler.py766
-rw-r--r--mesonbuild/interpreter/interpreter.py (renamed from mesonbuild/interpreter.py)2167
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py999
-rw-r--r--mesonbuild/interpreter/mesonmain.py369
-rw-r--r--mesonbuild/modules/cmake.py3
-rw-r--r--mesonbuild/modules/keyval.py3
-rw-r--r--mesonbuild/modules/unstable_rust.py4
-rwxr-xr-xrun_unittests.py4
9 files changed, 2211 insertions, 2129 deletions
diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py
new file mode 100644
index 0000000..e571971
--- /dev/null
+++ b/mesonbuild/interpreter/__init__.py
@@ -0,0 +1,25 @@
+# SPDX-license-identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Meson interpreter."""
+
+from .interpreter import Interpreter, permitted_kwargs
+from .compiler import CompilerHolder
+from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder,
+ CustomTargetIndexHolder, MachineHolder, Test,
+ ConfigurationDataHolder, SubprojectHolder, DependencyHolder,
+ GeneratedListHolder, ExternalProgramHolder,
+ extract_required_kwarg)
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
new file mode 100644
index 0000000..3a3ce34
--- /dev/null
+++ b/mesonbuild/interpreter/compiler.py
@@ -0,0 +1,766 @@
+import functools
+
+from .interpreterobjects import (IncludeDirsHolder, ExternalLibraryHolder,
+ extract_required_kwarg, extract_search_dirs)
+
+from .. import mesonlib
+from .. import mlog
+from .. import dependencies
+from ..interpreterbase import (InterpreterObject, noPosargs, noKwargs, permittedKwargs,
+ FeatureNew, FeatureNewKwargs, disablerIfNotFound,
+ check_stringlist, InterpreterException, InvalidArguments,
+ InvalidCode)
+
+import typing as T
+
+class TryRunResultHolder(InterpreterObject):
+ def __init__(self, res):
+ super().__init__()
+ self.res = res
+ self.methods.update({'returncode': self.returncode_method,
+ 'compiled': self.compiled_method,
+ 'stdout': self.stdout_method,
+ 'stderr': self.stderr_method,
+ })
+
+ @noPosargs
+ @permittedKwargs({})
+ def returncode_method(self, args, kwargs):
+ return self.res.returncode
+
+ @noPosargs
+ @permittedKwargs({})
+ def compiled_method(self, args, kwargs):
+ return self.res.compiled
+
+ @noPosargs
+ @permittedKwargs({})
+ def stdout_method(self, args, kwargs):
+ return self.res.stdout
+
+ @noPosargs
+ @permittedKwargs({})
+ def stderr_method(self, args, kwargs):
+ return self.res.stderr
+
+header_permitted_kwargs = {
+ 'required',
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+}
+
+find_library_permitted_kwargs = {
+ 'has_headers',
+ 'required',
+ 'dirs',
+ 'static',
+}
+
+find_library_permitted_kwargs |= {'header_' + k for k in header_permitted_kwargs}
+
+class CompilerHolder(InterpreterObject):
+ def __init__(self, compiler: 'Compiler', env: 'Environment', subproject: str):
+ InterpreterObject.__init__(self)
+ self.compiler = compiler
+ self.environment = env
+ self.subproject = subproject
+ self.methods.update({'compiles': self.compiles_method,
+ 'links': self.links_method,
+ 'get_id': self.get_id_method,
+ 'get_linker_id': self.get_linker_id_method,
+ 'compute_int': self.compute_int_method,
+ 'sizeof': self.sizeof_method,
+ 'get_define': self.get_define_method,
+ 'check_header': self.check_header_method,
+ 'has_header': self.has_header_method,
+ 'has_header_symbol': self.has_header_symbol_method,
+ 'run': self.run_method,
+ 'has_function': self.has_function_method,
+ 'has_member': self.has_member_method,
+ 'has_members': self.has_members_method,
+ 'has_type': self.has_type_method,
+ 'alignment': self.alignment_method,
+ 'version': self.version_method,
+ 'cmd_array': self.cmd_array_method,
+ 'find_library': self.find_library_method,
+ 'has_argument': self.has_argument_method,
+ 'has_function_attribute': self.has_func_attribute_method,
+ 'get_supported_function_attributes': self.get_supported_function_attributes_method,
+ 'has_multi_arguments': self.has_multi_arguments_method,
+ 'get_supported_arguments': self.get_supported_arguments_method,
+ 'first_supported_argument': self.first_supported_argument_method,
+ 'has_link_argument': self.has_link_argument_method,
+ 'has_multi_link_arguments': self.has_multi_link_arguments_method,
+ 'get_supported_link_arguments': self.get_supported_link_arguments_method,
+ 'first_supported_link_argument': self.first_supported_link_argument_method,
+ 'unittest_args': self.unittest_args_method,
+ 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
+ 'get_argument_syntax': self.get_argument_syntax_method,
+ })
+
+ def _dep_msg(self, deps, endl):
+ msg_single = 'with dependency {}'
+ msg_many = 'with dependencies {}'
+ if not deps:
+ return endl
+ if endl is None:
+ endl = ''
+ names = []
+ for d in deps:
+ if isinstance(d, dependencies.InternalDependency):
+ continue
+ if isinstance(d, dependencies.ExternalLibrary):
+ name = '-l' + d.name
+ else:
+ name = d.name
+ names.append(name)
+ if not names:
+ return None
+ tpl = msg_many if len(names) > 1 else msg_single
+ return tpl.format(', '.join(names)) + endl
+
+ @noPosargs
+ @permittedKwargs({})
+ def version_method(self, args, kwargs):
+ return self.compiler.version
+
+ @noPosargs
+ @permittedKwargs({})
+ def cmd_array_method(self, args, kwargs):
+ return self.compiler.exelist
+
+ def determine_args(self, kwargs, mode='link'):
+ nobuiltins = kwargs.get('no_builtin_args', False)
+ if not isinstance(nobuiltins, bool):
+ raise InterpreterException('Type of no_builtin_args not a boolean.')
+ args = []
+ incdirs = mesonlib.extract_as_list(kwargs, 'include_directories')
+ for i in incdirs:
+ if not isinstance(i, IncludeDirsHolder):
+ 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()):
+ args += self.compiler.get_include_args(idir, False)
+ if not nobuiltins:
+ opts = self.environment.coredata.options
+ args += self.compiler.get_option_compile_args(opts)
+ if mode == 'link':
+ args += self.compiler.get_option_link_args(opts)
+ args += mesonlib.stringlistify(kwargs.get('args', []))
+ return args
+
+ def determine_dependencies(self, kwargs, endl=':'):
+ deps = kwargs.get('dependencies', None)
+ if deps is not None:
+ final_deps = []
+ while deps:
+ next_deps = []
+ for d in mesonlib.unholder(mesonlib.listify(deps)):
+ if not isinstance(d, dependencies.Dependency) or d.is_built():
+ raise InterpreterException('Dependencies must be external dependencies')
+ final_deps.append(d)
+ next_deps.extend(d.ext_deps)
+ deps = next_deps
+ deps = final_deps
+ return deps, self._dep_msg(deps, endl)
+
+ @permittedKwargs({
+ 'prefix',
+ 'args',
+ 'dependencies',
+ })
+ def alignment_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Alignment method takes exactly one positional argument.')
+ check_stringlist(args)
+ typename = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of alignment must be a string.')
+ extra_args = mesonlib.stringlistify(kwargs.get('args', []))
+ deps, msg = self.determine_dependencies(kwargs)
+ result = self.compiler.alignment(typename, prefix, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result)
+ return result
+
+ @permittedKwargs({
+ 'name',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def run_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Run method takes exactly one positional argument.')
+ code = args[0]
+ if isinstance(code, mesonlib.File):
+ code = mesonlib.File.from_absolute_file(
+ code.rel_to_builddir(self.environment.source_dir))
+ elif not isinstance(code, str):
+ raise InvalidArguments('Argument must be string or file.')
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs, endl=None)
+ result = self.compiler.run(code, self.environment, extra_args=extra_args,
+ dependencies=deps)
+ if len(testname) > 0:
+ if not result.compiled:
+ h = mlog.red('DID NOT COMPILE')
+ elif result.returncode == 0:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO (%d)' % result.returncode)
+ mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h)
+ return TryRunResultHolder(result)
+
+ @noPosargs
+ @permittedKwargs({})
+ def get_id_method(self, args, kwargs):
+ return self.compiler.get_id()
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureNew('compiler.get_linker_id', '0.53.0')
+ def get_linker_id_method(self, args, kwargs):
+ return self.compiler.get_linker_id()
+
+ @noPosargs
+ @permittedKwargs({})
+ def symbols_have_underscore_prefix_method(self, args, kwargs):
+ '''
+ Check if the compiler prefixes _ (underscore) to global C symbols
+ See: https://en.wikipedia.org/wiki/Name_mangling#C
+ '''
+ return self.compiler.symbols_have_underscore_prefix(self.environment)
+
+ @noPosargs
+ @permittedKwargs({})
+ def unittest_args_method(self, args, kwargs):
+ '''
+ This function is deprecated and should not be used.
+ It can be removed in a future version of Meson.
+ '''
+ if not hasattr(self.compiler, 'get_feature_args'):
+ raise InterpreterException(f'This {self.compiler.get_display_language()} compiler has no feature arguments.')
+ build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir())
+ return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src)
+
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def has_member_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Has_member takes exactly two arguments.')
+ check_stringlist(args)
+ typename, membername = args
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_member must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ had, cached = self.compiler.has_members(typename, [membername], prefix,
+ self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking whether type', mlog.bold(typename, True),
+ 'has member', mlog.bold(membername, True), msg, hadtxt, cached)
+ return had
+
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def has_members_method(self, args, kwargs):
+ if len(args) < 2:
+ raise InterpreterException('Has_members needs at least two arguments.')
+ check_stringlist(args)
+ typename, *membernames = args
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_members must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ had, cached = self.compiler.has_members(typename, membernames, prefix,
+ self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
+ mlog.log('Checking whether type', mlog.bold(typename, True),
+ 'has members', members, msg, hadtxt, cached)
+ return had
+
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def has_function_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Has_function takes exactly one argument.')
+ check_stringlist(args)
+ funcname = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_function must be a string.')
+ extra_args = self.determine_args(kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ had, cached = self.compiler.has_function(funcname, prefix, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached)
+ return had
+
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def has_type_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Has_type takes exactly one argument.')
+ check_stringlist(args)
+ typename = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_type must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ had, cached = self.compiler.has_type(typename, prefix, self.environment,
+ extra_args=extra_args, dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if had:
+ hadtxt = mlog.green('YES')
+ else:
+ hadtxt = mlog.red('NO')
+ mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached)
+ return had
+
+ @FeatureNew('compiler.compute_int', '0.40.0')
+ @permittedKwargs({
+ 'prefix',
+ 'low',
+ 'high',
+ 'guess',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def compute_int_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Compute_int takes exactly one argument.')
+ check_stringlist(args)
+ expression = args[0]
+ prefix = kwargs.get('prefix', '')
+ low = kwargs.get('low', None)
+ high = kwargs.get('high', None)
+ guess = kwargs.get('guess', None)
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of compute_int must be a string.')
+ if low is not None and not isinstance(low, int):
+ raise InterpreterException('Low argument of compute_int must be an int.')
+ if high is not None and not isinstance(high, int):
+ raise InterpreterException('High argument of compute_int must be an int.')
+ if guess is not None and not isinstance(guess, int):
+ raise InterpreterException('Guess argument of compute_int must be an int.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ res = self.compiler.compute_int(expression, low, high, guess, prefix,
+ self.environment, extra_args=extra_args,
+ dependencies=deps)
+ mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
+ return res
+
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def sizeof_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Sizeof takes exactly one argument.')
+ check_stringlist(args)
+ element = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of sizeof must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ esize = self.compiler.sizeof(element, prefix, self.environment,
+ extra_args=extra_args, dependencies=deps)
+ mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
+ return esize
+
+ @FeatureNew('compiler.get_define', '0.40.0')
+ @permittedKwargs({
+ 'prefix',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def get_define_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('get_define() takes exactly one argument.')
+ check_stringlist(args)
+ element = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of get_define() must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ value, cached = self.compiler.get_define(element, prefix, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached)
+ return value
+
+ @permittedKwargs({
+ 'name',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def compiles_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('compiles method takes exactly one argument.')
+ code = args[0]
+ if isinstance(code, mesonlib.File):
+ code = mesonlib.File.from_absolute_file(
+ code.rel_to_builddir(self.environment.source_dir))
+ elif not isinstance(code, str):
+ raise InvalidArguments('Argument must be string or file.')
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs, endl=None)
+ result, cached = self.compiler.compiles(code, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ if len(testname) > 0:
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ cached = mlog.blue('(cached)') if cached else ''
+ mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached)
+ return result
+
+ @permittedKwargs({
+ 'name',
+ 'no_builtin_args',
+ 'include_directories',
+ 'args',
+ 'dependencies',
+ })
+ def links_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('links method takes exactly one argument.')
+ code = args[0]
+ if isinstance(code, mesonlib.File):
+ code = mesonlib.File.from_absolute_file(
+ code.rel_to_builddir(self.environment.source_dir))
+ elif not isinstance(code, str):
+ raise InvalidArguments('Argument must be string or file.')
+ testname = kwargs.get('name', '')
+ if not isinstance(testname, str):
+ raise InterpreterException('Testname argument must be a string.')
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs, endl=None)
+ result, cached = self.compiler.links(code, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if len(testname) > 0:
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h, cached)
+ return result
+
+ @FeatureNew('compiler.check_header', '0.47.0')
+ @FeatureNewKwargs('compiler.check_header', '0.50.0', ['required'])
+ @permittedKwargs(header_permitted_kwargs)
+ def check_header_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('check_header method takes exactly one argument.')
+ check_stringlist(args)
+ hname = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_header must be a string.')
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
+ if disabled:
+ mlog.log('Check usable header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return False
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ haz, cached = self.compiler.check_header(hname, prefix, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if required and not haz:
+ raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not usable')
+ elif haz:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Check usable header', mlog.bold(hname, True), msg, h, cached)
+ return haz
+
+ @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required'])
+ @permittedKwargs(header_permitted_kwargs)
+ def has_header_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('has_header method takes exactly one argument.')
+ check_stringlist(args)
+ hname = args[0]
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_header must be a string.')
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
+ if disabled:
+ mlog.log('Has header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return False
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ haz, cached = self.compiler.has_header(hname, prefix, self.environment,
+ extra_args=extra_args, dependencies=deps)
+ cached = mlog.blue('(cached)') if cached else ''
+ if required and not haz:
+ raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not found')
+ elif haz:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log('Has header', mlog.bold(hname, True), msg, h, cached)
+ return haz
+
+ @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required'])
+ @permittedKwargs(header_permitted_kwargs)
+ def has_header_symbol_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('has_header_symbol method takes exactly two arguments.')
+ check_stringlist(args)
+ hname, symbol = args
+ prefix = kwargs.get('prefix', '')
+ if not isinstance(prefix, str):
+ raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
+ if disabled:
+ mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return False
+ extra_args = functools.partial(self.determine_args, kwargs)
+ deps, msg = self.determine_dependencies(kwargs)
+ haz, cached = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ if required and not haz:
+ raise InterpreterException(f'{self.compiler.get_display_language()} symbol {symbol} not found in header {hname}')
+ elif haz:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ cached = mlog.blue('(cached)') if cached else ''
+ mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), msg, h, cached)
+ return haz
+
+ def notfound_library(self, libname):
+ lib = dependencies.ExternalLibrary(libname, None,
+ self.environment,
+ self.compiler.language,
+ silent=True)
+ return ExternalLibraryHolder(lib, self.subproject)
+
+ @FeatureNewKwargs('compiler.find_library', '0.51.0', ['static'])
+ @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
+ @FeatureNewKwargs('compiler.find_library', '0.49.0', ['disabler'])
+ @disablerIfNotFound
+ @permittedKwargs(find_library_permitted_kwargs)
+ def find_library_method(self, args, kwargs):
+ # TODO add dependencies support?
+ if len(args) != 1:
+ raise InterpreterException('find_library method takes one argument.')
+ libname = args[0]
+ if not isinstance(libname, str):
+ raise InterpreterException('Library name not a string.')
+
+ disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
+ if disabled:
+ mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return self.notfound_library(libname)
+
+ has_header_kwargs = {k[7:]: v for k, v in kwargs.items() if k.startswith('header_')}
+ has_header_kwargs['required'] = required
+ headers = mesonlib.stringlistify(kwargs.get('has_headers', []))
+ for h in headers:
+ if not self.has_header_method([h], has_header_kwargs):
+ return self.notfound_library(libname)
+
+ search_dirs = extract_search_dirs(kwargs)
+
+ libtype = mesonlib.LibType.PREFER_SHARED
+ if 'static' in kwargs:
+ if not isinstance(kwargs['static'], bool):
+ raise InterpreterException('static must be a boolean')
+ libtype = mesonlib.LibType.STATIC if kwargs['static'] else mesonlib.LibType.SHARED
+ linkargs = self.compiler.find_library(libname, self.environment, search_dirs, libtype)
+ if required and not linkargs:
+ if libtype == mesonlib.LibType.PREFER_SHARED:
+ libtype = 'shared or static'
+ else:
+ libtype = libtype.name.lower()
+ raise InterpreterException('{} {} library {!r} not found'
+ .format(self.compiler.get_display_language(),
+ libtype, libname))
+ lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
+ self.compiler.language)
+ return ExternalLibraryHolder(lib, self.subproject)
+
+ @permittedKwargs({})
+ def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool:
+ args = mesonlib.stringlistify(args)
+ if len(args) != 1:
+ raise InterpreterException('has_argument takes exactly one argument.')
+ return self.has_multi_arguments_method(args, kwargs)
+
+ @permittedKwargs({})
+ def has_multi_arguments_method(self, args: T.Sequence[str], kwargs: dict):
+ args = mesonlib.stringlistify(args)
+ result, cached = self.compiler.has_multi_arguments(args, self.environment)
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ cached = mlog.blue('(cached)') if cached else ''
+ mlog.log(
+ 'Compiler for {} supports arguments {}:'.format(
+ self.compiler.get_display_language(), ' '.join(args)),
+ h, cached)
+ return result
+
+ @FeatureNew('compiler.get_supported_arguments', '0.43.0')
+ @permittedKwargs({})
+ def get_supported_arguments_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ supported_args = []
+ for arg in args:
+ if self.has_argument_method(arg, kwargs):
+ supported_args.append(arg)
+ return supported_args
+
+ @permittedKwargs({})
+ def first_supported_argument_method(self, args: T.Sequence[str], kwargs: dict) -> T.List[str]:
+ for arg in mesonlib.stringlistify(args):
+ if self.has_argument_method(arg, kwargs):
+ mlog.log('First supported argument:', mlog.bold(arg))
+ return [arg]
+ mlog.log('First supported argument:', mlog.red('None'))
+ return []
+
+ @FeatureNew('compiler.has_link_argument', '0.46.0')
+ @permittedKwargs({})
+ def has_link_argument_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ if len(args) != 1:
+ raise InterpreterException('has_link_argument takes exactly one argument.')
+ return self.has_multi_link_arguments_method(args, kwargs)
+
+ @FeatureNew('compiler.has_multi_link_argument', '0.46.0')
+ @permittedKwargs({})
+ def has_multi_link_arguments_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ result, cached = self.compiler.has_multi_link_arguments(args, self.environment)
+ cached = mlog.blue('(cached)') if cached else ''
+ if result:
+ h = mlog.green('YES')
+ else:
+ h = mlog.red('NO')
+ mlog.log(
+ 'Compiler for {} supports link arguments {}:'.format(
+ self.compiler.get_display_language(), ' '.join(args)),
+ h, cached)
+ return result
+
+ @FeatureNew('compiler.get_supported_link_arguments_method', '0.46.0')
+ @permittedKwargs({})
+ def get_supported_link_arguments_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ supported_args = []
+ for arg in args:
+ if self.has_link_argument_method(arg, kwargs):
+ supported_args.append(arg)
+ return supported_args
+
+ @FeatureNew('compiler.first_supported_link_argument_method', '0.46.0')
+ @permittedKwargs({})
+ def first_supported_link_argument_method(self, args, kwargs):
+ for i in mesonlib.stringlistify(args):
+ if self.has_link_argument_method(i, kwargs):
+ mlog.log('First supported link argument:', mlog.bold(i))
+ return [i]
+ mlog.log('First supported link argument:', mlog.red('None'))
+ return []
+
+ @FeatureNew('compiler.has_function_attribute', '0.48.0')
+ @permittedKwargs({})
+ def has_func_attribute_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ if len(args) != 1:
+ raise InterpreterException('has_func_attribute takes exactly one argument.')
+ result, cached = self.compiler.has_func_attribute(args[0], self.environment)
+ cached = mlog.blue('(cached)') if cached else ''
+ h = mlog.green('YES') if result else mlog.red('NO')
+ mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h, cached)
+ return result
+
+ @FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
+ @permittedKwargs({})
+ def get_supported_function_attributes_method(self, args, kwargs):
+ args = mesonlib.stringlistify(args)
+ return [a for a in args if self.has_func_attribute_method(a, kwargs)]
+
+ @FeatureNew('compiler.get_argument_syntax_method', '0.49.0')
+ @noPosargs
+ @noKwargs
+ def get_argument_syntax_method(self, args, kwargs):
+ return self.compiler.get_argument_syntax()
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 2b7a36a..2056379 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -11,54 +11,58 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from . import mparser
-from . import environment
-from . import coredata
-from . import dependencies
-from . import mlog
-from . import build
-from . import optinterpreter
-from . import compilers
-from .wrap import wrap, WrapMode
-from . import mesonlib
-from .mesonlib import FileMode, MachineChoice, OptionKey, Popen_safe, listify, extract_as_list, has_path_sep, unholder
-from .programs import ExternalProgram, NonExistingExternalProgram, OverrideProgram
-from .dependencies import InternalDependency, Dependency, NotFoundDependency, DependencyException
-from .depfile import DepFile
-from .interpreterbase import InterpreterBase, typed_pos_args
-from .interpreterbase import check_stringlist, flatten, noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
-from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
-from .interpreterbase import InterpreterObject, MutableInterpreterObject, Disabler, disablerIfNotFound
-from .interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs
-from .interpreterbase import ObjectHolder, MesonVersionString, RangeHolder
-from .interpreterbase import TYPE_var, TYPE_nkwargs
-from .modules import ModuleReturnValue, ModuleObject, ModuleState
-from .cmake import CMakeInterpreter
-from .backend.backends import TestProtocol, Backend, ExecutableSerialisation
-
-from pathlib import Path, PurePath
+from .. import mparser
+from .. import environment
+from .. import coredata
+from .. import dependencies
+from .. import mlog
+from .. import build
+from .. import optinterpreter
+from .. import compilers
+from ..wrap import wrap, WrapMode
+from .. import mesonlib
+from ..mesonlib import FileMode, MachineChoice, OptionKey, listify, extract_as_list, has_path_sep, unholder
+from ..programs import ExternalProgram, NonExistingExternalProgram
+from ..dependencies import Dependency, NotFoundDependency, DependencyException
+from ..depfile import DepFile
+from ..interpreterbase import InterpreterBase, typed_pos_args
+from ..interpreterbase import noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
+from ..interpreterbase import InterpreterException, InvalidArguments, InvalidCode, SubdirDoneRequest
+from ..interpreterbase import InterpreterObject, Disabler, disablerIfNotFound
+from ..interpreterbase import FeatureNew, FeatureDeprecated, FeatureNewKwargs, FeatureDeprecatedKwargs
+from ..interpreterbase import ObjectHolder, RangeHolder
+from ..modules import ModuleObject
+from ..cmake import CMakeInterpreter
+from ..backend.backends import Backend, ExecutableSerialisation
+
+from .mesonmain import MesonMain
+from .compiler import CompilerHolder
+from .interpreterobjects import (SubprojectHolder, MachineHolder, EnvironmentVariablesHolder,
+ FeatureOptionHolder, ExternalProgramHolder, CustomTargetHolder,
+ RunTargetHolder, IncludeDirsHolder, ConfigurationDataHolder,
+ DependencyHolder, ModuleObjectHolder, GeneratedListHolder,
+ TargetHolder, CustomTargetIndexHolder, GeneratedObjectsHolder,
+ StaticLibraryHolder, ExecutableHolder, SharedLibraryHolder,
+ SharedModuleHolder, HeadersHolder, BothLibrariesHolder,
+ BuildTargetHolder, DataHolder, JarHolder, Test, RunProcess,
+ ManHolder, GeneratorHolder, InstallDirHolder, extract_required_kwarg,
+ extract_search_dirs)
+
+from pathlib import Path
import os
import shutil
import uuid
import re
-import shlex
import stat
-import subprocess
import collections
-import functools
import typing as T
import importlib
if T.TYPE_CHECKING:
- from .compilers import Compiler
- from .envconfig import MachineInfo
- from .environment import Environment
-
-permitted_method_kwargs = {
- 'partial_dependency': {'compile_args', 'link_args', 'links', 'includes',
- 'sources'},
-}
+ from ..compilers import Compiler
+ from ..envconfig import MachineInfo
+ from ..environment import Environment
def stringifyUserArguments(args, quote=False):
if isinstance(args, list):
@@ -71,1736 +75,6 @@ def stringifyUserArguments(args, quote=False):
return f"'{args}'" if quote else args
raise InvalidArguments('Function accepts only strings, integers, lists, dictionaries and lists thereof.')
-
-class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOption]):
- def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, option)
- if option.is_auto():
- # TODO: we need to case here because options is not a TypedDict
- self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')])
- self.name = name
- self.methods.update({'enabled': self.enabled_method,
- 'disabled': self.disabled_method,
- 'auto': self.auto_method,
- })
-
- @noPosargs
- @permittedKwargs({})
- def enabled_method(self, args, kwargs):
- return self.held_object.is_enabled()
-
- @noPosargs
- @permittedKwargs({})
- def disabled_method(self, args, kwargs):
- return self.held_object.is_disabled()
-
- @noPosargs
- @permittedKwargs({})
- def auto_method(self, args, kwargs):
- return self.held_object.is_auto()
-
-def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
- val = kwargs.get('required', default)
- disabled = False
- required = False
- feature = None
- if isinstance(val, FeatureOptionHolder):
- if not feature_check:
- feature_check = FeatureNew('User option "feature"', '0.47.0')
- feature_check.use(subproject)
- option = val.held_object
- feature = val.name
- if option.is_disabled():
- disabled = True
- elif option.is_enabled():
- required = True
- elif isinstance(val, bool):
- required = val
- else:
- raise InterpreterException('required keyword argument must be boolean or a feature option')
-
- # Keep boolean value in kwargs to simplify other places where this kwarg is
- # checked.
- kwargs['required'] = required
-
- return disabled, required, feature
-
-def extract_search_dirs(kwargs):
- search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
- search_dirs = [Path(d).expanduser() for d in search_dirs]
- for d in search_dirs:
- if mesonlib.is_windows() and d.root.startswith('\\'):
- # a Unix-path starting with `/` that is not absolute on Windows.
- # discard without failing for end-user ease of cross-platform directory arrays
- continue
- if not d.is_absolute():
- raise InvalidCode(f'Search directory {d} is not an absolute path.')
- return list(map(str, search_dirs))
-
-class TryRunResultHolder(InterpreterObject):
- def __init__(self, res):
- super().__init__()
- self.res = res
- self.methods.update({'returncode': self.returncode_method,
- 'compiled': self.compiled_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
-
- @noPosargs
- @permittedKwargs({})
- def returncode_method(self, args, kwargs):
- return self.res.returncode
-
- @noPosargs
- @permittedKwargs({})
- def compiled_method(self, args, kwargs):
- return self.res.compiled
-
- @noPosargs
- @permittedKwargs({})
- def stdout_method(self, args, kwargs):
- return self.res.stdout
-
- @noPosargs
- @permittedKwargs({})
- def stderr_method(self, args, kwargs):
- return self.res.stderr
-
-class RunProcess(InterpreterObject):
-
- def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
- super().__init__()
- if not isinstance(cmd, ExternalProgram):
- raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
- self.capture = capture
- pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
- self.returncode = pc.returncode
- self.methods.update({'returncode': self.returncode_method,
- 'stdout': self.stdout_method,
- 'stderr': self.stderr_method,
- })
-
- def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
- command_array = cmd.get_command() + args
- menv = {'MESON_SOURCE_ROOT': source_dir,
- 'MESON_BUILD_ROOT': build_dir,
- 'MESON_SUBDIR': subdir,
- 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
- }
- if in_builddir:
- cwd = os.path.join(build_dir, subdir)
- else:
- cwd = os.path.join(source_dir, subdir)
- child_env = os.environ.copy()
- child_env.update(menv)
- child_env = env.get_env(child_env)
- stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
- mlog.debug('Running command:', ' '.join(command_array))
- try:
- p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
- if self.capture:
- mlog.debug('--- stdout ---')
- mlog.debug(o)
- else:
- o = ''
- mlog.debug('--- stdout disabled ---')
- mlog.debug('--- stderr ---')
- mlog.debug(e)
- mlog.debug('')
-
- if check and p.returncode != 0:
- raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
-
- return p, o, e
- except FileNotFoundError:
- raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
-
- @noPosargs
- @permittedKwargs({})
- def returncode_method(self, args, kwargs):
- return self.returncode
-
- @noPosargs
- @permittedKwargs({})
- def stdout_method(self, args, kwargs):
- return self.stdout
-
- @noPosargs
- @permittedKwargs({})
- def stderr_method(self, args, kwargs):
- return self.stderr
-
-class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]):
- def __init__(self, initial_values=None, subproject: str = ''):
- MutableInterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.EnvironmentVariables(), subproject)
- self.methods.update({'set': self.set_method,
- 'append': self.append_method,
- 'prepend': self.prepend_method,
- })
- if isinstance(initial_values, dict):
- for k, v in initial_values.items():
- self.set_method([k, v], {})
- elif initial_values is not None:
- for e in mesonlib.stringlistify(initial_values):
- if '=' not in e:
- raise InterpreterException('Env var definition must be of type key=val.')
- (k, val) = e.split('=', 1)
- k = k.strip()
- val = val.strip()
- if ' ' in k:
- raise InterpreterException('Env var key must not have spaces in it.')
- self.set_method([k, val], {})
-
- def __repr__(self) -> str:
- repr_str = "<{0}: {1}>"
- return repr_str.format(self.__class__.__name__, self.held_object.envvars)
-
- def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str:
- separator = kwargs.get('separator', os.pathsep)
- if not isinstance(separator, str):
- raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
- " argument needs to be a string.")
- return separator
-
- def warn_if_has_name(self, name: str) -> None:
- # Multiple append/prepend operations was not supported until 0.58.0.
- if self.held_object.has_name(name):
- m = f'Overriding previous value of environment variable {name!r} with a new one'
- FeatureNew('0.58.0', m).use(self.subproject)
-
- @stringArgs
- @permittedKwargs({'separator'})
- @typed_pos_args('environment.set', str, varargs=str, min_varargs=1)
- def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
- name, values = args
- separator = self.unpack_separator(kwargs)
- self.held_object.set(name, values, separator)
-
- @stringArgs
- @permittedKwargs({'separator'})
- @typed_pos_args('environment.append', str, varargs=str, min_varargs=1)
- def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
- name, values = args
- separator = self.unpack_separator(kwargs)
- self.warn_if_has_name(name)
- self.held_object.append(name, values, separator)
-
- @stringArgs
- @permittedKwargs({'separator'})
- @typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1)
- def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
- name, values = args
- separator = self.unpack_separator(kwargs)
- self.warn_if_has_name(name)
- self.held_object.prepend(name, values, separator)
-
-
-class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.ConfigurationData]):
- def __init__(self, pv, initial_values=None):
- MutableInterpreterObject.__init__(self)
- self.used = False # These objects become immutable after use in configure_file.
- ObjectHolder.__init__(self, build.ConfigurationData(), pv)
- self.methods.update({'set': self.set_method,
- 'set10': self.set10_method,
- 'set_quoted': self.set_quoted_method,
- 'has': self.has_method,
- 'get': self.get_method,
- 'keys': self.keys_method,
- 'get_unquoted': self.get_unquoted_method,
- 'merge_from': self.merge_from_method,
- })
- if isinstance(initial_values, dict):
- for k, v in initial_values.items():
- self.set_method([k, v], {})
- elif initial_values:
- raise AssertionError('Unsupported ConfigurationDataHolder initial_values')
-
- def is_used(self):
- return self.used
-
- def mark_used(self):
- self.used = True
-
- def validate_args(self, args, kwargs):
- if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
- mlog.deprecation('Passing a list as the single argument to '
- 'configuration_data.set is deprecated. This will '
- 'become a hard error in the future.',
- location=self.current_node)
- args = args[0]
-
- if len(args) != 2:
- raise InterpreterException("Configuration set requires 2 arguments.")
- if self.used:
- raise InterpreterException("Can not set values on configuration object that has been used.")
- name, val = args
- if not isinstance(val, (int, str)):
- msg = 'Setting a configuration data value to {!r} is invalid, ' \
- 'and will fail at configure_file(). If you are using it ' \
- 'just to store some values, please use a dict instead.'
- mlog.deprecation(msg.format(val), location=self.current_node)
- desc = kwargs.get('description', None)
- if not isinstance(name, str):
- raise InterpreterException("First argument to set must be a string.")
- if desc is not None and not isinstance(desc, str):
- raise InterpreterException('Description must be a string.')
-
- return name, val, desc
-
- @noArgsFlattening
- def set_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- self.held_object.values[name] = (val, desc)
-
- def set_quoted_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- if not isinstance(val, str):
- raise InterpreterException("Second argument to set_quoted must be a string.")
- escaped_val = '\\"'.join(val.split('"'))
- self.held_object.values[name] = ('"' + escaped_val + '"', desc)
-
- def set10_method(self, args, kwargs):
- (name, val, desc) = self.validate_args(args, kwargs)
- if val:
- self.held_object.values[name] = (1, desc)
- else:
- self.held_object.values[name] = (0, desc)
-
- def has_method(self, args, kwargs):
- return args[0] in self.held_object.values
-
- @FeatureNew('configuration_data.get()', '0.38.0')
- @noArgsFlattening
- def get_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- name = args[0]
- if name in self.held_object:
- return self.held_object.get(name)[0]
- if len(args) > 1:
- return args[1]
- raise InterpreterException('Entry %s not in configuration data.' % name)
-
- @FeatureNew('configuration_data.get_unquoted()', '0.44.0')
- def get_unquoted_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get method takes one or two arguments.')
- name = args[0]
- if name in self.held_object:
- val = self.held_object.get(name)[0]
- elif len(args) > 1:
- val = args[1]
- else:
- raise InterpreterException('Entry %s not in configuration data.' % name)
- if val[0] == '"' and val[-1] == '"':
- return val[1:-1]
- return val
-
- def get(self, name):
- return self.held_object.values[name] # (val, desc)
-
- @FeatureNew('configuration_data.keys()', '0.57.0')
- @noPosargs
- def keys_method(self, args, kwargs):
- return sorted(self.keys())
-
- def keys(self):
- return self.held_object.values.keys()
-
- def merge_from_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Merge_from takes one positional argument.')
- from_object = args[0]
- if not isinstance(from_object, ConfigurationDataHolder):
- raise InterpreterException('Merge_from argument must be a configuration data object.')
- from_object = from_object.held_object
- for k, v in from_object.values.items():
- self.held_object.values[k] = v
-
-# Interpreter objects can not be pickled so we must have
-# these wrappers.
-
-class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
- def __init__(self, dep: Dependency, pv: str):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, dep, pv)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'version': self.version_method,
- 'name': self.name_method,
- 'get_pkgconfig_variable': self.pkgconfig_method,
- 'get_configtool_variable': self.configtool_method,
- 'get_variable': self.variable_method,
- 'partial_dependency': self.partial_dependency_method,
- 'include_type': self.include_type_method,
- 'as_system': self.as_system_method,
- 'as_link_whole': self.as_link_whole_method,
- })
-
- def found(self):
- return self.found_method([], {})
-
- @noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
- return self.held_object.type_name
-
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- if self.held_object.type_name == 'internal':
- return True
- return self.held_object.found()
-
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return self.held_object.get_version()
-
- @noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
- return self.held_object.get_name()
-
- @FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0',
- 'use Dependency.get_variable(pkgconfig : ...) instead')
- @permittedKwargs({'define_variable', 'default'})
- def pkgconfig_method(self, args, kwargs):
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_pkgconfig_variable(varname, kwargs)
-
- @FeatureNew('dep.get_configtool_variable', '0.44.0')
- @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0',
- 'use Dependency.get_variable(configtool : ...) instead')
- @permittedKwargs({})
- def configtool_method(self, args, kwargs):
- args = listify(args)
- if len(args) != 1:
- raise InterpreterException('get_configtool_variable takes exactly one argument.')
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Variable name must be a string.')
- return self.held_object.get_configtool_variable(varname)
-
- @FeatureNew('dep.partial_dependency', '0.46.0')
- @noPosargs
- @permittedKwargs(permitted_method_kwargs['partial_dependency'])
- def partial_dependency_method(self, args, kwargs):
- pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
-
- @FeatureNew('dep.get_variable', '0.51.0')
- @typed_pos_args('dep.get_variable', optargs=[str])
- @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
- @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal'])
- def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> str:
- default_varname = args[0]
- if default_varname is not None:
- FeatureNew('0.58.0', 'Positional argument to dep.get_variable()').use(self.subproject)
- for k in ['cmake', 'pkgconfig', 'configtool', 'internal']:
- kwargs.setdefault(k, default_varname)
- return self.held_object.get_variable(**kwargs)
-
- @FeatureNew('dep.include_type', '0.52.0')
- @noPosargs
- @permittedKwargs({})
- def include_type_method(self, args, kwargs):
- return self.held_object.get_include_type()
-
- @FeatureNew('dep.as_system', '0.52.0')
- @permittedKwargs({})
- def as_system_method(self, args, kwargs):
- args = listify(args)
- new_is_system = 'system'
- if len(args) > 1:
- raise InterpreterException('as_system takes only one optional value')
- if len(args) == 1:
- new_is_system = args[0]
- new_dep = self.held_object.generate_system_dependency(new_is_system)
- return DependencyHolder(new_dep, self.subproject)
-
- @FeatureNew('dep.as_link_whole', '0.56.0')
- @permittedKwargs({})
- @noPosargs
- def as_link_whole_method(self, args, kwargs):
- if not isinstance(self.held_object, InternalDependency):
- raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
- new_dep = self.held_object.generate_link_whole_dependency()
- return DependencyHolder(new_dep, self.subproject)
-
-class ExternalProgramHolder(InterpreterObject, ObjectHolder[ExternalProgram]):
- def __init__(self, ep: ExternalProgram, subproject: str, backend=None):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, ep)
- self.subproject = subproject
- self.backend = backend
- self.methods.update({'found': self.found_method,
- 'path': self.path_method,
- 'full_path': self.full_path_method})
- self.cached_version = None
-
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
-
- @noPosargs
- @permittedKwargs({})
- @FeatureDeprecated('ExternalProgram.path', '0.55.0',
- 'use ExternalProgram.full_path() instead')
- def path_method(self, args, kwargs):
- return self._full_path()
-
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('ExternalProgram.full_path', '0.55.0')
- def full_path_method(self, args, kwargs):
- return self._full_path()
-
- def _full_path(self):
- exe = self.held_object
- if isinstance(exe, build.Executable):
- return self.backend.get_target_filename_abs(exe)
- return exe.get_path()
-
- def found(self):
- return isinstance(self.held_object, build.Executable) or self.held_object.found()
-
- 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()
-
- def get_version(self, interpreter):
- if isinstance(self.held_object, build.Executable):
- return self.held_object.project_version
- if not self.cached_version:
- raw_cmd = self.get_command() + ['--version']
- cmd = [self, '--version']
- res = interpreter.run_command_impl(interpreter.current_node, cmd, {}, True)
- if res.returncode != 0:
- m = 'Running {!r} failed'
- raise InterpreterException(m.format(raw_cmd))
- output = res.stdout.strip()
- if not output:
- output = res.stderr.strip()
- match = re.search(r'([0-9][0-9\.]+)', output)
- if not match:
- m = 'Could not find a version number in output of {!r}'
- raise InterpreterException(m.format(raw_cmd))
- self.cached_version = match.group(1)
- return self.cached_version
-
-class ExternalLibraryHolder(InterpreterObject, ObjectHolder[dependencies.ExternalLibrary]):
- def __init__(self, el: dependencies.ExternalLibrary, pv: str):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, el, pv)
- self.methods.update({'found': self.found_method,
- 'type_name': self.type_name_method,
- 'partial_dependency': self.partial_dependency_method,
- })
-
- def found(self):
- return self.held_object.found()
-
- @noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
- return self.held_object.type_name
-
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
-
- def get_name(self):
- return self.held_object.name
-
- def get_compile_args(self):
- return self.held_object.get_compile_args()
-
- def get_link_args(self):
- return self.held_object.get_link_args()
-
- def get_exe_args(self):
- return self.held_object.get_exe_args()
-
- @FeatureNew('dep.partial_dependency', '0.46.0')
- @noPosargs
- @permittedKwargs(permitted_method_kwargs['partial_dependency'])
- def partial_dependency_method(self, args, kwargs):
- pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
-
-class GeneratorHolder(InterpreterObject, ObjectHolder[build.Generator]):
- @FeatureNewKwargs('generator', '0.43.0', ['capture'])
- def __init__(self, interp, args, kwargs):
- self.interpreter = interp
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.Generator(args, kwargs), interp.subproject)
- self.methods.update({'process': self.process_method})
-
- @FeatureNewKwargs('generator.process', '0.45.0', ['preserve_path_from'])
- @permittedKwargs({'extra_args', 'preserve_path_from'})
- def process_method(self, args, kwargs):
- extras = mesonlib.stringlistify(kwargs.get('extra_args', []))
- if 'preserve_path_from' in kwargs:
- preserve_path_from = kwargs['preserve_path_from']
- if not isinstance(preserve_path_from, str):
- raise InvalidArguments('Preserve_path_from must be a string.')
- preserve_path_from = os.path.normpath(preserve_path_from)
- if not os.path.isabs(preserve_path_from):
- # This is a bit of a hack. Fix properly before merging.
- raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
- else:
- preserve_path_from = None
- gl = self.held_object.process_files('Generator', args, self.interpreter,
- preserve_path_from, extra_args=extras)
- return GeneratedListHolder(gl)
-
-
-class GeneratedListHolder(InterpreterObject, ObjectHolder[build.GeneratedList]):
- def __init__(self, arg1, extra_args=None):
- InterpreterObject.__init__(self)
- if isinstance(arg1, GeneratorHolder):
- ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args if extra_args is not None else []))
- else:
- ObjectHolder.__init__(self, arg1)
-
- def __repr__(self):
- r = '<{}: {!r}>'
- return r.format(self.__class__.__name__, self.held_object.get_outputs())
-
- def add_file(self, a):
- self.held_object.add_file(a)
-
-# A machine that's statically known from the cross file
-class MachineHolder(InterpreterObject, ObjectHolder['MachineInfo']):
- def __init__(self, machine_info: 'MachineInfo'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, machine_info)
- self.methods.update({'system': self.system_method,
- 'cpu': self.cpu_method,
- 'cpu_family': self.cpu_family_method,
- 'endian': self.endian_method,
- })
-
- @noPosargs
- @permittedKwargs({})
- def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
- return self.held_object.cpu_family
-
- @noPosargs
- @permittedKwargs({})
- def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
- return self.held_object.cpu
-
- @noPosargs
- @permittedKwargs({})
- def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
- return self.held_object.system
-
- @noPosargs
- @permittedKwargs({})
- def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
- return self.held_object.endian
-
-class IncludeDirsHolder(InterpreterObject, ObjectHolder[build.IncludeDirs]):
- def __init__(self, idobj: build.IncludeDirs):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, idobj)
-
-class HeadersHolder(InterpreterObject, ObjectHolder[build.Headers]):
-
- def __init__(self, obj: build.Headers):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, 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(InterpreterObject, ObjectHolder[build.Data]):
- def __init__(self, data: build.Data):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, data)
-
- def get_source_subdir(self):
- return self.held_object.source_subdir
-
- def get_sources(self):
- return self.held_object.sources
-
- def get_install_dir(self):
- return self.held_object.install_dir
-
-class InstallDirHolder(InterpreterObject, ObjectHolder[build.IncludeDirs]):
-
- def __init__(self, obj: build.InstallDir):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, obj)
-
-class ManHolder(InterpreterObject, ObjectHolder[build.Man]):
-
- def __init__(self, obj: build.Man):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, 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(InterpreterObject, ObjectHolder[build.ExtractedObjects]):
- def __init__(self, held_object: build.ExtractedObjects):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, held_object)
-
-
-_Target = T.TypeVar('_Target', bound=build.Target)
-
-
-class TargetHolder(InterpreterObject, ObjectHolder[_Target]):
- def __init__(self, target: _Target, interp: 'Interpreter'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, target, interp.subproject)
- self.interpreter = interp
-
-
-_BuildTarget = T.TypeVar('_BuildTarget', bound=build.BuildTarget)
-
-class BuildTargetHolder(TargetHolder[_BuildTarget]):
- def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
- super().__init__(target, interp)
- self.methods.update({'extract_objects': self.extract_objects_method,
- 'extract_all_objects': self.extract_all_objects_method,
- 'name': self.name_method,
- 'get_id': self.get_id_method,
- 'outdir': self.outdir_method,
- 'full_path': self.full_path_method,
- 'private_dir_include': self.private_dir_include_method,
- })
-
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.filename)
-
- def is_cross(self):
- return not self.held_object.environment.machines.matches_build_machine(self.held_object.for_machine)
-
- @noPosargs
- @permittedKwargs({})
- def private_dir_include_method(self, args, kwargs):
- return IncludeDirsHolder(build.IncludeDirs('', [], False,
- [self.interpreter.backend.get_target_private_dir(self.held_object)]))
-
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
-
- @noPosargs
- @permittedKwargs({})
- def outdir_method(self, args, kwargs):
- return self.interpreter.backend.get_target_dir(self.held_object)
-
- @permittedKwargs({})
- def extract_objects_method(self, args, kwargs):
- gobjs = self.held_object.extract_objects(args)
- return GeneratedObjectsHolder(gobjs)
-
- @FeatureNewKwargs('extract_all_objects', '0.46.0', ['recursive'])
- @noPosargs
- @permittedKwargs({'recursive'})
- def extract_all_objects_method(self, args, kwargs):
- recursive = kwargs.get('recursive', False)
- gobjs = self.held_object.extract_all_objects(recursive)
- if gobjs.objlist and 'recursive' not in kwargs:
- mlog.warning('extract_all_objects called without setting recursive '
- 'keyword argument. Meson currently defaults to '
- 'non-recursive to maintain backward compatibility but '
- 'the default will be changed in the future.',
- location=self.current_node)
- return GeneratedObjectsHolder(gobjs)
-
- @noPosargs
- @permittedKwargs({})
- def get_id_method(self, args, kwargs):
- return self.held_object.get_id()
-
- @FeatureNew('name', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
- return self.held_object.name
-
-class ExecutableHolder(BuildTargetHolder[build.Executable]):
- pass
-
-class StaticLibraryHolder(BuildTargetHolder[build.StaticLibrary]):
- pass
-
-class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
- def __init__(self, target: build.SharedLibrary, interp: 'Interpreter'):
- super().__init__(target, interp)
- # Set to True only when called from self.func_shared_lib().
- target.shared_library_only = False
-
-class BothLibrariesHolder(BuildTargetHolder):
- def __init__(self, shared_holder, static_holder, interp):
- # FIXME: This build target always represents the shared library, but
- # that should be configurable.
- super().__init__(shared_holder.held_object, interp)
- self.shared_holder = shared_holder
- self.static_holder = static_holder
- self.methods.update({'get_shared_lib': self.get_shared_lib_method,
- 'get_static_lib': self.get_static_lib_method,
- })
-
- def __repr__(self):
- r = '<{} {}: {}, {}: {}>'
- h1 = self.shared_holder.held_object
- h2 = self.static_holder.held_object
- return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
-
- @noPosargs
- @permittedKwargs({})
- def get_shared_lib_method(self, args, kwargs):
- return self.shared_holder
-
- @noPosargs
- @permittedKwargs({})
- def get_static_lib_method(self, args, kwargs):
- return self.static_holder
-
-class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
- pass
-
-class JarHolder(BuildTargetHolder[build.Jar]):
- pass
-
-class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]):
- def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
- super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- })
-
- @FeatureNew('custom_target[i].full_path', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
-
-class CustomTargetHolder(TargetHolder):
- def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'):
- super().__init__(target, interp)
- self.methods.update({'full_path': self.full_path_method,
- 'to_list': self.to_list_method,
- })
-
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.command)
-
- @noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
-
- @FeatureNew('custom_target.to_list', '0.54.0')
- @noPosargs
- @permittedKwargs({})
- def to_list_method(self, args, kwargs):
- result = []
- for i in self.held_object:
- result.append(CustomTargetIndexHolder(i, self.interpreter))
- return result
-
- def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
-
- def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
- raise InterpreterException('Cannot set a member of a CustomTarget')
-
- def __delitem__(self, index): # lgtm[py/unexpected-raise-in-special-method]
- raise InterpreterException('Cannot delete a member of a CustomTarget')
-
- def outdir_include(self):
- return IncludeDirsHolder(build.IncludeDirs('', [], False,
- [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
-
-class RunTargetHolder(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
-
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.command)
-
-class Test(InterpreterObject):
- def __init__(self, name: str, project: str, suite: T.List[str], exe: build.Executable,
- depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]],
- is_parallel: bool, cmd_args: T.List[str], env: build.EnvironmentVariables,
- should_fail: bool, timeout: int, workdir: T.Optional[str], protocol: str,
- priority: int):
- InterpreterObject.__init__(self)
- self.name = name
- self.suite = suite
- self.project_name = project
- self.exe = exe
- self.depends = depends
- self.is_parallel = is_parallel
- self.cmd_args = cmd_args
- self.env = env
- self.should_fail = should_fail
- self.timeout = timeout
- self.workdir = workdir
- self.protocol = TestProtocol.from_str(protocol)
- self.priority = priority
-
- def get_exe(self):
- return self.exe
-
- def get_name(self):
- return self.name
-
-class SubprojectHolder(InterpreterObject, ObjectHolder[T.Optional['Interpreter']]):
-
- def __init__(self, subinterpreter: T.Optional['Interpreter'], subdir: str, warnings=0, disabled_feature=None,
- exception=None):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, subinterpreter)
- self.warnings = warnings
- self.disabled_feature = disabled_feature
- self.exception = exception
- self.subdir = PurePath(subdir).as_posix()
- self.methods.update({'get_variable': self.get_variable_method,
- 'found': self.found_method,
- })
-
- @noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
-
- def found(self):
- return self.held_object is not None
-
- @permittedKwargs({})
- @noArgsFlattening
- def get_variable_method(self, args, kwargs):
- if len(args) < 1 or len(args) > 2:
- raise InterpreterException('Get_variable takes one or two arguments.')
- if not self.found():
- raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir))
- varname = args[0]
- if not isinstance(varname, str):
- raise InterpreterException('Get_variable first argument must be a string.')
- try:
- return self.held_object.variables[varname]
- except KeyError:
- pass
-
- if len(args) == 2:
- return args[1]
-
- raise InvalidArguments(f'Requested variable "{varname}" not found.')
-
-header_permitted_kwargs = {
- 'required',
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
-}
-
-find_library_permitted_kwargs = {
- 'has_headers',
- 'required',
- 'dirs',
- 'static',
-}
-
-find_library_permitted_kwargs |= {'header_' + k for k in header_permitted_kwargs}
-
-class CompilerHolder(InterpreterObject):
- def __init__(self, compiler: 'Compiler', env: 'Environment', subproject: str):
- InterpreterObject.__init__(self)
- self.compiler = compiler
- self.environment = env
- self.subproject = subproject
- self.methods.update({'compiles': self.compiles_method,
- 'links': self.links_method,
- 'get_id': self.get_id_method,
- 'get_linker_id': self.get_linker_id_method,
- 'compute_int': self.compute_int_method,
- 'sizeof': self.sizeof_method,
- 'get_define': self.get_define_method,
- 'check_header': self.check_header_method,
- 'has_header': self.has_header_method,
- 'has_header_symbol': self.has_header_symbol_method,
- 'run': self.run_method,
- 'has_function': self.has_function_method,
- 'has_member': self.has_member_method,
- 'has_members': self.has_members_method,
- 'has_type': self.has_type_method,
- 'alignment': self.alignment_method,
- 'version': self.version_method,
- 'cmd_array': self.cmd_array_method,
- 'find_library': self.find_library_method,
- 'has_argument': self.has_argument_method,
- 'has_function_attribute': self.has_func_attribute_method,
- 'get_supported_function_attributes': self.get_supported_function_attributes_method,
- 'has_multi_arguments': self.has_multi_arguments_method,
- 'get_supported_arguments': self.get_supported_arguments_method,
- 'first_supported_argument': self.first_supported_argument_method,
- 'has_link_argument': self.has_link_argument_method,
- 'has_multi_link_arguments': self.has_multi_link_arguments_method,
- 'get_supported_link_arguments': self.get_supported_link_arguments_method,
- 'first_supported_link_argument': self.first_supported_link_argument_method,
- 'unittest_args': self.unittest_args_method,
- 'symbols_have_underscore_prefix': self.symbols_have_underscore_prefix_method,
- 'get_argument_syntax': self.get_argument_syntax_method,
- })
-
- def _dep_msg(self, deps, endl):
- msg_single = 'with dependency {}'
- msg_many = 'with dependencies {}'
- if not deps:
- return endl
- if endl is None:
- endl = ''
- names = []
- for d in deps:
- if isinstance(d, dependencies.InternalDependency):
- continue
- if isinstance(d, dependencies.ExternalLibrary):
- name = '-l' + d.name
- else:
- name = d.name
- names.append(name)
- if not names:
- return None
- tpl = msg_many if len(names) > 1 else msg_single
- return tpl.format(', '.join(names)) + endl
-
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return self.compiler.version
-
- @noPosargs
- @permittedKwargs({})
- def cmd_array_method(self, args, kwargs):
- return self.compiler.exelist
-
- def determine_args(self, kwargs, mode='link'):
- nobuiltins = kwargs.get('no_builtin_args', False)
- if not isinstance(nobuiltins, bool):
- raise InterpreterException('Type of no_builtin_args not a boolean.')
- args = []
- incdirs = extract_as_list(kwargs, 'include_directories')
- for i in incdirs:
- if not isinstance(i, IncludeDirsHolder):
- 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()):
- args += self.compiler.get_include_args(idir, False)
- if not nobuiltins:
- opts = self.environment.coredata.options
- args += self.compiler.get_option_compile_args(opts)
- if mode == 'link':
- args += self.compiler.get_option_link_args(opts)
- args += mesonlib.stringlistify(kwargs.get('args', []))
- return args
-
- def determine_dependencies(self, kwargs, endl=':'):
- deps = kwargs.get('dependencies', None)
- if deps is not None:
- final_deps = []
- while deps:
- next_deps = []
- for d in unholder(listify(deps)):
- if not isinstance(d, Dependency) or d.is_built():
- raise InterpreterException('Dependencies must be external dependencies')
- final_deps.append(d)
- next_deps.extend(d.ext_deps)
- deps = next_deps
- deps = final_deps
- return deps, self._dep_msg(deps, endl)
-
- @permittedKwargs({
- 'prefix',
- 'args',
- 'dependencies',
- })
- def alignment_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Alignment method takes exactly one positional argument.')
- check_stringlist(args)
- typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of alignment must be a string.')
- extra_args = mesonlib.stringlistify(kwargs.get('args', []))
- deps, msg = self.determine_dependencies(kwargs)
- result = self.compiler.alignment(typename, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- mlog.log('Checking for alignment of', mlog.bold(typename, True), msg, result)
- return result
-
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def run_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Run method takes exactly one positional argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result = self.compiler.run(code, self.environment, extra_args=extra_args,
- dependencies=deps)
- if len(testname) > 0:
- if not result.compiled:
- h = mlog.red('DID NOT COMPILE')
- elif result.returncode == 0:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO (%d)' % result.returncode)
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h)
- return TryRunResultHolder(result)
-
- @noPosargs
- @permittedKwargs({})
- def get_id_method(self, args, kwargs):
- return self.compiler.get_id()
-
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('compiler.get_linker_id', '0.53.0')
- def get_linker_id_method(self, args, kwargs):
- return self.compiler.get_linker_id()
-
- @noPosargs
- @permittedKwargs({})
- def symbols_have_underscore_prefix_method(self, args, kwargs):
- '''
- Check if the compiler prefixes _ (underscore) to global C symbols
- See: https://en.wikipedia.org/wiki/Name_mangling#C
- '''
- return self.compiler.symbols_have_underscore_prefix(self.environment)
-
- @noPosargs
- @permittedKwargs({})
- def unittest_args_method(self, args, kwargs):
- '''
- This function is deprecated and should not be used.
- It can be removed in a future version of Meson.
- '''
- if not hasattr(self.compiler, 'get_feature_args'):
- raise InterpreterException(f'This {self.compiler.get_display_language()} compiler has no feature arguments.')
- build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir())
- return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src)
-
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_member_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Has_member takes exactly two arguments.')
- check_stringlist(args)
- typename, membername = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_member must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_members(typename, [membername], prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has member', mlog.bold(membername, True), msg, hadtxt, cached)
- return had
-
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_members_method(self, args, kwargs):
- if len(args) < 2:
- raise InterpreterException('Has_members needs at least two arguments.')
- check_stringlist(args)
- typename, *membernames = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_members must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_members(typename, membernames, prefix,
- self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
- mlog.log('Checking whether type', mlog.bold(typename, True),
- 'has members', members, msg, hadtxt, cached)
- return had
-
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_function_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_function takes exactly one argument.')
- check_stringlist(args)
- funcname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_function must be a string.')
- extra_args = self.determine_args(kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_function(funcname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for function', mlog.bold(funcname, True), msg, hadtxt, cached)
- return had
-
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def has_type_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Has_type takes exactly one argument.')
- check_stringlist(args)
- typename = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_type must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- had, cached = self.compiler.has_type(typename, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if had:
- hadtxt = mlog.green('YES')
- else:
- hadtxt = mlog.red('NO')
- mlog.log('Checking for type', mlog.bold(typename, True), msg, hadtxt, cached)
- return had
-
- @FeatureNew('compiler.compute_int', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'low',
- 'high',
- 'guess',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compute_int_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Compute_int takes exactly one argument.')
- check_stringlist(args)
- expression = args[0]
- prefix = kwargs.get('prefix', '')
- low = kwargs.get('low', None)
- high = kwargs.get('high', None)
- guess = kwargs.get('guess', None)
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of compute_int must be a string.')
- if low is not None and not isinstance(low, int):
- raise InterpreterException('Low argument of compute_int must be an int.')
- if high is not None and not isinstance(high, int):
- raise InterpreterException('High argument of compute_int must be an int.')
- if guess is not None and not isinstance(guess, int):
- raise InterpreterException('Guess argument of compute_int must be an int.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- res = self.compiler.compute_int(expression, low, high, guess, prefix,
- self.environment, extra_args=extra_args,
- dependencies=deps)
- mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
- return res
-
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def sizeof_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Sizeof takes exactly one argument.')
- check_stringlist(args)
- element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of sizeof must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- esize = self.compiler.sizeof(element, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
- return esize
-
- @FeatureNew('compiler.get_define', '0.40.0')
- @permittedKwargs({
- 'prefix',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def get_define_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('get_define() takes exactly one argument.')
- check_stringlist(args)
- element = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of get_define() must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- value, cached = self.compiler.get_define(element, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached)
- return value
-
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def compiles_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('compiles method takes exactly one argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result, cached = self.compiler.compiles(code, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if len(testname) > 0:
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'compiles:', h, cached)
- return result
-
- @permittedKwargs({
- 'name',
- 'no_builtin_args',
- 'include_directories',
- 'args',
- 'dependencies',
- })
- def links_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('links method takes exactly one argument.')
- code = args[0]
- if isinstance(code, mesonlib.File):
- code = mesonlib.File.from_absolute_file(
- code.rel_to_builddir(self.environment.source_dir))
- elif not isinstance(code, str):
- raise InvalidArguments('Argument must be string or file.')
- testname = kwargs.get('name', '')
- if not isinstance(testname, str):
- raise InterpreterException('Testname argument must be a string.')
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs, endl=None)
- result, cached = self.compiler.links(code, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if len(testname) > 0:
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Checking if', mlog.bold(testname, True), msg, 'links:', h, cached)
- return result
-
- @FeatureNew('compiler.check_header', '0.47.0')
- @FeatureNewKwargs('compiler.check_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def check_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('check_header method takes exactly one argument.')
- check_stringlist(args)
- hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log('Check usable header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.check_header(hname, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if required and not haz:
- raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not usable')
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Check usable header', mlog.bold(hname, True), msg, h, cached)
- return haz
-
- @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('has_header method takes exactly one argument.')
- check_stringlist(args)
- hname = args[0]
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log('Has header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.has_header(hname, prefix, self.environment,
- extra_args=extra_args, dependencies=deps)
- cached = mlog.blue('(cached)') if cached else ''
- if required and not haz:
- raise InterpreterException(f'{self.compiler.get_display_language()} header {hname!r} not found')
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log('Has header', mlog.bold(hname, True), msg, h, cached)
- return haz
-
- @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required'])
- @permittedKwargs(header_permitted_kwargs)
- def has_header_symbol_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('has_header_symbol method takes exactly two arguments.')
- check_stringlist(args)
- hname, symbol = args
- prefix = kwargs.get('prefix', '')
- if not isinstance(prefix, str):
- raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
- if disabled:
- mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled')
- return False
- extra_args = functools.partial(self.determine_args, kwargs)
- deps, msg = self.determine_dependencies(kwargs)
- haz, cached = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
- extra_args=extra_args,
- dependencies=deps)
- if required and not haz:
- raise InterpreterException(f'{self.compiler.get_display_language()} symbol {symbol} not found in header {hname}')
- elif haz:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log(f'Header <{hname}> has symbol', mlog.bold(symbol, True), msg, h, cached)
- return haz
-
- def notfound_library(self, libname):
- lib = dependencies.ExternalLibrary(libname, None,
- self.environment,
- self.compiler.language,
- silent=True)
- return ExternalLibraryHolder(lib, self.subproject)
-
- @FeatureNewKwargs('compiler.find_library', '0.51.0', ['static'])
- @FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
- @FeatureNewKwargs('compiler.find_library', '0.49.0', ['disabler'])
- @disablerIfNotFound
- @permittedKwargs(find_library_permitted_kwargs)
- def find_library_method(self, args, kwargs):
- # TODO add dependencies support?
- if len(args) != 1:
- raise InterpreterException('find_library method takes one argument.')
- libname = args[0]
- if not isinstance(libname, str):
- raise InterpreterException('Library name not a string.')
-
- disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- if disabled:
- mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
- return self.notfound_library(libname)
-
- has_header_kwargs = {k[7:]: v for k, v in kwargs.items() if k.startswith('header_')}
- has_header_kwargs['required'] = required
- headers = mesonlib.stringlistify(kwargs.get('has_headers', []))
- for h in headers:
- if not self.has_header_method([h], has_header_kwargs):
- return self.notfound_library(libname)
-
- search_dirs = extract_search_dirs(kwargs)
-
- libtype = mesonlib.LibType.PREFER_SHARED
- if 'static' in kwargs:
- if not isinstance(kwargs['static'], bool):
- raise InterpreterException('static must be a boolean')
- libtype = mesonlib.LibType.STATIC if kwargs['static'] else mesonlib.LibType.SHARED
- linkargs = self.compiler.find_library(libname, self.environment, search_dirs, libtype)
- if required and not linkargs:
- if libtype == mesonlib.LibType.PREFER_SHARED:
- libtype = 'shared or static'
- else:
- libtype = libtype.name.lower()
- raise InterpreterException('{} {} library {!r} not found'
- .format(self.compiler.get_display_language(),
- libtype, libname))
- lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
- self.compiler.language)
- return ExternalLibraryHolder(lib, self.subproject)
-
- @permittedKwargs({})
- def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool:
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_argument takes exactly one argument.')
- return self.has_multi_arguments_method(args, kwargs)
-
- @permittedKwargs({})
- def has_multi_arguments_method(self, args: T.Sequence[str], kwargs: dict):
- args = mesonlib.stringlistify(args)
- result, cached = self.compiler.has_multi_arguments(args, self.environment)
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- cached = mlog.blue('(cached)') if cached else ''
- mlog.log(
- 'Compiler for {} supports arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h, cached)
- return result
-
- @FeatureNew('compiler.get_supported_arguments', '0.43.0')
- @permittedKwargs({})
- def get_supported_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- supported_args = []
- for arg in args:
- if self.has_argument_method(arg, kwargs):
- supported_args.append(arg)
- return supported_args
-
- @permittedKwargs({})
- def first_supported_argument_method(self, args: T.Sequence[str], kwargs: dict) -> T.List[str]:
- for arg in mesonlib.stringlistify(args):
- if self.has_argument_method(arg, kwargs):
- mlog.log('First supported argument:', mlog.bold(arg))
- return [arg]
- mlog.log('First supported argument:', mlog.red('None'))
- return []
-
- @FeatureNew('compiler.has_link_argument', '0.46.0')
- @permittedKwargs({})
- def has_link_argument_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_link_argument takes exactly one argument.')
- return self.has_multi_link_arguments_method(args, kwargs)
-
- @FeatureNew('compiler.has_multi_link_argument', '0.46.0')
- @permittedKwargs({})
- def has_multi_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- result, cached = self.compiler.has_multi_link_arguments(args, self.environment)
- cached = mlog.blue('(cached)') if cached else ''
- if result:
- h = mlog.green('YES')
- else:
- h = mlog.red('NO')
- mlog.log(
- 'Compiler for {} supports link arguments {}:'.format(
- self.compiler.get_display_language(), ' '.join(args)),
- h, cached)
- return result
-
- @FeatureNew('compiler.get_supported_link_arguments_method', '0.46.0')
- @permittedKwargs({})
- def get_supported_link_arguments_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- supported_args = []
- for arg in args:
- if self.has_link_argument_method(arg, kwargs):
- supported_args.append(arg)
- return supported_args
-
- @FeatureNew('compiler.first_supported_link_argument_method', '0.46.0')
- @permittedKwargs({})
- def first_supported_link_argument_method(self, args, kwargs):
- for i in mesonlib.stringlistify(args):
- if self.has_link_argument_method(i, kwargs):
- mlog.log('First supported link argument:', mlog.bold(i))
- return [i]
- mlog.log('First supported link argument:', mlog.red('None'))
- return []
-
- @FeatureNew('compiler.has_function_attribute', '0.48.0')
- @permittedKwargs({})
- def has_func_attribute_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- if len(args) != 1:
- raise InterpreterException('has_func_attribute takes exactly one argument.')
- result, cached = self.compiler.has_func_attribute(args[0], self.environment)
- cached = mlog.blue('(cached)') if cached else ''
- h = mlog.green('YES') if result else mlog.red('NO')
- mlog.log('Compiler for {} supports function attribute {}:'.format(self.compiler.get_display_language(), args[0]), h, cached)
- return result
-
- @FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
- @permittedKwargs({})
- def get_supported_function_attributes_method(self, args, kwargs):
- args = mesonlib.stringlistify(args)
- return [a for a in args if self.has_func_attribute_method(a, kwargs)]
-
- @FeatureNew('compiler.get_argument_syntax_method', '0.49.0')
- @noPosargs
- @noKwargs
- def get_argument_syntax_method(self, args, kwargs):
- return self.compiler.get_argument_syntax()
-
-
-class ModuleObjectHolder(InterpreterObject, ObjectHolder['ModuleObject']):
- def __init__(self, modobj: 'ModuleObject', interpreter: 'Interpreter'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, modobj)
- self.interpreter = interpreter
-
- def method_call(self, method_name, args, kwargs):
- modobj = self.held_object
- method = modobj.methods.get(method_name)
- if not method and not modobj.methods:
- # FIXME: Port all modules to use the methods dict.
- method = getattr(modobj, method_name, None)
- if method_name.startswith('_'):
- raise InvalidArguments(f'Method {method_name!r} is private.')
- if not method:
- raise InvalidCode('Unknown method "%s" in object.' % method_name)
- if not getattr(method, 'no-args-flattening', False):
- args = flatten(args)
- state = ModuleState(self.interpreter)
- # Many modules do for example self.interpreter.find_program_impl(),
- # so we have to ensure they use the current interpreter and not the one
- # that first imported that module, otherwise it will use outdated
- # overrides.
- modobj.interpreter = self.interpreter
- if method_name in modobj.snippets:
- ret = method(self.interpreter, state, args, kwargs)
- else:
- # This is not 100% reliable but we can't use hash()
- # because the Build object contains dicts and lists.
- num_targets = len(self.interpreter.build.targets)
- ret = method(state, args, kwargs)
- if num_targets != len(self.interpreter.build.targets):
- raise InterpreterException('Extension module altered internal state illegally.')
- if isinstance(ret, ModuleReturnValue):
- self.interpreter.process_new_values(ret.new_objects)
- ret = ret.return_value
- return self.interpreter.holderify(ret)
-
-
class Summary:
def __init__(self, project_name, project_version):
self.project_name = project_name
@@ -1871,357 +145,6 @@ class Summary:
line_len += v_len
mlog.log(*line, sep=list_sep)
-class MesonMain(InterpreterObject):
- def __init__(self, build: 'build.Build', interpreter: 'Interpreter'):
- InterpreterObject.__init__(self)
- self.build = build
- self.interpreter = interpreter
- self.methods.update({'get_compiler': self.get_compiler_method,
- 'is_cross_build': self.is_cross_build_method,
- 'has_exe_wrapper': self.has_exe_wrapper_method,
- 'can_run_host_binaries': self.can_run_host_binaries_method,
- 'is_unity': self.is_unity_method,
- 'is_subproject': self.is_subproject_method,
- 'current_source_dir': self.current_source_dir_method,
- 'current_build_dir': self.current_build_dir_method,
- 'source_root': self.source_root_method,
- 'build_root': self.build_root_method,
- 'project_source_root': self.project_source_root_method,
- 'project_build_root': self.project_build_root_method,
- 'add_install_script': self.add_install_script_method,
- 'add_postconf_script': self.add_postconf_script_method,
- 'add_dist_script': self.add_dist_script_method,
- 'install_dependency_manifest': self.install_dependency_manifest_method,
- 'override_dependency': self.override_dependency_method,
- 'override_find_program': self.override_find_program_method,
- 'project_version': self.project_version_method,
- 'project_license': self.project_license_method,
- 'version': self.version_method,
- 'project_name': self.project_name_method,
- 'get_cross_property': self.get_cross_property_method,
- 'get_external_property': self.get_external_property_method,
- 'has_external_property': self.has_external_property_method,
- 'backend': self.backend_method,
- 'add_devenv': self.add_devenv_method,
- })
-
- def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args):
-
- if isinstance(prog, (ExecutableHolder, ExternalProgramHolder)):
- return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args)
- found = self.interpreter.func_find_program({}, prog, {}).held_object
- es = self.interpreter.backend.get_executable_serialisation([found] + args)
- es.subproject = self.interpreter.subproject
- return es
-
- def _process_script_args(
- self, name: str, args: T.List[T.Union[
- str, mesonlib.File, CustomTargetHolder,
- CustomTargetIndexHolder,
- ExternalProgramHolder, ExecutableHolder,
- ]], allow_built: bool = False) -> T.List[str]:
- script_args = [] # T.List[str]
- new = False
- for a in args:
- a = unholder(a)
- if isinstance(a, str):
- script_args.append(a)
- elif isinstance(a, mesonlib.File):
- new = True
- script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir))
- elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)):
- if not allow_built:
- raise InterpreterException(f'Arguments to {name} cannot be built')
- new = True
- script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()])
-
- # This feels really hacky, but I'm not sure how else to fix
- # this without completely rewriting install script handling.
- # This is complicated by the fact that the install target
- # depends on all.
- if isinstance(a, build.CustomTargetIndex):
- a.target.build_by_default = True
- else:
- a.build_by_default = True
- elif isinstance(a, ExternalProgram):
- script_args.extend(a.command)
- new = True
- else:
- raise InterpreterException(
- 'Arguments to {} must be strings, Files, or CustomTargets, '
- 'Indexes of CustomTargets'.format(name))
- if new:
- FeatureNew.single_use(
- 'Calling "{}" with File, CustomTaget, Index of CustomTarget, '
- 'Executable, or ExternalProgram'.format(name),
- '0.55.0', self.interpreter.subproject)
- return script_args
-
- @FeatureNewKwargs('add_install_script', '0.57.0', ['skip_if_destdir'])
- @permittedKwargs({'skip_if_destdir'})
- def add_install_script_method(self, args: 'T.Tuple[T.Union[str, mesonlib.File, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder], ...]', kwargs):
- if len(args) < 1:
- raise InterpreterException('add_install_script takes one or more arguments')
- if isinstance(args[0], mesonlib.File):
- FeatureNew.single_use('Passing file object to script parameter of add_install_script',
- '0.57.0', self.interpreter.subproject)
- skip_if_destdir = kwargs.get('skip_if_destdir', False)
- if not isinstance(skip_if_destdir, bool):
- raise InterpreterException('skip_if_destdir keyword argument must be boolean')
- script_args = self._process_script_args('add_install_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- script.skip_if_destdir = skip_if_destdir
- self.build.install_scripts.append(script)
-
- @permittedKwargs(set())
- def add_postconf_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_postconf_script takes one or more arguments')
- if isinstance(args[0], mesonlib.File):
- FeatureNew.single_use('Passing file object to script parameter of add_postconf_script',
- '0.57.0', self.interpreter.subproject)
- script_args = self._process_script_args('add_postconf_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- self.build.postconf_scripts.append(script)
-
- @permittedKwargs(set())
- def add_dist_script_method(self, args, kwargs):
- if len(args) < 1:
- raise InterpreterException('add_dist_script takes one or more arguments')
- if len(args) > 1:
- FeatureNew.single_use('Calling "add_dist_script" with multiple arguments',
- '0.49.0', self.interpreter.subproject)
- if isinstance(args[0], mesonlib.File):
- FeatureNew.single_use('Passing file object to script parameter of add_dist_script',
- '0.57.0', self.interpreter.subproject)
- if self.interpreter.subproject != '':
- FeatureNew.single_use('Calling "add_dist_script" in a subproject',
- '0.58.0', self.interpreter.subproject)
- script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True)
- script = self._find_source_script(args[0], script_args)
- self.build.dist_scripts.append(script)
-
- @noPosargs
- @permittedKwargs({})
- def current_source_dir_method(self, args, kwargs):
- src = self.interpreter.environment.source_dir
- sub = self.interpreter.subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
-
- @noPosargs
- @permittedKwargs({})
- def current_build_dir_method(self, args, kwargs):
- src = self.interpreter.environment.build_dir
- sub = self.interpreter.subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
-
- @noPosargs
- @permittedKwargs({})
- def backend_method(self, args, kwargs):
- return self.interpreter.backend.name
-
- @noPosargs
- @permittedKwargs({})
- @FeatureDeprecated('meson.source_root', '0.56.0', 'use meson.current_source_dir instead.')
- def source_root_method(self, args, kwargs):
- return self.interpreter.environment.source_dir
-
- @noPosargs
- @permittedKwargs({})
- @FeatureDeprecated('meson.build_root', '0.56.0', 'use meson.current_build_dir instead.')
- def build_root_method(self, args, kwargs):
- return self.interpreter.environment.build_dir
-
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('meson.project_source_root', '0.56.0')
- def project_source_root_method(self, args, kwargs):
- src = self.interpreter.environment.source_dir
- sub = self.interpreter.root_subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
-
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('meson.project_build_root', '0.56.0')
- def project_build_root_method(self, args, kwargs):
- src = self.interpreter.environment.build_dir
- sub = self.interpreter.root_subdir
- if sub == '':
- return src
- return os.path.join(src, sub)
-
- @noPosargs
- @permittedKwargs({})
- @FeatureDeprecated('meson.has_exe_wrapper', '0.55.0', 'use meson.can_run_host_binaries instead.')
- def has_exe_wrapper_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool:
- return self.can_run_host_binaries_impl(args, kwargs)
-
- @noPosargs
- @permittedKwargs({})
- @FeatureNew('meson.can_run_host_binaries', '0.55.0')
- def can_run_host_binaries_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool:
- return self.can_run_host_binaries_impl(args, kwargs)
-
- def can_run_host_binaries_impl(self, args, kwargs):
- if (self.is_cross_build_method(None, None) and
- self.build.environment.need_exe_wrapper()):
- if self.build.environment.exe_wrapper is None:
- return False
- # We return True when exe_wrap is defined, when it's not needed, and
- # when we're compiling natively. The last two are semantically confusing.
- # Need to revisit this.
- return True
-
- @noPosargs
- @permittedKwargs({})
- def is_cross_build_method(self, args, kwargs):
- return self.build.environment.is_cross_build()
-
- @permittedKwargs({'native'})
- def get_compiler_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('get_compiler_method must have one and only one argument.')
- cname = args[0]
- for_machine = Interpreter.machine_from_native_kwarg(kwargs)
- clist = self.interpreter.coredata.compilers[for_machine]
- if cname in clist:
- return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject)
- raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.')
-
- @noPosargs
- @permittedKwargs({})
- def is_unity_method(self, args, kwargs):
- optval = self.interpreter.environment.coredata.get_option(OptionKey('unity'))
- if optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()):
- return True
- return False
-
- @noPosargs
- @permittedKwargs({})
- def is_subproject_method(self, args, kwargs):
- return self.interpreter.is_subproject()
-
- @permittedKwargs({})
- def install_dependency_manifest_method(self, args, kwargs):
- if len(args) != 1:
- raise InterpreterException('Must specify manifest install file name')
- if not isinstance(args[0], str):
- raise InterpreterException('Argument must be a string.')
- self.build.dep_manifest_name = args[0]
-
- @FeatureNew('meson.override_find_program', '0.46.0')
- @permittedKwargs({})
- def override_find_program_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Override needs two arguments')
- name, exe = args
- if not isinstance(name, str):
- raise InterpreterException('First argument must be a string')
- exe = unholder(exe)
- if isinstance(exe, mesonlib.File):
- abspath = exe.absolute_path(self.interpreter.environment.source_dir,
- self.interpreter.environment.build_dir)
- if not os.path.exists(abspath):
- raise InterpreterException('Tried to override %s with a file that does not exist.' % name)
- exe = OverrideProgram(name, abspath)
- if not isinstance(exe, (ExternalProgram, build.Executable)):
- raise InterpreterException('Second argument must be an external program or executable.')
- self.interpreter.add_find_program_override(name, exe)
-
- @FeatureNew('meson.override_dependency', '0.54.0')
- @permittedKwargs({'native'})
- def override_dependency_method(self, args, kwargs):
- if len(args) != 2:
- raise InterpreterException('Override needs two arguments')
- name = args[0]
- dep = args[1]
- if not isinstance(name, str) or not name:
- raise InterpreterException('First argument must be a string and cannot be empty')
- dep = unholder(dep)
- if not isinstance(dep, dependencies.Dependency):
- raise InterpreterException('Second argument must be a dependency object')
- identifier = dependencies.get_dep_identifier(name, kwargs)
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
- override = self.build.dependency_overrides[for_machine].get(identifier)
- if override:
- m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}'
- location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
- raise InterpreterException(m.format(name, location))
- self.build.dependency_overrides[for_machine][identifier] = \
- build.DependencyOverride(dep, self.interpreter.current_node)
-
- @noPosargs
- @permittedKwargs({})
- def project_version_method(self, args, kwargs):
- return self.build.dep_manifest[self.interpreter.active_projectname]['version']
-
- @FeatureNew('meson.project_license()', '0.45.0')
- @noPosargs
- @permittedKwargs({})
- def project_license_method(self, args, kwargs):
- return self.build.dep_manifest[self.interpreter.active_projectname]['license']
-
- @noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
- return MesonVersionString(coredata.version)
-
- @noPosargs
- @permittedKwargs({})
- def project_name_method(self, args, kwargs):
- return self.interpreter.active_projectname
-
- def __get_external_property_impl(self, propname: str, fallback: T.Optional[object], machine: MachineChoice) -> object:
- """Shared implementation for get_cross_property and get_external_property."""
- try:
- return self.interpreter.environment.properties[machine][propname]
- except KeyError:
- if fallback is not None:
- return fallback
- raise InterpreterException(f'Unknown property for {machine.get_lower_case_name()} machine: {propname}')
-
- @noArgsFlattening
- @permittedKwargs({})
- @FeatureDeprecated('meson.get_cross_property', '0.58.0', 'Use meson.get_external_property() instead')
- @typed_pos_args('meson.get_cross_property', str, optargs=[object])
- def get_cross_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: T.Dict[str, T.Any]) -> object:
- propname, fallback = args
- return self.__get_external_property_impl(propname, fallback, MachineChoice.HOST)
-
- @noArgsFlattening
- @permittedKwargs({'native'})
- @FeatureNew('meson.get_external_property', '0.54.0')
- @typed_pos_args('meson.get_external_property', str, optargs=[object])
- def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: T.Dict[str, T.Any]) -> object:
- propname, fallback = args
- machine = self.interpreter.machine_from_native_kwarg(kwargs)
- return self.__get_external_property_impl(propname, fallback, machine)
-
-
- @permittedKwargs({'native'})
- @FeatureNew('meson.has_external_property', '0.58.0')
- @typed_pos_args('meson.has_external_property', str)
- def has_external_property_method(self, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
- prop_name = args[0]
- for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
- return prop_name in self.interpreter.environment.properties[for_machine]
-
- @FeatureNew('add_devenv', '0.58.0')
- @noKwargs
- @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder))
- def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None:
- env = args[0]
- if isinstance(env, (str, list, dict)):
- env = EnvironmentVariablesHolder(env)
- self.build.devenv.append(env.held_object)
-
-
known_library_kwargs = (
build.known_shlib_kwargs |
build.known_stlib_kwargs
@@ -2721,7 +644,7 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('Assert value not bool.')
if not value:
if message is None:
- from .ast import AstPrinter
+ from ..ast import AstPrinter
printer = AstPrinter()
node.args.arguments[0].accept(printer)
message = printer.result
@@ -2953,7 +876,7 @@ external dependencies (including libraries) must go to "dependencies".''')
new_build = self.build.copy()
prefix = self.coredata.options[OptionKey('prefix')].value
- from .modules.cmake import CMakeSubprojectOptions
+ from ..modules.cmake import CMakeSubprojectOptions
options = kwargs.get('options', CMakeSubprojectOptions())
if not isinstance(options, CMakeSubprojectOptions):
raise InterpreterException('"options" kwarg must be CMakeSubprojectOptions'
@@ -2973,7 +896,7 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.log('Processing generated meson AST')
# Debug print the generated meson file
- from .ast import AstIndentationGenerator, AstPrinter
+ from ..ast import AstIndentationGenerator, AstPrinter
printer = AstPrinter()
ast.accept(AstIndentationGenerator())
ast.accept(printer)
@@ -3061,7 +984,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if self.backend is not None:
return
backend = self.coredata.get_option(OptionKey('backend'))
- from .backend import backends
+ from ..backend import backends
self.backend = backends.get_backend_from_name(backend, self.build, self)
if self.backend is None:
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
new file mode 100644
index 0000000..7d43752
--- /dev/null
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -0,0 +1,999 @@
+import os
+import shlex
+import subprocess
+import re
+
+from pathlib import Path, PurePath
+
+from .. import mesonlib
+from .. import coredata
+from .. import build
+from .. import mlog
+
+from ..modules import ModuleReturnValue, ModuleObject, ModuleState
+from ..backend.backends import TestProtocol
+from ..interpreterbase import (InterpreterObject, ObjectHolder, MutableInterpreterObject,
+ FeatureNewKwargs, FeatureNew, FeatureDeprecated,
+ typed_pos_args, stringArgs, permittedKwargs,
+ noArgsFlattening, noPosargs, TYPE_var, TYPE_nkwargs,
+ flatten, InterpreterException, InvalidArguments, InvalidCode)
+from ..dependencies import Dependency, ExternalLibrary, InternalDependency
+from ..programs import ExternalProgram
+from ..mesonlib import FileMode, OptionKey, listify, Popen_safe
+
+import typing as T
+
+def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
+ val = kwargs.get('required', default)
+ disabled = False
+ required = False
+ feature = None
+ if isinstance(val, FeatureOptionHolder):
+ if not feature_check:
+ feature_check = FeatureNew('User option "feature"', '0.47.0')
+ feature_check.use(subproject)
+ option = val.held_object
+ feature = val.name
+ if option.is_disabled():
+ disabled = True
+ elif option.is_enabled():
+ required = True
+ elif isinstance(val, bool):
+ required = val
+ else:
+ raise InterpreterException('required keyword argument must be boolean or a feature option')
+
+ # Keep boolean value in kwargs to simplify other places where this kwarg is
+ # checked.
+ kwargs['required'] = required
+
+ return disabled, required, feature
+
+def extract_search_dirs(kwargs):
+ search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
+ search_dirs = [Path(d).expanduser() for d in search_dirs]
+ for d in search_dirs:
+ if mesonlib.is_windows() and d.root.startswith('\\'):
+ # a Unix-path starting with `/` that is not absolute on Windows.
+ # discard without failing for end-user ease of cross-platform directory arrays
+ continue
+ if not d.is_absolute():
+ raise InvalidCode(f'Search directory {d} is not an absolute path.')
+ return list(map(str, search_dirs))
+
+class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOption]):
+ def __init__(self, env: 'Environment', name: str, option: coredata.UserFeatureOption):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, option)
+ if option.is_auto():
+ # TODO: we need to case here because options is not a TypedDict
+ self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')])
+ self.name = name
+ self.methods.update({'enabled': self.enabled_method,
+ 'disabled': self.disabled_method,
+ 'auto': self.auto_method,
+ })
+
+ @noPosargs
+ @permittedKwargs({})
+ def enabled_method(self, args, kwargs):
+ return self.held_object.is_enabled()
+
+ @noPosargs
+ @permittedKwargs({})
+ def disabled_method(self, args, kwargs):
+ return self.held_object.is_disabled()
+
+ @noPosargs
+ @permittedKwargs({})
+ def auto_method(self, args, kwargs):
+ return self.held_object.is_auto()
+
+class RunProcess(InterpreterObject):
+
+ def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
+ super().__init__()
+ if not isinstance(cmd, ExternalProgram):
+ raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
+ self.capture = capture
+ pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
+ self.returncode = pc.returncode
+ self.methods.update({'returncode': self.returncode_method,
+ 'stdout': self.stdout_method,
+ 'stderr': self.stderr_method,
+ })
+
+ def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
+ command_array = cmd.get_command() + args
+ menv = {'MESON_SOURCE_ROOT': source_dir,
+ 'MESON_BUILD_ROOT': build_dir,
+ 'MESON_SUBDIR': subdir,
+ 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
+ }
+ if in_builddir:
+ cwd = os.path.join(build_dir, subdir)
+ else:
+ cwd = os.path.join(source_dir, subdir)
+ child_env = os.environ.copy()
+ child_env.update(menv)
+ child_env = env.get_env(child_env)
+ stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
+ mlog.debug('Running command:', ' '.join(command_array))
+ try:
+ p, o, e = Popen_safe(command_array, stdout=stdout, env=child_env, cwd=cwd)
+ if self.capture:
+ mlog.debug('--- stdout ---')
+ mlog.debug(o)
+ else:
+ o = ''
+ mlog.debug('--- stdout disabled ---')
+ mlog.debug('--- stderr ---')
+ mlog.debug(e)
+ mlog.debug('')
+
+ if check and p.returncode != 0:
+ raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
+
+ return p, o, e
+ except FileNotFoundError:
+ raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
+
+ @noPosargs
+ @permittedKwargs({})
+ def returncode_method(self, args, kwargs):
+ return self.returncode
+
+ @noPosargs
+ @permittedKwargs({})
+ def stdout_method(self, args, kwargs):
+ return self.stdout
+
+ @noPosargs
+ @permittedKwargs({})
+ def stderr_method(self, args, kwargs):
+ return self.stderr
+
+class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]):
+ def __init__(self, initial_values=None, subproject: str = ''):
+ MutableInterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, build.EnvironmentVariables(), subproject)
+ self.methods.update({'set': self.set_method,
+ 'append': self.append_method,
+ 'prepend': self.prepend_method,
+ })
+ if isinstance(initial_values, dict):
+ for k, v in initial_values.items():
+ self.set_method([k, v], {})
+ elif initial_values is not None:
+ for e in mesonlib.stringlistify(initial_values):
+ if '=' not in e:
+ raise InterpreterException('Env var definition must be of type key=val.')
+ (k, val) = e.split('=', 1)
+ k = k.strip()
+ val = val.strip()
+ if ' ' in k:
+ raise InterpreterException('Env var key must not have spaces in it.')
+ self.set_method([k, val], {})
+
+ def __repr__(self) -> str:
+ repr_str = "<{0}: {1}>"
+ return repr_str.format(self.__class__.__name__, self.held_object.envvars)
+
+ def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str:
+ separator = kwargs.get('separator', os.pathsep)
+ if not isinstance(separator, str):
+ raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
+ " argument needs to be a string.")
+ return separator
+
+ def warn_if_has_name(self, name: str) -> None:
+ # Multiple append/prepend operations was not supported until 0.58.0.
+ if self.held_object.has_name(name):
+ m = f'Overriding previous value of environment variable {name!r} with a new one'
+ FeatureNew('0.58.0', m).use(self.subproject)
+
+ @stringArgs
+ @permittedKwargs({'separator'})
+ @typed_pos_args('environment.set', str, varargs=str, min_varargs=1)
+ def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
+ name, values = args
+ separator = self.unpack_separator(kwargs)
+ self.held_object.set(name, values, separator)
+
+ @stringArgs
+ @permittedKwargs({'separator'})
+ @typed_pos_args('environment.append', str, varargs=str, min_varargs=1)
+ def append_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
+ name, values = args
+ separator = self.unpack_separator(kwargs)
+ self.warn_if_has_name(name)
+ self.held_object.append(name, values, separator)
+
+ @stringArgs
+ @permittedKwargs({'separator'})
+ @typed_pos_args('environment.prepend', str, varargs=str, min_varargs=1)
+ def prepend_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
+ name, values = args
+ separator = self.unpack_separator(kwargs)
+ self.warn_if_has_name(name)
+ self.held_object.prepend(name, values, separator)
+
+
+class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.ConfigurationData]):
+ def __init__(self, pv, initial_values=None):
+ MutableInterpreterObject.__init__(self)
+ self.used = False # These objects become immutable after use in configure_file.
+ ObjectHolder.__init__(self, build.ConfigurationData(), pv)
+ self.methods.update({'set': self.set_method,
+ 'set10': self.set10_method,
+ 'set_quoted': self.set_quoted_method,
+ 'has': self.has_method,
+ 'get': self.get_method,
+ 'keys': self.keys_method,
+ 'get_unquoted': self.get_unquoted_method,
+ 'merge_from': self.merge_from_method,
+ })
+ if isinstance(initial_values, dict):
+ for k, v in initial_values.items():
+ self.set_method([k, v], {})
+ elif initial_values:
+ raise AssertionError('Unsupported ConfigurationDataHolder initial_values')
+
+ def is_used(self):
+ return self.used
+
+ def mark_used(self):
+ self.used = True
+
+ def validate_args(self, args, kwargs):
+ if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
+ mlog.deprecation('Passing a list as the single argument to '
+ 'configuration_data.set is deprecated. This will '
+ 'become a hard error in the future.',
+ location=self.current_node)
+ args = args[0]
+
+ if len(args) != 2:
+ raise InterpreterException("Configuration set requires 2 arguments.")
+ if self.used:
+ raise InterpreterException("Can not set values on configuration object that has been used.")
+ name, val = args
+ if not isinstance(val, (int, str)):
+ msg = 'Setting a configuration data value to {!r} is invalid, ' \
+ 'and will fail at configure_file(). If you are using it ' \
+ 'just to store some values, please use a dict instead.'
+ mlog.deprecation(msg.format(val), location=self.current_node)
+ desc = kwargs.get('description', None)
+ if not isinstance(name, str):
+ raise InterpreterException("First argument to set must be a string.")
+ if desc is not None and not isinstance(desc, str):
+ raise InterpreterException('Description must be a string.')
+
+ return name, val, desc
+
+ @noArgsFlattening
+ def set_method(self, args, kwargs):
+ (name, val, desc) = self.validate_args(args, kwargs)
+ self.held_object.values[name] = (val, desc)
+
+ def set_quoted_method(self, args, kwargs):
+ (name, val, desc) = self.validate_args(args, kwargs)
+ if not isinstance(val, str):
+ raise InterpreterException("Second argument to set_quoted must be a string.")
+ escaped_val = '\\"'.join(val.split('"'))
+ self.held_object.values[name] = ('"' + escaped_val + '"', desc)
+
+ def set10_method(self, args, kwargs):
+ (name, val, desc) = self.validate_args(args, kwargs)
+ if val:
+ self.held_object.values[name] = (1, desc)
+ else:
+ self.held_object.values[name] = (0, desc)
+
+ def has_method(self, args, kwargs):
+ return args[0] in self.held_object.values
+
+ @FeatureNew('configuration_data.get()', '0.38.0')
+ @noArgsFlattening
+ def get_method(self, args, kwargs):
+ if len(args) < 1 or len(args) > 2:
+ raise InterpreterException('Get method takes one or two arguments.')
+ name = args[0]
+ if name in self.held_object:
+ return self.held_object.get(name)[0]
+ if len(args) > 1:
+ return args[1]
+ raise InterpreterException('Entry %s not in configuration data.' % name)
+
+ @FeatureNew('configuration_data.get_unquoted()', '0.44.0')
+ def get_unquoted_method(self, args, kwargs):
+ if len(args) < 1 or len(args) > 2:
+ raise InterpreterException('Get method takes one or two arguments.')
+ name = args[0]
+ if name in self.held_object:
+ val = self.held_object.get(name)[0]
+ elif len(args) > 1:
+ val = args[1]
+ else:
+ raise InterpreterException('Entry %s not in configuration data.' % name)
+ if val[0] == '"' and val[-1] == '"':
+ return val[1:-1]
+ return val
+
+ def get(self, name):
+ return self.held_object.values[name] # (val, desc)
+
+ @FeatureNew('configuration_data.keys()', '0.57.0')
+ @noPosargs
+ def keys_method(self, args, kwargs):
+ return sorted(self.keys())
+
+ def keys(self):
+ return self.held_object.values.keys()
+
+ def merge_from_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Merge_from takes one positional argument.')
+ from_object = args[0]
+ if not isinstance(from_object, ConfigurationDataHolder):
+ raise InterpreterException('Merge_from argument must be a configuration data object.')
+ from_object = from_object.held_object
+ for k, v in from_object.values.items():
+ self.held_object.values[k] = v
+
+permitted_partial_dependency_kwargs = {
+ 'compile_args', 'link_args', 'links', 'includes', 'sources'
+}
+
+class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
+ def __init__(self, dep: Dependency, pv: str):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, dep, pv)
+ self.methods.update({'found': self.found_method,
+ 'type_name': self.type_name_method,
+ 'version': self.version_method,
+ 'name': self.name_method,
+ 'get_pkgconfig_variable': self.pkgconfig_method,
+ 'get_configtool_variable': self.configtool_method,
+ 'get_variable': self.variable_method,
+ 'partial_dependency': self.partial_dependency_method,
+ 'include_type': self.include_type_method,
+ 'as_system': self.as_system_method,
+ 'as_link_whole': self.as_link_whole_method,
+ })
+
+ def found(self):
+ return self.found_method([], {})
+
+ @noPosargs
+ @permittedKwargs({})
+ def type_name_method(self, args, kwargs):
+ return self.held_object.type_name
+
+ @noPosargs
+ @permittedKwargs({})
+ def found_method(self, args, kwargs):
+ if self.held_object.type_name == 'internal':
+ return True
+ return self.held_object.found()
+
+ @noPosargs
+ @permittedKwargs({})
+ def version_method(self, args, kwargs):
+ return self.held_object.get_version()
+
+ @noPosargs
+ @permittedKwargs({})
+ def name_method(self, args, kwargs):
+ return self.held_object.get_name()
+
+ @FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0',
+ 'use Dependency.get_variable(pkgconfig : ...) instead')
+ @permittedKwargs({'define_variable', 'default'})
+ def pkgconfig_method(self, args, kwargs):
+ args = listify(args)
+ if len(args) != 1:
+ raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Variable name must be a string.')
+ return self.held_object.get_pkgconfig_variable(varname, kwargs)
+
+ @FeatureNew('dep.get_configtool_variable', '0.44.0')
+ @FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0',
+ 'use Dependency.get_variable(configtool : ...) instead')
+ @permittedKwargs({})
+ def configtool_method(self, args, kwargs):
+ args = listify(args)
+ if len(args) != 1:
+ raise InterpreterException('get_configtool_variable takes exactly one argument.')
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Variable name must be a string.')
+ return self.held_object.get_configtool_variable(varname)
+
+ @FeatureNew('dep.partial_dependency', '0.46.0')
+ @noPosargs
+ @permittedKwargs(permitted_partial_dependency_kwargs)
+ def partial_dependency_method(self, args, kwargs):
+ pdep = self.held_object.get_partial_dependency(**kwargs)
+ return DependencyHolder(pdep, self.subproject)
+
+ @FeatureNew('dep.get_variable', '0.51.0')
+ @typed_pos_args('dep.get_variable', optargs=[str])
+ @permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
+ @FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal'])
+ def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> str:
+ default_varname = args[0]
+ if default_varname is not None:
+ FeatureNew('0.58.0', 'Positional argument to dep.get_variable()').use(self.subproject)
+ for k in ['cmake', 'pkgconfig', 'configtool', 'internal']:
+ kwargs.setdefault(k, default_varname)
+ return self.held_object.get_variable(**kwargs)
+
+ @FeatureNew('dep.include_type', '0.52.0')
+ @noPosargs
+ @permittedKwargs({})
+ def include_type_method(self, args, kwargs):
+ return self.held_object.get_include_type()
+
+ @FeatureNew('dep.as_system', '0.52.0')
+ @permittedKwargs({})
+ def as_system_method(self, args, kwargs):
+ args = listify(args)
+ new_is_system = 'system'
+ if len(args) > 1:
+ raise InterpreterException('as_system takes only one optional value')
+ if len(args) == 1:
+ new_is_system = args[0]
+ new_dep = self.held_object.generate_system_dependency(new_is_system)
+ return DependencyHolder(new_dep, self.subproject)
+
+ @FeatureNew('dep.as_link_whole', '0.56.0')
+ @permittedKwargs({})
+ @noPosargs
+ def as_link_whole_method(self, args, kwargs):
+ if not isinstance(self.held_object, InternalDependency):
+ raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
+ new_dep = self.held_object.generate_link_whole_dependency()
+ return DependencyHolder(new_dep, self.subproject)
+
+class ExternalProgramHolder(InterpreterObject, ObjectHolder[ExternalProgram]):
+ def __init__(self, ep: ExternalProgram, subproject: str, backend=None):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, ep)
+ self.subproject = subproject
+ self.backend = backend
+ self.methods.update({'found': self.found_method,
+ 'path': self.path_method,
+ 'full_path': self.full_path_method})
+ self.cached_version = None
+
+ @noPosargs
+ @permittedKwargs({})
+ def found_method(self, args, kwargs):
+ return self.found()
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureDeprecated('ExternalProgram.path', '0.55.0',
+ 'use ExternalProgram.full_path() instead')
+ def path_method(self, args, kwargs):
+ return self._full_path()
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureNew('ExternalProgram.full_path', '0.55.0')
+ def full_path_method(self, args, kwargs):
+ return self._full_path()
+
+ def _full_path(self):
+ exe = self.held_object
+ if isinstance(exe, build.Executable):
+ return self.backend.get_target_filename_abs(exe)
+ return exe.get_path()
+
+ def found(self):
+ return isinstance(self.held_object, build.Executable) or self.held_object.found()
+
+ 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()
+
+ def get_version(self, interpreter):
+ if isinstance(self.held_object, build.Executable):
+ return self.held_object.project_version
+ if not self.cached_version:
+ raw_cmd = self.get_command() + ['--version']
+ cmd = [self, '--version']
+ res = interpreter.run_command_impl(interpreter.current_node, cmd, {}, True)
+ if res.returncode != 0:
+ m = 'Running {!r} failed'
+ raise InterpreterException(m.format(raw_cmd))
+ output = res.stdout.strip()
+ if not output:
+ output = res.stderr.strip()
+ match = re.search(r'([0-9][0-9\.]+)', output)
+ if not match:
+ m = 'Could not find a version number in output of {!r}'
+ raise InterpreterException(m.format(raw_cmd))
+ self.cached_version = match.group(1)
+ return self.cached_version
+
+class ExternalLibraryHolder(InterpreterObject, ObjectHolder[ExternalLibrary]):
+ def __init__(self, el: ExternalLibrary, pv: str):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, el, pv)
+ self.methods.update({'found': self.found_method,
+ 'type_name': self.type_name_method,
+ 'partial_dependency': self.partial_dependency_method,
+ })
+
+ def found(self):
+ return self.held_object.found()
+
+ @noPosargs
+ @permittedKwargs({})
+ def type_name_method(self, args, kwargs):
+ return self.held_object.type_name
+
+ @noPosargs
+ @permittedKwargs({})
+ def found_method(self, args, kwargs):
+ return self.found()
+
+ def get_name(self):
+ return self.held_object.name
+
+ def get_compile_args(self):
+ return self.held_object.get_compile_args()
+
+ def get_link_args(self):
+ return self.held_object.get_link_args()
+
+ def get_exe_args(self):
+ return self.held_object.get_exe_args()
+
+ @FeatureNew('dep.partial_dependency', '0.46.0')
+ @noPosargs
+ @permittedKwargs(permitted_partial_dependency_kwargs)
+ def partial_dependency_method(self, args, kwargs):
+ pdep = self.held_object.get_partial_dependency(**kwargs)
+ return DependencyHolder(pdep, self.subproject)
+
+class GeneratorHolder(InterpreterObject, ObjectHolder[build.Generator]):
+ @FeatureNewKwargs('generator', '0.43.0', ['capture'])
+ def __init__(self, interp, args, kwargs):
+ self.interpreter = interp
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, build.Generator(args, kwargs), interp.subproject)
+ self.methods.update({'process': self.process_method})
+
+ @FeatureNewKwargs('generator.process', '0.45.0', ['preserve_path_from'])
+ @permittedKwargs({'extra_args', 'preserve_path_from'})
+ def process_method(self, args, kwargs):
+ extras = mesonlib.stringlistify(kwargs.get('extra_args', []))
+ if 'preserve_path_from' in kwargs:
+ preserve_path_from = kwargs['preserve_path_from']
+ if not isinstance(preserve_path_from, str):
+ raise InvalidArguments('Preserve_path_from must be a string.')
+ preserve_path_from = os.path.normpath(preserve_path_from)
+ if not os.path.isabs(preserve_path_from):
+ # This is a bit of a hack. Fix properly before merging.
+ raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
+ else:
+ preserve_path_from = None
+ gl = self.held_object.process_files('Generator', args, self.interpreter,
+ preserve_path_from, extra_args=extras)
+ return GeneratedListHolder(gl)
+
+
+class GeneratedListHolder(InterpreterObject, ObjectHolder[build.GeneratedList]):
+ def __init__(self, arg1, extra_args=None):
+ InterpreterObject.__init__(self)
+ if isinstance(arg1, GeneratorHolder):
+ ObjectHolder.__init__(self, build.GeneratedList(arg1.held_object, extra_args if extra_args is not None else []))
+ else:
+ ObjectHolder.__init__(self, arg1)
+
+ def __repr__(self):
+ r = '<{}: {!r}>'
+ return r.format(self.__class__.__name__, self.held_object.get_outputs())
+
+ def add_file(self, a):
+ self.held_object.add_file(a)
+
+# A machine that's statically known from the cross file
+class MachineHolder(InterpreterObject, ObjectHolder['MachineInfo']):
+ def __init__(self, machine_info: 'MachineInfo'):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, machine_info)
+ self.methods.update({'system': self.system_method,
+ 'cpu': self.cpu_method,
+ 'cpu_family': self.cpu_family_method,
+ 'endian': self.endian_method,
+ })
+
+ @noPosargs
+ @permittedKwargs({})
+ def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ return self.held_object.cpu_family
+
+ @noPosargs
+ @permittedKwargs({})
+ def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ return self.held_object.cpu
+
+ @noPosargs
+ @permittedKwargs({})
+ def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ return self.held_object.system
+
+ @noPosargs
+ @permittedKwargs({})
+ def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ return self.held_object.endian
+
+class IncludeDirsHolder(InterpreterObject, ObjectHolder[build.IncludeDirs]):
+ def __init__(self, idobj: build.IncludeDirs):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, idobj)
+
+class HeadersHolder(InterpreterObject, ObjectHolder[build.Headers]):
+
+ def __init__(self, obj: build.Headers):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, 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(InterpreterObject, ObjectHolder[build.Data]):
+ def __init__(self, data: build.Data):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, data)
+
+ def get_source_subdir(self):
+ return self.held_object.source_subdir
+
+ def get_sources(self):
+ return self.held_object.sources
+
+ def get_install_dir(self):
+ return self.held_object.install_dir
+
+class InstallDirHolder(InterpreterObject, ObjectHolder[build.IncludeDirs]):
+
+ def __init__(self, obj: build.InstallDir):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, obj)
+
+class ManHolder(InterpreterObject, ObjectHolder[build.Man]):
+
+ def __init__(self, obj: build.Man):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, 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(InterpreterObject, ObjectHolder[build.ExtractedObjects]):
+ def __init__(self, held_object: build.ExtractedObjects):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, held_object)
+
+class Test(InterpreterObject):
+ def __init__(self, name: str, project: str, suite: T.List[str], exe: build.Executable,
+ depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]],
+ is_parallel: bool, cmd_args: T.List[str], env: build.EnvironmentVariables,
+ should_fail: bool, timeout: int, workdir: T.Optional[str], protocol: str,
+ priority: int):
+ InterpreterObject.__init__(self)
+ self.name = name
+ self.suite = suite
+ self.project_name = project
+ self.exe = exe
+ self.depends = depends
+ self.is_parallel = is_parallel
+ self.cmd_args = cmd_args
+ self.env = env
+ self.should_fail = should_fail
+ self.timeout = timeout
+ self.workdir = workdir
+ self.protocol = TestProtocol.from_str(protocol)
+ self.priority = priority
+
+ def get_exe(self):
+ return self.exe
+
+ def get_name(self):
+ return self.name
+
+class SubprojectHolder(InterpreterObject, ObjectHolder[T.Optional['Interpreter']]):
+
+ def __init__(self, subinterpreter: T.Optional['Interpreter'], subdir: str, warnings=0, disabled_feature=None,
+ exception=None):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, subinterpreter)
+ self.warnings = warnings
+ self.disabled_feature = disabled_feature
+ self.exception = exception
+ self.subdir = PurePath(subdir).as_posix()
+ self.methods.update({'get_variable': self.get_variable_method,
+ 'found': self.found_method,
+ })
+
+ @noPosargs
+ @permittedKwargs({})
+ def found_method(self, args, kwargs):
+ return self.found()
+
+ def found(self):
+ return self.held_object is not None
+
+ @permittedKwargs({})
+ @noArgsFlattening
+ def get_variable_method(self, args, kwargs):
+ if len(args) < 1 or len(args) > 2:
+ raise InterpreterException('Get_variable takes one or two arguments.')
+ if not self.found():
+ raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir))
+ varname = args[0]
+ if not isinstance(varname, str):
+ raise InterpreterException('Get_variable first argument must be a string.')
+ try:
+ return self.held_object.variables[varname]
+ except KeyError:
+ pass
+
+ if len(args) == 2:
+ return args[1]
+
+ raise InvalidArguments(f'Requested variable "{varname}" not found.')
+
+class ModuleObjectHolder(InterpreterObject, ObjectHolder['ModuleObject']):
+ def __init__(self, modobj: 'ModuleObject', interpreter: 'Interpreter'):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, modobj)
+ self.interpreter = interpreter
+
+ def method_call(self, method_name, args, kwargs):
+ modobj = self.held_object
+ method = modobj.methods.get(method_name)
+ if not method and not modobj.methods:
+ # FIXME: Port all modules to use the methods dict.
+ method = getattr(modobj, method_name, None)
+ if method_name.startswith('_'):
+ raise InvalidArguments(f'Method {method_name!r} is private.')
+ if not method:
+ raise InvalidCode('Unknown method "%s" in object.' % method_name)
+ if not getattr(method, 'no-args-flattening', False):
+ args = flatten(args)
+ state = ModuleState(self.interpreter)
+ # Many modules do for example self.interpreter.find_program_impl(),
+ # so we have to ensure they use the current interpreter and not the one
+ # that first imported that module, otherwise it will use outdated
+ # overrides.
+ modobj.interpreter = self.interpreter
+ if method_name in modobj.snippets:
+ ret = method(self.interpreter, state, args, kwargs)
+ else:
+ # This is not 100% reliable but we can't use hash()
+ # because the Build object contains dicts and lists.
+ num_targets = len(self.interpreter.build.targets)
+ ret = method(state, args, kwargs)
+ if num_targets != len(self.interpreter.build.targets):
+ raise InterpreterException('Extension module altered internal state illegally.')
+ if isinstance(ret, ModuleReturnValue):
+ self.interpreter.process_new_values(ret.new_objects)
+ ret = ret.return_value
+ return self.interpreter.holderify(ret)
+
+_Target = T.TypeVar('_Target', bound=build.Target)
+
+
+class TargetHolder(InterpreterObject, ObjectHolder[_Target]):
+ def __init__(self, target: _Target, interp: 'Interpreter'):
+ InterpreterObject.__init__(self)
+ ObjectHolder.__init__(self, target, interp.subproject)
+ self.interpreter = interp
+
+
+_BuildTarget = T.TypeVar('_BuildTarget', bound=build.BuildTarget)
+
+class BuildTargetHolder(TargetHolder[_BuildTarget]):
+ def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'extract_objects': self.extract_objects_method,
+ 'extract_all_objects': self.extract_all_objects_method,
+ 'name': self.name_method,
+ 'get_id': self.get_id_method,
+ 'outdir': self.outdir_method,
+ 'full_path': self.full_path_method,
+ 'private_dir_include': self.private_dir_include_method,
+ })
+
+ def __repr__(self):
+ r = '<{} {}: {}>'
+ h = self.held_object
+ return r.format(self.__class__.__name__, h.get_id(), h.filename)
+
+ def is_cross(self):
+ return not self.held_object.environment.machines.matches_build_machine(self.held_object.for_machine)
+
+ @noPosargs
+ @permittedKwargs({})
+ def private_dir_include_method(self, args, kwargs):
+ return IncludeDirsHolder(build.IncludeDirs('', [], False,
+ [self.interpreter.backend.get_target_private_dir(self.held_object)]))
+
+ @noPosargs
+ @permittedKwargs({})
+ def full_path_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
+
+ @noPosargs
+ @permittedKwargs({})
+ def outdir_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_dir(self.held_object)
+
+ @permittedKwargs({})
+ def extract_objects_method(self, args, kwargs):
+ gobjs = self.held_object.extract_objects(args)
+ return GeneratedObjectsHolder(gobjs)
+
+ @FeatureNewKwargs('extract_all_objects', '0.46.0', ['recursive'])
+ @noPosargs
+ @permittedKwargs({'recursive'})
+ def extract_all_objects_method(self, args, kwargs):
+ recursive = kwargs.get('recursive', False)
+ gobjs = self.held_object.extract_all_objects(recursive)
+ if gobjs.objlist and 'recursive' not in kwargs:
+ mlog.warning('extract_all_objects called without setting recursive '
+ 'keyword argument. Meson currently defaults to '
+ 'non-recursive to maintain backward compatibility but '
+ 'the default will be changed in the future.',
+ location=self.current_node)
+ return GeneratedObjectsHolder(gobjs)
+
+ @noPosargs
+ @permittedKwargs({})
+ def get_id_method(self, args, kwargs):
+ return self.held_object.get_id()
+
+ @FeatureNew('name', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def name_method(self, args, kwargs):
+ return self.held_object.name
+
+class ExecutableHolder(BuildTargetHolder[build.Executable]):
+ pass
+
+class StaticLibraryHolder(BuildTargetHolder[build.StaticLibrary]):
+ pass
+
+class SharedLibraryHolder(BuildTargetHolder[build.SharedLibrary]):
+ def __init__(self, target: build.SharedLibrary, interp: 'Interpreter'):
+ super().__init__(target, interp)
+ # Set to True only when called from self.func_shared_lib().
+ target.shared_library_only = False
+
+class BothLibrariesHolder(BuildTargetHolder):
+ def __init__(self, shared_holder, static_holder, interp):
+ # FIXME: This build target always represents the shared library, but
+ # that should be configurable.
+ super().__init__(shared_holder.held_object, interp)
+ self.shared_holder = shared_holder
+ self.static_holder = static_holder
+ self.methods.update({'get_shared_lib': self.get_shared_lib_method,
+ 'get_static_lib': self.get_static_lib_method,
+ })
+
+ def __repr__(self):
+ r = '<{} {}: {}, {}: {}>'
+ h1 = self.shared_holder.held_object
+ h2 = self.static_holder.held_object
+ return r.format(self.__class__.__name__, h1.get_id(), h1.filename, h2.get_id(), h2.filename)
+
+ @noPosargs
+ @permittedKwargs({})
+ def get_shared_lib_method(self, args, kwargs):
+ return self.shared_holder
+
+ @noPosargs
+ @permittedKwargs({})
+ def get_static_lib_method(self, args, kwargs):
+ return self.static_holder
+
+class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
+ pass
+
+class JarHolder(BuildTargetHolder[build.Jar]):
+ pass
+
+class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]):
+ def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ })
+
+ @FeatureNew('custom_target[i].full_path', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def full_path_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
+
+class CustomTargetHolder(TargetHolder):
+ def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'):
+ super().__init__(target, interp)
+ self.methods.update({'full_path': self.full_path_method,
+ 'to_list': self.to_list_method,
+ })
+
+ def __repr__(self):
+ r = '<{} {}: {}>'
+ h = self.held_object
+ return r.format(self.__class__.__name__, h.get_id(), h.command)
+
+ @noPosargs
+ @permittedKwargs({})
+ def full_path_method(self, args, kwargs):
+ return self.interpreter.backend.get_target_filename_abs(self.held_object)
+
+ @FeatureNew('custom_target.to_list', '0.54.0')
+ @noPosargs
+ @permittedKwargs({})
+ def to_list_method(self, args, kwargs):
+ result = []
+ for i in self.held_object:
+ result.append(CustomTargetIndexHolder(i, self.interpreter))
+ return result
+
+ def __getitem__(self, index):
+ return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
+
+ def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
+ raise InterpreterException('Cannot set a member of a CustomTarget')
+
+ def __delitem__(self, index): # lgtm[py/unexpected-raise-in-special-method]
+ raise InterpreterException('Cannot delete a member of a CustomTarget')
+
+ def outdir_include(self):
+ return IncludeDirsHolder(build.IncludeDirs('', [], False,
+ [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
+
+class RunTargetHolder(TargetHolder):
+ def __init__(self, target, interp):
+ super().__init__(target, interp)
+
+ def __repr__(self):
+ r = '<{} {}: {}>'
+ h = self.held_object
+ return r.format(self.__class__.__name__, h.get_id(), h.command)
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
new file mode 100644
index 0000000..eef1ccf
--- /dev/null
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -0,0 +1,369 @@
+import os
+
+from .. import mesonlib
+from .. import dependencies
+from .. import build
+from .. import mlog
+
+from ..mesonlib import unholder, MachineChoice, OptionKey
+from ..programs import OverrideProgram, ExternalProgram
+from ..interpreterbase import (InterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
+ typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs,
+ MesonVersionString, InterpreterException)
+
+from .compiler import CompilerHolder
+from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
+ CustomTargetHolder, CustomTargetIndexHolder,
+ EnvironmentVariablesHolder)
+
+import typing as T
+
+class MesonMain(InterpreterObject):
+ def __init__(self, build: 'build.Build', interpreter: 'Interpreter'):
+ InterpreterObject.__init__(self)
+ self.build = build
+ self.interpreter = interpreter
+ self.methods.update({'get_compiler': self.get_compiler_method,
+ 'is_cross_build': self.is_cross_build_method,
+ 'has_exe_wrapper': self.has_exe_wrapper_method,
+ 'can_run_host_binaries': self.can_run_host_binaries_method,
+ 'is_unity': self.is_unity_method,
+ 'is_subproject': self.is_subproject_method,
+ 'current_source_dir': self.current_source_dir_method,
+ 'current_build_dir': self.current_build_dir_method,
+ 'source_root': self.source_root_method,
+ 'build_root': self.build_root_method,
+ 'project_source_root': self.project_source_root_method,
+ 'project_build_root': self.project_build_root_method,
+ 'add_install_script': self.add_install_script_method,
+ 'add_postconf_script': self.add_postconf_script_method,
+ 'add_dist_script': self.add_dist_script_method,
+ 'install_dependency_manifest': self.install_dependency_manifest_method,
+ 'override_dependency': self.override_dependency_method,
+ 'override_find_program': self.override_find_program_method,
+ 'project_version': self.project_version_method,
+ 'project_license': self.project_license_method,
+ 'version': self.version_method,
+ 'project_name': self.project_name_method,
+ 'get_cross_property': self.get_cross_property_method,
+ 'get_external_property': self.get_external_property_method,
+ 'has_external_property': self.has_external_property_method,
+ 'backend': self.backend_method,
+ 'add_devenv': self.add_devenv_method,
+ })
+
+ def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args):
+
+ if isinstance(prog, (ExecutableHolder, ExternalProgramHolder)):
+ return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args)
+ found = self.interpreter.func_find_program({}, prog, {}).held_object
+ es = self.interpreter.backend.get_executable_serialisation([found] + args)
+ es.subproject = self.interpreter.subproject
+ return es
+
+ def _process_script_args(
+ self, name: str, args: T.List[T.Union[
+ str, mesonlib.File, CustomTargetHolder,
+ CustomTargetIndexHolder,
+ ExternalProgramHolder, ExecutableHolder,
+ ]], allow_built: bool = False) -> T.List[str]:
+ script_args = [] # T.List[str]
+ new = False
+ for a in args:
+ a = unholder(a)
+ if isinstance(a, str):
+ script_args.append(a)
+ elif isinstance(a, mesonlib.File):
+ new = True
+ script_args.append(a.rel_to_builddir(self.interpreter.environment.source_dir))
+ elif isinstance(a, (build.BuildTarget, build.CustomTarget, build.CustomTargetIndex)):
+ if not allow_built:
+ raise InterpreterException(f'Arguments to {name} cannot be built')
+ new = True
+ script_args.extend([os.path.join(a.get_subdir(), o) for o in a.get_outputs()])
+
+ # This feels really hacky, but I'm not sure how else to fix
+ # this without completely rewriting install script handling.
+ # This is complicated by the fact that the install target
+ # depends on all.
+ if isinstance(a, build.CustomTargetIndex):
+ a.target.build_by_default = True
+ else:
+ a.build_by_default = True
+ elif isinstance(a, ExternalProgram):
+ script_args.extend(a.command)
+ new = True
+ else:
+ raise InterpreterException(
+ 'Arguments to {} must be strings, Files, or CustomTargets, '
+ 'Indexes of CustomTargets'.format(name))
+ if new:
+ FeatureNew.single_use(
+ 'Calling "{}" with File, CustomTaget, Index of CustomTarget, '
+ 'Executable, or ExternalProgram'.format(name),
+ '0.55.0', self.interpreter.subproject)
+ return script_args
+
+ @FeatureNewKwargs('add_install_script', '0.57.0', ['skip_if_destdir'])
+ @permittedKwargs({'skip_if_destdir'})
+ def add_install_script_method(self, args: 'T.Tuple[T.Union[str, mesonlib.File, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder], ...]', kwargs):
+ if len(args) < 1:
+ raise InterpreterException('add_install_script takes one or more arguments')
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_install_script',
+ '0.57.0', self.interpreter.subproject)
+ skip_if_destdir = kwargs.get('skip_if_destdir', False)
+ if not isinstance(skip_if_destdir, bool):
+ raise InterpreterException('skip_if_destdir keyword argument must be boolean')
+ script_args = self._process_script_args('add_install_script', args[1:], allow_built=True)
+ script = self._find_source_script(args[0], script_args)
+ script.skip_if_destdir = skip_if_destdir
+ self.build.install_scripts.append(script)
+
+ @permittedKwargs(set())
+ def add_postconf_script_method(self, args, kwargs):
+ if len(args) < 1:
+ raise InterpreterException('add_postconf_script takes one or more arguments')
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_postconf_script',
+ '0.57.0', self.interpreter.subproject)
+ script_args = self._process_script_args('add_postconf_script', args[1:], allow_built=True)
+ script = self._find_source_script(args[0], script_args)
+ self.build.postconf_scripts.append(script)
+
+ @permittedKwargs(set())
+ def add_dist_script_method(self, args, kwargs):
+ if len(args) < 1:
+ raise InterpreterException('add_dist_script takes one or more arguments')
+ if len(args) > 1:
+ FeatureNew.single_use('Calling "add_dist_script" with multiple arguments',
+ '0.49.0', self.interpreter.subproject)
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_dist_script',
+ '0.57.0', self.interpreter.subproject)
+ if self.interpreter.subproject != '':
+ FeatureNew.single_use('Calling "add_dist_script" in a subproject',
+ '0.58.0', self.interpreter.subproject)
+ script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True)
+ script = self._find_source_script(args[0], script_args)
+ self.build.dist_scripts.append(script)
+
+ @noPosargs
+ @permittedKwargs({})
+ def current_source_dir_method(self, args, kwargs):
+ src = self.interpreter.environment.source_dir
+ sub = self.interpreter.subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ @noPosargs
+ @permittedKwargs({})
+ def current_build_dir_method(self, args, kwargs):
+ src = self.interpreter.environment.build_dir
+ sub = self.interpreter.subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ @noPosargs
+ @permittedKwargs({})
+ def backend_method(self, args, kwargs):
+ return self.interpreter.backend.name
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureDeprecated('meson.source_root', '0.56.0', 'use meson.current_source_dir instead.')
+ def source_root_method(self, args, kwargs):
+ return self.interpreter.environment.source_dir
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureDeprecated('meson.build_root', '0.56.0', 'use meson.current_build_dir instead.')
+ def build_root_method(self, args, kwargs):
+ return self.interpreter.environment.build_dir
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureNew('meson.project_source_root', '0.56.0')
+ def project_source_root_method(self, args, kwargs):
+ src = self.interpreter.environment.source_dir
+ sub = self.interpreter.root_subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureNew('meson.project_build_root', '0.56.0')
+ def project_build_root_method(self, args, kwargs):
+ src = self.interpreter.environment.build_dir
+ sub = self.interpreter.root_subdir
+ if sub == '':
+ return src
+ return os.path.join(src, sub)
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureDeprecated('meson.has_exe_wrapper', '0.55.0', 'use meson.can_run_host_binaries instead.')
+ def has_exe_wrapper_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool:
+ return self.can_run_host_binaries_impl(args, kwargs)
+
+ @noPosargs
+ @permittedKwargs({})
+ @FeatureNew('meson.can_run_host_binaries', '0.55.0')
+ def can_run_host_binaries_method(self, args: T.Tuple[object, ...], kwargs: T.Dict[str, object]) -> bool:
+ return self.can_run_host_binaries_impl(args, kwargs)
+
+ def can_run_host_binaries_impl(self, args, kwargs):
+ if (self.is_cross_build_method(None, None) and
+ self.build.environment.need_exe_wrapper()):
+ if self.build.environment.exe_wrapper is None:
+ return False
+ # We return True when exe_wrap is defined, when it's not needed, and
+ # when we're compiling natively. The last two are semantically confusing.
+ # Need to revisit this.
+ return True
+
+ @noPosargs
+ @permittedKwargs({})
+ def is_cross_build_method(self, args, kwargs):
+ return self.build.environment.is_cross_build()
+
+ @permittedKwargs({'native'})
+ def get_compiler_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('get_compiler_method must have one and only one argument.')
+ cname = args[0]
+ for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ clist = self.interpreter.coredata.compilers[for_machine]
+ if cname in clist:
+ return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject)
+ raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.')
+
+ @noPosargs
+ @permittedKwargs({})
+ def is_unity_method(self, args, kwargs):
+ optval = self.interpreter.environment.coredata.get_option(OptionKey('unity'))
+ if optval == 'on' or (optval == 'subprojects' and self.interpreter.is_subproject()):
+ return True
+ return False
+
+ @noPosargs
+ @permittedKwargs({})
+ def is_subproject_method(self, args, kwargs):
+ return self.interpreter.is_subproject()
+
+ @permittedKwargs({})
+ def install_dependency_manifest_method(self, args, kwargs):
+ if len(args) != 1:
+ raise InterpreterException('Must specify manifest install file name')
+ if not isinstance(args[0], str):
+ raise InterpreterException('Argument must be a string.')
+ self.build.dep_manifest_name = args[0]
+
+ @FeatureNew('meson.override_find_program', '0.46.0')
+ @permittedKwargs({})
+ def override_find_program_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Override needs two arguments')
+ name, exe = args
+ if not isinstance(name, str):
+ raise InterpreterException('First argument must be a string')
+ exe = unholder(exe)
+ if isinstance(exe, mesonlib.File):
+ abspath = exe.absolute_path(self.interpreter.environment.source_dir,
+ self.interpreter.environment.build_dir)
+ if not os.path.exists(abspath):
+ raise InterpreterException('Tried to override %s with a file that does not exist.' % name)
+ exe = OverrideProgram(name, abspath)
+ if not isinstance(exe, (ExternalProgram, build.Executable)):
+ raise InterpreterException('Second argument must be an external program or executable.')
+ self.interpreter.add_find_program_override(name, exe)
+
+ @FeatureNew('meson.override_dependency', '0.54.0')
+ @permittedKwargs({'native'})
+ def override_dependency_method(self, args, kwargs):
+ if len(args) != 2:
+ raise InterpreterException('Override needs two arguments')
+ name = args[0]
+ dep = args[1]
+ if not isinstance(name, str) or not name:
+ raise InterpreterException('First argument must be a string and cannot be empty')
+ dep = unholder(dep)
+ if not isinstance(dep, dependencies.Dependency):
+ raise InterpreterException('Second argument must be a dependency object')
+ identifier = dependencies.get_dep_identifier(name, kwargs)
+ for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ override = self.build.dependency_overrides[for_machine].get(identifier)
+ if override:
+ m = 'Tried to override dependency {!r} which has already been resolved or overridden at {}'
+ location = mlog.get_error_location_string(override.node.filename, override.node.lineno)
+ raise InterpreterException(m.format(name, location))
+ self.build.dependency_overrides[for_machine][identifier] = \
+ build.DependencyOverride(dep, self.interpreter.current_node)
+
+ @noPosargs
+ @permittedKwargs({})
+ def project_version_method(self, args, kwargs):
+ return self.build.dep_manifest[self.interpreter.active_projectname]['version']
+
+ @FeatureNew('meson.project_license()', '0.45.0')
+ @noPosargs
+ @permittedKwargs({})
+ def project_license_method(self, args, kwargs):
+ return self.build.dep_manifest[self.interpreter.active_projectname]['license']
+
+ @noPosargs
+ @permittedKwargs({})
+ def version_method(self, args, kwargs):
+ return MesonVersionString(self.interpreter.coredata.version)
+
+ @noPosargs
+ @permittedKwargs({})
+ def project_name_method(self, args, kwargs):
+ return self.interpreter.active_projectname
+
+ def __get_external_property_impl(self, propname: str, fallback: T.Optional[object], machine: MachineChoice) -> object:
+ """Shared implementation for get_cross_property and get_external_property."""
+ try:
+ return self.interpreter.environment.properties[machine][propname]
+ except KeyError:
+ if fallback is not None:
+ return fallback
+ raise InterpreterException(f'Unknown property for {machine.get_lower_case_name()} machine: {propname}')
+
+ @noArgsFlattening
+ @permittedKwargs({})
+ @FeatureDeprecated('meson.get_cross_property', '0.58.0', 'Use meson.get_external_property() instead')
+ @typed_pos_args('meson.get_cross_property', str, optargs=[object])
+ def get_cross_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: T.Dict[str, T.Any]) -> object:
+ propname, fallback = args
+ return self.__get_external_property_impl(propname, fallback, MachineChoice.HOST)
+
+ @noArgsFlattening
+ @permittedKwargs({'native'})
+ @FeatureNew('meson.get_external_property', '0.54.0')
+ @typed_pos_args('meson.get_external_property', str, optargs=[object])
+ def get_external_property_method(self, args: T.Tuple[str, T.Optional[object]], kwargs: T.Dict[str, T.Any]) -> object:
+ propname, fallback = args
+ machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ return self.__get_external_property_impl(propname, fallback, machine)
+
+
+ @permittedKwargs({'native'})
+ @FeatureNew('meson.has_external_property', '0.58.0')
+ @typed_pos_args('meson.has_external_property', str)
+ def has_external_property_method(self, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
+ prop_name = args[0]
+ for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ return prop_name in self.interpreter.environment.properties[for_machine]
+
+ @FeatureNew('add_devenv', '0.58.0')
+ @noKwargs
+ @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder))
+ def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None:
+ env = args[0]
+ if isinstance(env, (str, list, dict)):
+ env = EnvironmentVariablesHolder(env)
+ self.build.devenv.append(env.held_object)
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index 84fe658..ee09359 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -20,8 +20,9 @@ from . import ExtensionModule, ModuleReturnValue
from .. import build, mesonlib, mlog
from ..cmake import SingleTargetOptions, TargetOptions, cmake_defines_to_args
-from ..interpreter import ConfigurationDataHolder, InterpreterException, SubprojectHolder, DependencyHolder
+from ..interpreter import ConfigurationDataHolder, SubprojectHolder, DependencyHolder
from ..interpreterbase import (
+ InterpreterException,
InterpreterObject,
ObjectHolder,
diff --git a/mesonbuild/modules/keyval.py b/mesonbuild/modules/keyval.py
index 8123a57..5fc4d52 100644
--- a/mesonbuild/modules/keyval.py
+++ b/mesonbuild/modules/keyval.py
@@ -16,8 +16,7 @@ from . import ExtensionModule
from .. import mesonlib
from ..mesonlib import typeslistify
-from ..interpreterbase import FeatureNew, noKwargs
-from ..interpreter import InvalidCode
+from ..interpreterbase import FeatureNew, noKwargs, InvalidCode
import os
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py
index 91f7146..36d3164 100644
--- a/mesonbuild/modules/unstable_rust.py
+++ b/mesonbuild/modules/unstable_rust.py
@@ -19,8 +19,8 @@ from . import ExtensionModule, ModuleReturnValue
from .. import mlog
from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget
from ..dependencies import Dependency, ExternalLibrary
-from ..interpreter import ExecutableHolder, BuildTargetHolder, CustomTargetHolder, permitted_kwargs, noPosargs
-from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew, typed_pos_args
+from ..interpreter import ExecutableHolder, BuildTargetHolder, CustomTargetHolder, permitted_kwargs
+from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew, typed_pos_args, noPosargs
from ..mesonlib import stringlistify, unholder, listify, typeslistify, File
if T.TYPE_CHECKING:
diff --git a/run_unittests.py b/run_unittests.py
index 1eba4df..33e2eec 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -50,8 +50,8 @@ import mesonbuild.environment
import mesonbuild.mesonlib
import mesonbuild.coredata
import mesonbuild.modules.gnome
-from mesonbuild.interpreter import Interpreter, ObjectHolder
-from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments
+from mesonbuild.interpreter import Interpreter
+from mesonbuild.interpreterbase import typed_pos_args, InvalidArguments, ObjectHolder
from mesonbuild.ast import AstInterpreter
from mesonbuild.mesonlib import (
BuildDirLock, LibType, MachineChoice, PerMachine, Version, is_windows,