aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Quick-guide.md2
-rw-r--r--docs/markdown/Reference-manual.md13
-rw-r--r--docs/markdown/_include_qt_base.md22
-rw-r--r--docs/markdown/snippets/build-target-found.md16
-rw-r--r--docs/markdown/snippets/compiler_argument_checking.md6
-rw-r--r--mesonbuild/ast/interpreter.py39
-rw-r--r--mesonbuild/backend/backends.py18
-rw-r--r--mesonbuild/backend/ninjabackend.py4
-rw-r--r--mesonbuild/build.py169
-rw-r--r--mesonbuild/cmake/interpreter.py4
-rw-r--r--mesonbuild/compilers/__init__.py2
-rw-r--r--mesonbuild/compilers/compilers.py9
-rw-r--r--mesonbuild/coredata.py4
-rw-r--r--mesonbuild/dependencies/base.py4
-rw-r--r--mesonbuild/envconfig.py4
-rw-r--r--mesonbuild/interpreter/__init__.py2
-rw-r--r--mesonbuild/interpreter/compiler.py75
-rw-r--r--mesonbuild/interpreter/dependencyfallbacks.py57
-rw-r--r--mesonbuild/interpreter/interpreter.py490
-rw-r--r--mesonbuild/interpreter/interpreterobjects.py790
-rw-r--r--mesonbuild/interpreter/kwargs.py30
-rw-r--r--mesonbuild/interpreter/mesonmain.py37
-rw-r--r--mesonbuild/interpreterbase/__init__.py8
-rw-r--r--mesonbuild/interpreterbase/_unholder.py37
-rw-r--r--mesonbuild/interpreterbase/baseobjects.py55
-rw-r--r--mesonbuild/interpreterbase/decorators.py22
-rw-r--r--mesonbuild/interpreterbase/disabler.py4
-rw-r--r--mesonbuild/interpreterbase/helpers.py8
-rw-r--r--mesonbuild/interpreterbase/interpreterbase.py151
-rw-r--r--mesonbuild/mesonlib/universal.py38
-rw-r--r--mesonbuild/modules/__init__.py18
-rw-r--r--mesonbuild/modules/cmake.py17
-rw-r--r--mesonbuild/modules/gnome.py70
-rw-r--r--mesonbuild/modules/hotdoc.py11
-rw-r--r--mesonbuild/modules/i18n.py3
-rw-r--r--mesonbuild/modules/pkgconfig.py44
-rw-r--r--mesonbuild/modules/python.py54
-rw-r--r--mesonbuild/modules/qt.py79
-rw-r--r--mesonbuild/modules/sourceset.py10
-rw-r--r--mesonbuild/modules/unstable_cuda.py11
-rw-r--r--mesonbuild/modules/unstable_external_project.py3
-rw-r--r--mesonbuild/modules/unstable_rust.py40
-rw-r--r--mesonbuild/modules/unstable_simd.py4
-rw-r--r--mesonbuild/modules/windows.py12
-rw-r--r--mesonbuild/programs.py25
-rwxr-xr-xrun_mypy.py3
-rwxr-xr-xrun_project_tests.py21
-rwxr-xr-xrun_single_test.py2
-rwxr-xr-xrun_tests.py16
-rwxr-xr-xrun_unittests.py34
-rw-r--r--test cases/cmake/25 assembler/main.c18
-rw-r--r--test cases/cmake/25 assembler/meson.build9
-rw-r--r--test cases/cmake/25 assembler/subprojects/cmTest/CMakeLists.txt45
-rw-r--r--test cases/cmake/25 assembler/subprojects/cmTest/cmTest.c8
-rw-r--r--test cases/cmake/25 assembler/subprojects/cmTest/cmTestAsm.s4
-rw-r--r--test cases/common/178 bothlibraries/meson.build3
-rw-r--r--test cases/common/182 find override/meson.build10
-rw-r--r--test cases/common/182 find override/otherdir/meson.build4
-rw-r--r--test cases/failing/115 compiler argument checking/meson.build4
-rw-r--r--test cases/failing/115 compiler argument checking/test.json7
-rw-r--r--test cases/failing/115 empty fallback/meson.build6
-rw-r--r--test cases/failing/115 empty fallback/subprojects/foo/meson.build3
-rw-r--r--test cases/failing/115 empty fallback/test.json7
-rw-r--r--test cases/failing/45 abspath to srcdir/test.json2
-rw-r--r--test cases/frameworks/4 qt/meson.build10
-rw-r--r--test cases/native/9 override with exe/meson.build2
66 files changed, 1582 insertions, 1157 deletions
diff --git a/docs/markdown/Quick-guide.md b/docs/markdown/Quick-guide.md
index 1363a69..c1de820 100644
--- a/docs/markdown/Quick-guide.md
+++ b/docs/markdown/Quick-guide.md
@@ -29,7 +29,7 @@ Requirements
* [Ninja](https://github.com/ninja-build/ninja/)
*Ninja is only needed if you use the Ninja backend. Meson can also
-generate native VS and XCode project files.*
+generate native VS and Xcode project files.*
Installation using package manager
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index f3f87cc..de7f9f2 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -515,6 +515,8 @@ This function supports the following keyword arguments:
in this case the subproject must use
`meson.override_dependency('dependency_name', subproj_dep)`
to specify the dependency object used in the superproject.
+ If the value is an empty list, it has the same effect as
+ `allow_fallback: false`.
- `language` *(since 0.42.0)*: defines what language-specific
dependency to find if it's available for multiple languages.
- `method`: defines the way the dependency is detected, the default is
@@ -2561,6 +2563,12 @@ module](#shared_module).
this and will also allow Meson to setup inter-target dependencies
correctly. Please file a bug if that doesn't work for you.
+- `path()` *(since 0.59.0)* **(deprecated)**: does the exact same
+ as `full_path()`. **NOTE:** This function is solely kept for compatebility
+ with [`external program`](#external-program-object) objects. It will be
+ removed once the, also deprecated, corresponding `path()` function in the
+ `external program` object is removed.
+
- `private_dir_include()`: returns a opaque value that works like
`include_directories` but points to the private directory of this
target, usually only needed if an another target needs to access
@@ -2568,6 +2576,11 @@ module](#shared_module).
- `name()` *(since 0.54.0)*: returns the target name.
+- `found()` *(since 0.59.0)*: Always returns `true`. This function is meant
+ to make executables objects feature compatible with
+ [`external program`](#external-program-object) objects. This simplifies
+ use-cases where an executable is used instead of an external program.
+
### `configuration` data object
diff --git a/docs/markdown/_include_qt_base.md b/docs/markdown/_include_qt_base.md
index 4c50abc..aff9146 100644
--- a/docs/markdown/_include_qt_base.md
+++ b/docs/markdown/_include_qt_base.md
@@ -41,7 +41,7 @@ It takes no positional arguments, and the following keyword arguments:
- `extra_args` string[]: Extra arguments to pass directly to `qt-moc`
- `method` string: The method to use to detect qt, see `dependency()` for more
information.
- - `include_directories` IncludeDirectory[]: A list of `include_directory()`
+ - `include_directories` (string | IncludeDirectory)[]: A list of `include_directory()`
objects used when transpiling the .moc files
## preprocess
@@ -62,16 +62,16 @@ sources += qt.preprocess(qresources : ['resources'])
```
This method takes the following keyword arguments:
- - `qresources`: a list of strings, Files, Custom Targets, or Build Targets to pass the `rcc` compiler
- - `ui_files`: a list of strings, Files, Custom Targets, or Build Targets to pass the `uic` compiler
- - `moc_sources`: a list of strings, Files, Custom Targets, or Build Targets to pass the `moc` compiler the
- - `moc_headers`: a list of strings, Files, Custom Targets, or Build Targets to pass the `moc` compiler. These will be converted into .cpp files
- - `include_directories`, the directories to add to header search path for `moc` (optional)
- - `moc_extra_arguments`, any additional arguments to `moc` (optional). Available since v0.44.0.
- - `uic_extra_arguments`, any additional arguments to `uic` (optional). Available since v0.49.0.
- - `rcc_extra_arguments`, any additional arguments to `rcc` (optional). Available since v0.49.0.
- - `dependencies`, dependency objects needed by moc. Available since v0.48.0.
- - `sources`, a list of extra sources, which are added to the output unchaged. Deprecated in 0.59.0
+ - `qresources` (string | File | CustomTarget | BuildTarget)[]: Passed to the RCC compiler
+ - `ui_files`: (string | File | CustomTarget | BuilduTarget)[]: Passed the `uic` compiler
+ - `moc_sources`: (string | File | CustomTarget | BuildTarget)[]: Passed the `moc` compiler the
+ - `moc_headers`: (string | File | CustomTarget | BuildTarget)[]: Passied the `moc` compiler. These will be converted into .cpp files
+ - `include_directories` (IncludeDirectories | string)[], the directories to add to header search path for `moc`
+ - `moc_extra_arguments` string[]: any additional arguments to `moc`. Since v0.44.0.
+ - `uic_extra_arguments` string[]: any additional arguments to `uic`. Since v0.49.0.
+ - `rcc_extra_arguments` string[]: any additional arguments to `rcc`. Since v0.49.0.
+ - `dependencies` Dependency[]: dependency objects needed by moc. Available since v0.48.0.
+ - `sources`: a list of extra sources, which are added to the output unchaged. Deprecated in 0.59.0.
It returns an array of targets and sources to pass to a compilation target.
diff --git a/docs/markdown/snippets/build-target-found.md b/docs/markdown/snippets/build-target-found.md
new file mode 100644
index 0000000..60c5083
--- /dev/null
+++ b/docs/markdown/snippets/build-target-found.md
@@ -0,0 +1,16 @@
+## New `build target` methods
+
+The [`build target` object](Reference-manual.md#build-target-object) now supports
+the following two functions, to ensure feature compatebility with
+[`external program` objects](Reference-manual.html#external-program-object):
+
+- `found()`: Always returns `true`. This function is meant
+ to make executables objects feature compatible with
+ `external program` objects. This simplifies
+ use-cases where an executable is used instead of an external program.
+
+- `path()`: **(deprecated)** does the exact same as `full_path()`.
+ **NOTE:** This function is solely kept for compatebility
+ with `external program` objects. It will be
+ removed once the, also deprecated, corresponding `path()` function in the
+ `external program` object is removed.
diff --git a/docs/markdown/snippets/compiler_argument_checking.md b/docs/markdown/snippets/compiler_argument_checking.md
new file mode 100644
index 0000000..0e038ec
--- /dev/null
+++ b/docs/markdown/snippets/compiler_argument_checking.md
@@ -0,0 +1,6 @@
+## Compiler argument checking for `get_supported_arguments`
+
+The compiler method `get_supported_arguments` now supports
+a new keyword argument named `checked` that can be set to
+one of `warn`, `require` or `off` (defaults to `off`) to
+enforce argument checks.
diff --git a/mesonbuild/ast/interpreter.py b/mesonbuild/ast/interpreter.py
index 71c7f47..b9dfc7b 100644
--- a/mesonbuild/ast/interpreter.py
+++ b/mesonbuild/ast/interpreter.py
@@ -16,10 +16,20 @@
# or an interpreter-based tool.
from .visitor import AstVisitor
-from .. import interpreterbase, mparser, mesonlib
+from .. import mparser, mesonlib
from .. import environment
-from ..interpreterbase import InvalidArguments, BreakRequest, ContinueRequest, TYPE_nvar, TYPE_nkwargs
+from ..interpreterbase import (
+ MesonInterpreterObject,
+ InterpreterBase,
+ InvalidArguments,
+ BreakRequest,
+ ContinueRequest,
+ default_resolve_key,
+ TYPE_nvar,
+ TYPE_nkwargs,
+)
+
from ..mparser import (
AndNode,
ArgumentNode,
@@ -45,28 +55,31 @@ from ..mparser import (
import os, sys
import typing as T
-class DontCareObject(interpreterbase.InterpreterObject):
+class DontCareObject(MesonInterpreterObject):
pass
-class MockExecutable(interpreterbase.InterpreterObject):
+class MockExecutable(MesonInterpreterObject):
pass
-class MockStaticLibrary(interpreterbase.InterpreterObject):
+class MockStaticLibrary(MesonInterpreterObject):
pass
-class MockSharedLibrary(interpreterbase.InterpreterObject):
+class MockSharedLibrary(MesonInterpreterObject):
pass
-class MockCustomTarget(interpreterbase.InterpreterObject):
+class MockCustomTarget(MesonInterpreterObject):
pass
-class MockRunTarget(interpreterbase.InterpreterObject):
+class MockRunTarget(MesonInterpreterObject):
pass
ADD_SOURCE = 0
REMOVE_SOURCE = 1
-class AstInterpreter(interpreterbase.InterpreterBase):
+_T = T.TypeVar('_T')
+_V = T.TypeVar('_V')
+
+class AstInterpreter(InterpreterBase):
def __init__(self, source_root: str, subdir: str, subproject: str, visitors: T.Optional[T.List[AstVisitor]] = None):
super().__init__(source_root, subdir, subproject)
self.visitors = visitors if visitors is not None else []
@@ -131,6 +144,12 @@ class AstInterpreter(interpreterbase.InterpreterBase):
'range': self.func_do_nothing,
})
+ def _unholder_args(self, args: _T, kwargs: _V) -> T.Tuple[_T, _V]:
+ return args, kwargs
+
+ def _holderify(self, res: _T) -> _T:
+ return res
+
def func_do_nothing(self, node: BaseNode, args: T.List[TYPE_nvar], kwargs: T.Dict[str, TYPE_nvar]) -> bool:
return True
@@ -224,7 +243,7 @@ class AstInterpreter(interpreterbase.InterpreterBase):
def reduce_arguments(
self,
args: mparser.ArgumentNode,
- key_resolver: T.Callable[[mparser.BaseNode], str] = interpreterbase.default_resolve_key,
+ key_resolver: T.Callable[[mparser.BaseNode], str] = default_resolve_key,
duplicate_key_error: T.Optional[str] = None,
) -> T.Tuple[T.List[TYPE_nvar], TYPE_nkwargs]:
if isinstance(args, ArgumentNode):
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 64e0b18..2652ae6 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -32,7 +32,7 @@ from .. import mlog
from ..compilers import LANGUAGES_USING_LDFLAGS
from ..mesonlib import (
File, MachineChoice, MesonException, OptionType, OrderedSet, OptionOverrideProxy,
- classify_unity_sources, unholder, OptionKey, join_args
+ classify_unity_sources, OptionKey, join_args
)
if T.TYPE_CHECKING:
@@ -237,7 +237,7 @@ class Backend:
def generate(self) -> None:
raise RuntimeError(f'generate is not implemented in {type(self).__name__}')
- def get_target_filename(self, t, *, warn_multi_output: bool = True):
+ def get_target_filename(self, t: T.Union[build.Target, build.CustomTargetIndex], *, warn_multi_output: bool = True):
if isinstance(t, build.CustomTarget):
if warn_multi_output and len(t.get_outputs()) != 1:
mlog.warning(f'custom_target {t.name!r} has more than one output! '
@@ -250,7 +250,7 @@ class Backend:
filename = t.get_filename()
return os.path.join(self.get_target_dir(t), filename)
- def get_target_filename_abs(self, target):
+ def get_target_filename_abs(self, target: T.Union[build.Target, build.CustomTargetIndex]) -> str:
return os.path.join(self.environment.get_build_dir(), self.get_target_filename(target))
def get_base_options_for_target(self, target: build.BuildTarget) -> OptionOverrideProxy:
@@ -309,7 +309,7 @@ class Backend:
raise AssertionError(f'BUG: Tried to link to {target!r} which is not linkable')
@lru_cache(maxsize=None)
- def get_target_dir(self, target):
+ def get_target_dir(self, target: build.Target) -> str:
if self.environment.coredata.get_option(OptionKey('layout')) == 'mirror':
dirname = target.get_subdir()
else:
@@ -329,7 +329,7 @@ class Backend:
return os.path.join(self.build_to_src, target_dir)
return self.build_to_src
- def get_target_private_dir(self, target):
+ def get_target_private_dir(self, target: build.Target) -> str:
return os.path.join(self.get_target_filename(target, warn_multi_output=False) + '.p')
def get_target_private_dir_abs(self, target):
@@ -963,7 +963,7 @@ class Backend:
depends = set(t.depends)
if isinstance(exe, build.Target):
depends.add(exe)
- for a in unholder(t.cmd_args):
+ for a in t.cmd_args:
if isinstance(a, build.Target):
depends.add(a)
if isinstance(a, build.BuildTarget):
@@ -1091,10 +1091,10 @@ class Backend:
# also be built by default. XXX: Sometime in the future these should be
# built only before running tests.
for t in self.build.get_tests():
- exe = unholder(t.exe)
+ exe = t.exe
if isinstance(exe, (build.CustomTarget, build.BuildTarget)):
result[exe.get_id()] = exe
- for arg in unholder(t.cmd_args):
+ for arg in t.cmd_args:
if not isinstance(arg, (build.CustomTarget, build.BuildTarget)):
continue
result[arg.get_id()] = arg
@@ -1133,7 +1133,7 @@ class Backend:
Returns the path to them relative to the build root directory.
'''
srcs = []
- for i in unholder(target.get_sources()):
+ for i in target.get_sources():
if isinstance(i, str):
fname = [os.path.join(self.build_to_src, target.subdir, i)]
elif isinstance(i, build.BuildTarget):
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 688149b..192cef3 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -40,7 +40,7 @@ from ..compilers import (
from ..linkers import ArLinker, RSPFileSyntax
from ..mesonlib import (
File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
- ProgressBar, quote_arg, unholder,
+ ProgressBar, quote_arg
)
from ..mesonlib import get_compiler_for_source, has_path_sep, OptionKey
from .backends import CleanTrees
@@ -937,7 +937,7 @@ int dummy;
self.generate_target(t)
def custom_target_generator_inputs(self, target):
- for s in unholder(target.sources):
+ for s in target.sources:
if isinstance(s, build.GeneratedList):
self.generate_genlist_for_target(s, target)
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 0a792fc..03f97b2 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -20,6 +20,7 @@ import itertools, pathlib
import os
import pickle
import re
+import textwrap
import typing as T
from . import environment
@@ -27,10 +28,12 @@ from . import dependencies
from . import mlog
from . import programs
from .mesonlib import (
+ HoldableObject,
File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
extract_as_list, typeslistify, stringlistify, classify_unity_sources,
- get_filenames_templates_dict, substitute_values, has_path_sep, unholder,
+ get_filenames_templates_dict, substitute_values, has_path_sep,
OptionKey, PerMachineDefaultable,
+ MesonBugException, FileOrString,
)
from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
@@ -45,7 +48,6 @@ if T.TYPE_CHECKING:
from .mesonlib import FileMode, FileOrString
from .modules import ModuleState
from .backend.backends import Backend
- from .interpreter.interpreterobjects import GeneratorHolder
pch_kwargs = {'c_pch', 'cpp_pch'}
@@ -124,13 +126,13 @@ def get_target_macos_dylib_install_name(ld) -> str:
class InvalidArguments(MesonException):
pass
-class DependencyOverride:
+class DependencyOverride(HoldableObject):
def __init__(self, dep, node, explicit=True):
self.dep = dep
self.node = node
self.explicit = explicit
-class Headers:
+class Headers(HoldableObject):
def __init__(self, sources: T.List[File], install_subdir: T.Optional[str],
install_dir: T.Optional[str], install_mode: T.Optional['FileMode'],
@@ -160,7 +162,7 @@ class Headers:
return self.custom_install_mode
-class Man:
+class Man(HoldableObject):
def __init__(self, sources: T.List[File], install_dir: T.Optional[str],
install_mode: T.Optional['FileMode'], subproject: str,
@@ -181,7 +183,7 @@ class Man:
return self.sources
-class InstallDir:
+class InstallDir(HoldableObject):
def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str,
install_mode: T.Optional['FileMode'],
@@ -326,29 +328,30 @@ class Build:
return link_args.get(compiler.get_language(), [])
-class IncludeDirs:
- def __init__(self, curdir, dirs, is_system, extra_build_dirs=None):
+class IncludeDirs(HoldableObject):
+
+ """Internal representation of an include_directories call."""
+
+ def __init__(self, curdir: str, dirs: T.List[str], is_system: bool, extra_build_dirs: T.Optional[T.List[str]] = None):
self.curdir = curdir
self.incdirs = dirs
self.is_system = is_system
+
# Interpreter has validated that all given directories
# actually exist.
- if extra_build_dirs is None:
- self.extra_build_dirs = []
- else:
- self.extra_build_dirs = extra_build_dirs
+ self.extra_build_dirs: T.List[str] = extra_build_dirs or []
- def __repr__(self):
+ def __repr__(self) -> str:
r = '<{} {}/{}>'
return r.format(self.__class__.__name__, self.curdir, self.incdirs)
- def get_curdir(self):
+ def get_curdir(self) -> str:
return self.curdir
- def get_incdirs(self):
+ def get_incdirs(self) -> T.List[str]:
return self.incdirs
- def get_extra_build_dirs(self):
+ def get_extra_build_dirs(self) -> T.List[str]:
return self.extra_build_dirs
def to_string_list(self, sourcedir: str) -> T.List[str]:
@@ -358,7 +361,7 @@ class IncludeDirs:
strlist.append(os.path.join(sourcedir, self.curdir, idir))
return strlist
-class ExtractedObjects:
+class ExtractedObjects(HoldableObject):
'''
Holds a list of sources for which the objects must be extracted
'''
@@ -415,7 +418,7 @@ class ExtractedObjects:
for source in self.get_sources(self.srclist, self.genlist)
]
-class EnvironmentVariables:
+class EnvironmentVariables(HoldableObject):
def __init__(self) -> None:
self.envvars = []
# The set of all env vars we have operations for. Only used for self.has_name()
@@ -457,16 +460,18 @@ class EnvironmentVariables:
env[name] = method(env, name, values, separator)
return env
-class Target:
+class Target(HoldableObject):
# TODO: should Target be an abc.ABCMeta?
def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice):
if has_path_sep(name):
# Fix failing test 53 when this becomes an error.
- mlog.warning(f'''Target "{name}" has a path separator in its name.
-This is not supported, it can cause unexpected failures and will become
-a hard error in the future.''')
+ mlog.warning(textwrap.dedent(f'''\
+ Target "{name}" has a path separator in its name.
+ This is not supported, it can cause unexpected failures and will become
+ a hard error in the future.\
+ '''))
self.name = name
self.subdir = subdir
self.subproject = subproject
@@ -677,7 +682,7 @@ class BuildTarget(Target):
def process_objectlist(self, objects):
assert(isinstance(objects, list))
- for s in unholder(objects):
+ for s in objects:
if isinstance(s, (str, File, ExtractedObjects)):
self.objects.append(s)
elif isinstance(s, (GeneratedList, CustomTarget)):
@@ -773,7 +778,7 @@ class BuildTarget(Target):
# which is what we need.
if not is_object(s):
sources.append(s)
- for d in unholder(self.external_deps):
+ for d in self.external_deps:
for s in d.sources:
if isinstance(s, (str, File)):
sources.append(s)
@@ -844,7 +849,7 @@ class BuildTarget(Target):
link_depends.
"""
sources = listify(sources)
- for s in unholder(sources):
+ for s in sources:
if isinstance(s, File):
self.link_depends.append(s)
elif isinstance(s, str):
@@ -861,35 +866,16 @@ class BuildTarget(Target):
def get_original_kwargs(self):
return self.kwargs
- def unpack_holder(self, d):
- d = listify(d)
- newd = []
- for i in d:
- if isinstance(i, list):
- i = self.unpack_holder(i)
- elif hasattr(i, 'held_object'):
- i = i.held_object
- for t in ['dependencies', 'link_with', 'include_directories', 'sources']:
- if hasattr(i, t):
- setattr(i, t, self.unpack_holder(getattr(i, t)))
- newd.append(i)
- return newd
-
def copy_kwargs(self, kwargs):
self.kwargs = copy.copy(kwargs)
- # This sucks quite badly. Arguments
- # are holders but they can't be pickled
- # so unpack those known.
for k, v in self.kwargs.items():
if isinstance(v, list):
- self.kwargs[k] = self.unpack_holder(v)
- if hasattr(v, 'held_object'):
- self.kwargs[k] = v.held_object
+ self.kwargs[k] = listify(v, flatten=True)
for t in ['dependencies', 'link_with', 'include_directories', 'sources']:
if t in self.kwargs:
- self.kwargs[t] = self.unpack_holder(self.kwargs[t])
+ self.kwargs[t] = listify(self.kwargs[t], flatten=True)
- def extract_objects(self, srclist):
+ def extract_objects(self, srclist: T.List[FileOrString]) -> ExtractedObjects:
obj_src = []
sources_set = set(self.sources)
for src in srclist:
@@ -898,14 +884,14 @@ class BuildTarget(Target):
elif isinstance(src, File):
FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject)
else:
- raise MesonException('Object extraction arguments must be strings or Files.')
+ raise MesonException(f'Object extraction arguments must be strings or Files (got {type(src).__name__}).')
# FIXME: It could be a generated source
if src not in sources_set:
raise MesonException(f'Tried to extract unknown source {src}.')
obj_src.append(src)
return ExtractedObjects(self, obj_src)
- def extract_all_objects(self, recursive=True):
+ def extract_all_objects(self, recursive: bool = True) -> ExtractedObjects:
return ExtractedObjects(self, self.sources, self.generated, self.objects,
recursive)
@@ -957,13 +943,15 @@ class BuildTarget(Target):
kwargs.get('modules', [])
self.need_install = kwargs.get('install', self.need_install)
llist = extract_as_list(kwargs, 'link_with')
- for linktarget in unholder(llist):
+ for linktarget in llist:
if isinstance(linktarget, dependencies.ExternalLibrary):
- raise MesonException('''An external library was used in link_with keyword argument, which
-is reserved for libraries built as part of this project. External
-libraries must be passed using the dependencies keyword argument
-instead, because they are conceptually "external dependencies",
-just like those detected with the dependency() function.''')
+ raise MesonException(textwrap.dedent('''\
+ An external library was used in link_with keyword argument, which
+ is reserved for libraries built as part of this project. External
+ libraries must be passed using the dependencies keyword argument
+ instead, because they are conceptually "external dependencies",
+ just like those detected with the dependency() function.\
+ '''))
self.link(linktarget)
lwhole = extract_as_list(kwargs, 'link_whole')
for linktarget in lwhole:
@@ -998,7 +986,7 @@ just like those detected with the dependency() function.''')
if dfeature_debug:
dfeatures['debug'] = dfeature_debug
if 'd_import_dirs' in kwargs:
- dfeature_import_dirs = unholder(extract_as_list(kwargs, 'd_import_dirs'))
+ dfeature_import_dirs = extract_as_list(kwargs, 'd_import_dirs')
for d in dfeature_import_dirs:
if not isinstance(d, IncludeDirs):
raise InvalidArguments('Arguments to d_import_dirs must be include_directories.')
@@ -1012,8 +1000,11 @@ just like those detected with the dependency() function.''')
raise InvalidArguments('Link_args arguments must be strings.')
for l in self.link_args:
if '-Wl,-rpath' in l or l.startswith('-rpath'):
- mlog.warning('''Please do not define rpath with a linker argument, use install_rpath or build_rpath properties instead.
-This will become a hard error in a future Meson release.''')
+ mlog.warning(textwrap.dedent('''\
+ Please do not define rpath with a linker argument, use install_rpath
+ or build_rpath properties instead.
+ This will become a hard error in a future Meson release.\
+ '''))
self.process_link_depends(kwargs.get('link_depends', []), environment)
# Target-specific include dirs must be added BEFORE include dirs from
# internal deps (added inside self.add_deps()) to override them.
@@ -1192,7 +1183,7 @@ This will become a hard error in a future Meson release.''')
def add_deps(self, deps):
deps = listify(deps)
- for dep in unholder(deps):
+ for dep in deps:
if dep in self.added_deps:
continue
if isinstance(dep, dependencies.InternalDependency):
@@ -1242,7 +1233,9 @@ You probably should put it in link_with instead.''')
return isinstance(self, StaticLibrary) and not self.need_install
def link(self, target):
- for t in unholder(listify(target)):
+ for t in listify(target):
+ if isinstance(t, BothLibraries):
+ t = t.get_preferred_library()
if isinstance(self, StaticLibrary) and self.need_install:
if isinstance(t, (CustomTarget, CustomTargetIndex)):
if not t.should_install():
@@ -1270,7 +1263,10 @@ You probably should put it in link_with instead.''')
self.link_targets.append(t)
def link_whole(self, target):
- for t in unholder(listify(target)):
+ for t in listify(target):
+ # Always use the static library from BothLibraries, since shared libs aren't supported anyway
+ if isinstance(t, BothLibraries):
+ t = t.static
if isinstance(t, (CustomTarget, CustomTargetIndex)):
if not t.is_linkable_target():
raise InvalidArguments(f'Custom target {t!r} is not linkable.')
@@ -1336,7 +1332,7 @@ You probably should put it in link_with instead.''')
def add_include_dirs(self, args, set_is_system: T.Optional[str] = None):
ids = []
- for a in unholder(args):
+ for a in args:
if not isinstance(a, IncludeDirs):
raise InvalidArguments('Include directory to be added is not an include directory object.')
ids.append(a)
@@ -1500,7 +1496,7 @@ You probably should put it in link_with instead.''')
'platforms')
return
-class Generator:
+class Generator(HoldableObject):
def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
arguments: T.List[str],
output: T.List[str],
@@ -1576,7 +1572,7 @@ class Generator:
return output
-class GeneratedList:
+class GeneratedList(HoldableObject):
"""The output of generator.process."""
@@ -1832,6 +1828,8 @@ class SharedLibrary(BuildTarget):
self.gcc_import_filename = None
# The debugging information file this target will generate
self.debug_filename = None
+ # Use by the pkgconfig module
+ self.shared_library_only = False
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
if 'rust' in self.compilers:
# If no crate type is specified, or it's the generic lib type, use dylib
@@ -2048,7 +2046,7 @@ class SharedLibrary(BuildTarget):
# Visual Studio module-definitions file
if 'vs_module_defs' in kwargs:
- path = unholder(kwargs['vs_module_defs'])
+ path = kwargs['vs_module_defs']
if isinstance(path, str):
if os.path.isabs(path):
self.vs_module_defs = File.from_absolute_file(path)
@@ -2151,9 +2149,22 @@ class SharedModule(SharedLibrary):
def get_default_install_dir(self, environment):
return environment.get_shared_module_dir()
+class BothLibraries(HoldableObject):
+ def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None:
+ self._preferred_library = 'shared'
+ self.shared = shared
+ self.static = static
+
+ def get_preferred_library(self) -> BuildTarget:
+ if self._preferred_library == 'shared':
+ return self.shared
+ elif self._preferred_library == 'static':
+ return self.static
+ raise MesonBugException(f'self._preferred_library == "{self._preferred_library}" is neither "shared" nor "static".')
+
class CommandBase:
def flatten_command(self, cmd):
- cmd = unholder(listify(cmd))
+ cmd = listify(cmd)
final_cmd = []
for c in cmd:
if isinstance(c, str):
@@ -2228,7 +2239,7 @@ class CustomTarget(Target, CommandBase):
def get_target_dependencies(self):
deps = self.dependencies[:]
deps += self.extra_depends
- for c in unholder(self.sources):
+ for c in self.sources:
if isinstance(c, (BuildTarget, CustomTarget)):
deps.append(c)
return deps
@@ -2253,7 +2264,7 @@ class CustomTarget(Target, CommandBase):
def process_kwargs(self, kwargs, backend):
self.process_kwargs_base(kwargs)
- self.sources = unholder(extract_as_list(kwargs, 'input'))
+ self.sources = extract_as_list(kwargs, 'input')
if 'output' not in kwargs:
raise InvalidArguments('Missing keyword argument "output".')
self.outputs = listify(kwargs['output'])
@@ -2332,7 +2343,7 @@ class CustomTarget(Target, CommandBase):
if not isinstance(self.build_always_stale, bool):
raise InvalidArguments('Argument build_always_stale must be a boolean.')
extra_deps, depend_files = [extract_as_list(kwargs, c, pop=False) for c in ['depends', 'depend_files']]
- for ed in unholder(extra_deps):
+ for ed in extra_deps:
if not isinstance(ed, (CustomTarget, BuildTarget)):
raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target '
f'(executable or a library) got: {type(ed)}({ed})')
@@ -2368,7 +2379,7 @@ class CustomTarget(Target, CommandBase):
def get_generated_lists(self):
genlists = []
- for c in unholder(self.sources):
+ for c in self.sources:
if isinstance(c, GeneratedList):
genlists.append(c)
return genlists
@@ -2419,7 +2430,7 @@ class CustomTarget(Target, CommandBase):
def type_suffix(self):
return "@cus"
- def __getitem__(self, index):
+ def __getitem__(self, index: int) -> 'CustomTargetIndex':
return CustomTargetIndex(self, self.outputs[index])
def __setitem__(self, index, value):
@@ -2519,7 +2530,7 @@ class Jar(BuildTarget):
return ['-cp', os.pathsep.join(cp_paths)]
return []
-class CustomTargetIndex:
+class CustomTargetIndex(HoldableObject):
"""A special opaque object returned by indexing a CustomTarget. This object
exists in Meson, but acts as a proxy in the backends, making targets depend
@@ -2575,10 +2586,16 @@ class CustomTargetIndex:
def get_custom_install_dir(self):
return self.target.get_custom_install_dir()
-class ConfigurationData:
+class ConfigurationData(HoldableObject):
def __init__(self) -> None:
super().__init__()
- self.values = {} # T.Dict[str, T.Union[str, int, bool]]
+ self.values: T.Dict[
+ str,
+ T.Tuple[
+ T.Union[str, int, bool],
+ T.Optional[str]
+ ]
+ ] = {}
def __repr__(self):
return repr(self.values)
@@ -2594,7 +2611,7 @@ class ConfigurationData:
# A bit poorly named, but this represents plain data files to copy
# during install.
-class Data:
+class Data(HoldableObject):
def __init__(self, sources: T.List[File], install_dir: str,
install_mode: T.Optional['FileMode'], subproject: str,
rename: T.List[str] = None):
@@ -2623,7 +2640,7 @@ def get_sources_string_names(sources, backend):
get all the output basenames.
'''
names = []
- for s in unholder(sources):
+ for s in sources:
if isinstance(s, str):
names.append(s)
elif isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)):
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index f79f7d2..745def1 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -24,7 +24,7 @@ from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib
from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible, OptionKey
from ..mesondata import mesondata
-from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
+from ..compilers.compilers import assembler_suffixes, lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from ..programs import ExternalProgram
from enum import Enum
from functools import lru_cache
@@ -435,7 +435,7 @@ class ConverterTarget:
self.link_libraries = temp
# Filter out files that are not supported by the language
- supported = list(header_suffixes) + list(obj_suffixes)
+ supported = list(assembler_suffixes) + list(header_suffixes) + list(obj_suffixes)
for i in self.languages:
supported += list(lang_suffixes[i])
supported = [f'.{x}' for x in supported]
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index b46c8f6..be4809f 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -15,6 +15,7 @@
# Public symbols for compilers sub-package when using 'from . import compilers'
__all__ = [
'Compiler',
+ 'RunResult',
'all_languages',
'base_options',
@@ -112,6 +113,7 @@ __all__ = [
# Bring symbols from each module into compilers sub-package namespace
from .compilers import (
Compiler,
+ RunResult,
all_languages,
base_options,
clib_langs,
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 19288eb..ff87819 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -23,6 +23,7 @@ from .. import coredata
from .. import mlog
from .. import mesonlib
from ..mesonlib import (
+ HoldableObject,
EnvironmentException, MachineChoice, MesonException,
Popen_safe, LibType, TemporaryDirectoryWinProof, OptionKey,
)
@@ -72,6 +73,8 @@ c_suffixes = lang_suffixes['c'] + ('h',) # type: T.Tuple[str, ...]
# List of languages that by default consume and output libraries following the
# C ABI; these can generally be used interchangeably
clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',) # type: T.Tuple[str, ...]
+# List of assembler suffixes that can be linked with C code directly by the linker
+assembler_suffixes: T.Tuple[str, ...] = ('s', 'S')
# List of languages that can be linked with C code directly by the linker
# used in build.py:process_compilers() and build.py:get_dynamic_linker()
clink_langs = ('d', 'cuda') + clib_langs # type: T.Tuple[str, ...]
@@ -435,7 +438,7 @@ def get_base_link_args(options: 'KeyedOptionDictType', linker: 'Compiler',
class CrossNoRunException(MesonException):
pass
-class RunResult:
+class RunResult(HoldableObject):
def __init__(self, compiled: bool, returncode: int = 999,
stdout: str = 'UNDEFINED', stderr: str = 'UNDEFINED'):
self.compiled = compiled
@@ -444,7 +447,7 @@ class RunResult:
self.stderr = stderr
-class CompileResult:
+class CompileResult(HoldableObject):
"""The result of Compiler.compiles (and friends)."""
@@ -467,7 +470,7 @@ class CompileResult:
self.text_mode = text_mode
-class Compiler(metaclass=abc.ABCMeta):
+class Compiler(HoldableObject, metaclass=abc.ABCMeta):
# Libraries to ignore in find_library() since they are provided by the
# compiler or the C library. Currently only used for MSVC.
ignore_libs = [] # type: T.List[str]
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 89cec46..107e4b8 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -19,6 +19,7 @@ from itertools import chain
from pathlib import PurePath
from collections import OrderedDict
from .mesonlib import (
+ HoldableObject,
MesonException, EnvironmentException, MachineChoice, PerMachine,
PerMachineDefaultable, default_libdir, default_libexecdir,
default_prefix, split_args, OptionKey, OptionType, stringlistify,
@@ -61,7 +62,7 @@ class MesonVersionMismatchException(MesonException):
self.current_version = current_version
-class UserOption(T.Generic[_T]):
+class UserOption(T.Generic[_T], HoldableObject):
def __init__(self, description: str, choices: T.Optional[T.Union[str, T.List[_T]]], yielding: T.Optional[bool]):
super().__init__()
self.choices = choices
@@ -255,6 +256,7 @@ class UserFeatureOption(UserComboOption):
def __init__(self, description: str, value: T.Any, yielding: T.Optional[bool] = None):
super().__init__(description, self.static_choices, value, yielding)
+ self.name: T.Optional[str] = None # TODO: Refactor options to all store their name
def is_enabled(self) -> bool:
return self.value == 'enabled'
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 515edcf..e12c697 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -22,7 +22,7 @@ from enum import Enum
from .. import mlog
from ..compilers import clib_langs
-from ..mesonlib import MachineChoice, MesonException
+from ..mesonlib import MachineChoice, MesonException, HoldableObject
from ..mesonlib import version_compare_many
from ..interpreterbase import FeatureDeprecated
@@ -65,7 +65,7 @@ class DependencyMethods(Enum):
DependencyTypeName = T.NewType('DependencyTypeName', str)
-class Dependency:
+class Dependency(HoldableObject):
@classmethod
def _process_include_type_kw(cls, kwargs: T.Dict[str, T.Any]) -> str:
diff --git a/mesonbuild/envconfig.py b/mesonbuild/envconfig.py
index a93905a..307aac3 100644
--- a/mesonbuild/envconfig.py
+++ b/mesonbuild/envconfig.py
@@ -17,7 +17,7 @@ import typing as T
from enum import Enum
from . import mesonlib
-from .mesonlib import EnvironmentException
+from .mesonlib import EnvironmentException, HoldableObject
from . import mlog
from pathlib import Path
@@ -232,7 +232,7 @@ class Properties:
def get(self, key: str, default: T.Optional[T.Union[str, bool, int, T.List[str]]] = None) -> T.Optional[T.Union[str, bool, int, T.List[str]]]:
return self.properties.get(key, default)
-class MachineInfo:
+class MachineInfo(HoldableObject):
def __init__(self, system: str, cpu_family: str, cpu: str, endian: str):
self.system = system
self.cpu_family = cpu_family
diff --git a/mesonbuild/interpreter/__init__.py b/mesonbuild/interpreter/__init__.py
index 58ee729..62b09bf 100644
--- a/mesonbuild/interpreter/__init__.py
+++ b/mesonbuild/interpreter/__init__.py
@@ -20,6 +20,6 @@ from .interpreter import Interpreter, permitted_dependency_kwargs
from .compiler import CompilerHolder
from .interpreterobjects import (ExecutableHolder, BuildTargetHolder, CustomTargetHolder,
CustomTargetIndexHolder, MachineHolder, Test,
- ConfigurationDataHolder, SubprojectHolder, DependencyHolder,
+ ConfigurationDataObject, SubprojectHolder, DependencyHolder,
GeneratedListHolder, ExternalProgramHolder,
extract_required_kwarg)
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index 3a3ce34..b1eef2f 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -1,22 +1,26 @@
import functools
-from .interpreterobjects import (IncludeDirsHolder, ExternalLibraryHolder,
- extract_required_kwarg, extract_search_dirs)
+from ..interpreterbase.decorators import typed_kwargs, KwargInfo
+
+from .interpreterobjects import (extract_required_kwarg, extract_search_dirs)
from .. import mesonlib
from .. import mlog
from .. import dependencies
-from ..interpreterbase import (InterpreterObject, noPosargs, noKwargs, permittedKwargs,
+from ..interpreterbase import (ObjectHolder, noPosargs, noKwargs, permittedKwargs,
FeatureNew, FeatureNewKwargs, disablerIfNotFound,
- check_stringlist, InterpreterException, InvalidArguments,
- InvalidCode)
+ check_stringlist, InterpreterException, InvalidArguments)
import typing as T
+import os
+
+if T.TYPE_CHECKING:
+ from ..interpreter import Interpreter
+ from ..compilers import Compiler, RunResult
-class TryRunResultHolder(InterpreterObject):
- def __init__(self, res):
- super().__init__()
- self.res = res
+class TryRunResultHolder(ObjectHolder['RunResult']):
+ def __init__(self, res: 'RunResult', interpreter: 'Interpreter'):
+ super().__init__(res, interpreter)
self.methods.update({'returncode': self.returncode_method,
'compiled': self.compiled_method,
'stdout': self.stdout_method,
@@ -26,22 +30,22 @@ class TryRunResultHolder(InterpreterObject):
@noPosargs
@permittedKwargs({})
def returncode_method(self, args, kwargs):
- return self.res.returncode
+ return self.held_object.returncode
@noPosargs
@permittedKwargs({})
def compiled_method(self, args, kwargs):
- return self.res.compiled
+ return self.held_object.compiled
@noPosargs
@permittedKwargs({})
def stdout_method(self, args, kwargs):
- return self.res.stdout
+ return self.held_object.stdout
@noPosargs
@permittedKwargs({})
def stderr_method(self, args, kwargs):
- return self.res.stderr
+ return self.held_object.stderr
header_permitted_kwargs = {
'required',
@@ -61,12 +65,10 @@ find_library_permitted_kwargs = {
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
+class CompilerHolder(ObjectHolder['Compiler']):
+ def __init__(self, compiler: 'Compiler', interpreter: 'Interpreter'):
+ super().__init__(compiler, interpreter)
+ self.environment = self.env
self.methods.update({'compiles': self.compiles_method,
'links': self.links_method,
'get_id': self.get_id_method,
@@ -101,6 +103,10 @@ class CompilerHolder(InterpreterObject):
'get_argument_syntax': self.get_argument_syntax_method,
})
+ @property
+ def compiler(self) -> 'Compiler':
+ return self.held_object
+
def _dep_msg(self, deps, endl):
msg_single = 'with dependency {}'
msg_many = 'with dependencies {}'
@@ -139,9 +145,10 @@ class CompilerHolder(InterpreterObject):
args = []
incdirs = mesonlib.extract_as_list(kwargs, 'include_directories')
for i in incdirs:
- if not isinstance(i, IncludeDirsHolder):
+ from ..build import IncludeDirs
+ if not isinstance(i, IncludeDirs):
raise InterpreterException('Include directories argument must be an include_directories object.')
- for idir in i.held_object.to_string_list(self.environment.get_source_dir()):
+ for idir in i.to_string_list(self.environment.get_source_dir()):
args += self.compiler.get_include_args(idir, False)
if not nobuiltins:
opts = self.environment.coredata.options
@@ -157,7 +164,7 @@ class CompilerHolder(InterpreterObject):
final_deps = []
while deps:
next_deps = []
- for d in mesonlib.unholder(mesonlib.listify(deps)):
+ for d in mesonlib.listify(deps):
if not isinstance(d, dependencies.Dependency) or d.is_built():
raise InterpreterException('Dependencies must be external dependencies')
final_deps.append(d)
@@ -218,7 +225,7 @@ class CompilerHolder(InterpreterObject):
else:
h = mlog.red('NO (%d)' % result.returncode)
mlog.log('Checking if', mlog.bold(testname, True), msg, 'runs:', h)
- return TryRunResultHolder(result)
+ return result
@noPosargs
@permittedKwargs({})
@@ -609,7 +616,7 @@ class CompilerHolder(InterpreterObject):
self.environment,
self.compiler.language,
silent=True)
- return ExternalLibraryHolder(lib, self.subproject)
+ return lib
@FeatureNewKwargs('compiler.find_library', '0.51.0', ['static'])
@FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
@@ -654,7 +661,7 @@ class CompilerHolder(InterpreterObject):
libtype, libname))
lib = dependencies.ExternalLibrary(libname, linkargs, self.environment,
self.compiler.language)
- return ExternalLibraryHolder(lib, self.subproject)
+ return lib
@permittedKwargs({})
def has_argument_method(self, args: T.Sequence[str], kwargs) -> bool:
@@ -679,12 +686,24 @@ class CompilerHolder(InterpreterObject):
return result
@FeatureNew('compiler.get_supported_arguments', '0.43.0')
- @permittedKwargs({})
- def get_supported_arguments_method(self, args, kwargs):
+ @typed_kwargs(
+ 'compiler.get_supported_arguments',
+ KwargInfo('checked', str, default='off', since='0.59.0',
+ validator=lambda s: 'must be one of "warn", "require" or "off"' if s not in ['warn', 'require', 'off'] else None)
+ )
+ def get_supported_arguments_method(self, args: T.Sequence[str], kwargs: T.Dict[str, T.Any]):
args = mesonlib.stringlistify(args)
supported_args = []
+ checked = kwargs.pop('checked')
+
for arg in args:
- if self.has_argument_method(arg, kwargs):
+ if not self.has_argument_method(arg, kwargs):
+ msg = f'Compiler for {self.compiler.get_display_language()} does not support "{arg}"'
+ if checked == 'warn':
+ mlog.warning(msg)
+ elif checked == 'require':
+ raise mesonlib.MesonException(msg)
+ else:
supported_args.append(arg)
return supported_args
diff --git a/mesonbuild/interpreter/dependencyfallbacks.py b/mesonbuild/interpreter/dependencyfallbacks.py
index 6edb129..019073c 100644
--- a/mesonbuild/interpreter/dependencyfallbacks.py
+++ b/mesonbuild/interpreter/dependencyfallbacks.py
@@ -1,12 +1,12 @@
-from .interpreterobjects import DependencyHolder, SubprojectHolder, extract_required_kwarg
+from .interpreterobjects import SubprojectHolder, extract_required_kwarg
from .. import mlog
from .. import dependencies
from .. import build
from ..wrap import WrapMode
from ..mesonlib import OptionKey, extract_as_list, stringlistify, version_compare_many
-from ..dependencies import DependencyException, NotFoundDependency
-from ..interpreterbase import (InterpreterObject, FeatureNew,
+from ..dependencies import Dependency, DependencyException, NotFoundDependency
+from ..interpreterbase import (MesonInterpreterObject, FeatureNew,
InterpreterException, InvalidArguments,
TYPE_nkwargs, TYPE_nvar)
@@ -15,7 +15,7 @@ if T.TYPE_CHECKING:
from .interpreter import Interpreter
-class DependencyFallbacksHolder(InterpreterObject):
+class DependencyFallbacksHolder(MesonInterpreterObject):
def __init__(self, interpreter: 'Interpreter', names: T.List[str], allow_fallback: T.Optional[bool] = None) -> None:
super().__init__()
self.interpreter = interpreter
@@ -47,6 +47,10 @@ class DependencyFallbacksHolder(InterpreterObject):
location=self.interpreter.current_node)
return
fbinfo = stringlistify(fbinfo)
+ if len(fbinfo) == 0:
+ # dependency('foo', fallback: []) is the same as dependency('foo', allow_fallback: false)
+ self.allow_fallback = False
+ return
if len(fbinfo) == 1:
FeatureNew.single_use('Fallback without variable name', '0.53.0', self.subproject)
subp_name, varname = fbinfo[0], None
@@ -71,14 +75,14 @@ class DependencyFallbacksHolder(InterpreterObject):
self.subproject_varname = varname
self.subproject_kwargs = kwargs
- def _do_dependency_cache(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _do_dependency_cache(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
name = func_args[0]
cached_dep = self._get_cached_dep(name, kwargs)
if cached_dep:
self._verify_fallback_consistency(cached_dep)
return cached_dep
- def _do_dependency(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _do_dependency(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
# Note that there is no df.dependency() method, this is called for names
# given as positional arguments to dependency_fallbacks(name1, ...).
# We use kwargs from the dependency() function, for things like version,
@@ -90,17 +94,17 @@ class DependencyFallbacksHolder(InterpreterObject):
for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
identifier = dependencies.get_dep_identifier(name, kwargs)
self.coredata.deps[for_machine].put(identifier, dep)
- return DependencyHolder(dep, self.subproject)
+ return dep
return None
- def _do_existing_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _do_existing_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
subp_name = func_args[0]
varname = self.subproject_varname
if subp_name and self._get_subproject(subp_name):
return self._get_subproject_dep(subp_name, varname, kwargs)
return None
- def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _do_subproject(self, kwargs: TYPE_nkwargs, func_args: TYPE_nvar, func_kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
if self.nofallback:
mlog.log('Not looking for a fallback subproject for the dependency',
mlog.bold(self.display_name), 'because:\nUse of fallback dependencies is disabled.')
@@ -124,7 +128,7 @@ class DependencyFallbacksHolder(InterpreterObject):
return sub
return None
- def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _get_subproject_dep(self, subp_name: str, varname: str, kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
# Verify the subproject is found
subproject = self._get_subproject(subp_name)
if not subproject:
@@ -166,7 +170,7 @@ class DependencyFallbacksHolder(InterpreterObject):
return var_dep
wanted = stringlistify(kwargs.get('version', []))
- found = var_dep.held_object.get_version()
+ found = var_dep.get_version()
if not self._check_version(wanted, found):
mlog.log('Dependency', mlog.bold(self.display_name), 'from subproject',
mlog.bold(subproject.subdir), 'found:', mlog.red('NO'),
@@ -179,7 +183,7 @@ class DependencyFallbacksHolder(InterpreterObject):
mlog.normal_cyan(found) if found else None)
return var_dep
- def _get_cached_dep(self, name: str, kwargs: TYPE_nkwargs) -> T.Optional[DependencyHolder]:
+ def _get_cached_dep(self, name: str, kwargs: TYPE_nkwargs) -> T.Optional[Dependency]:
# Unlike other methods, this one returns not-found dependency instead
# of None in the case the dependency is cached as not-found, or if cached
# version does not match. In that case we don't want to continue with
@@ -198,7 +202,7 @@ class DependencyFallbacksHolder(InterpreterObject):
if not cached_dep.found():
mlog.log('Dependency', mlog.bold(self.display_name),
'found:', mlog.red('NO'), *info)
- return DependencyHolder(cached_dep, self.subproject)
+ return cached_dep
else:
info = [mlog.blue('(cached)')]
cached_dep = self.coredata.deps[for_machine].get(identifier)
@@ -216,24 +220,27 @@ class DependencyFallbacksHolder(InterpreterObject):
info = [mlog.normal_cyan(found_vers), *info]
mlog.log('Dependency', mlog.bold(self.display_name),
'found:', mlog.green('YES'), *info)
- return DependencyHolder(cached_dep, self.subproject)
+ return cached_dep
return None
- def _get_subproject_variable(self, subproject: SubprojectHolder, varname: str) -> T.Optional[DependencyHolder]:
- var_dep = subproject.held_object.variables.get(varname)
- if not isinstance(var_dep, DependencyHolder):
+ def _get_subproject_variable(self, subproject: SubprojectHolder, varname: str) -> T.Optional[Dependency]:
+ try:
+ var_dep = subproject.get_variable_method([varname], {})
+ except InvalidArguments:
+ var_dep = None
+ if not isinstance(var_dep, Dependency):
mlog.warning(f'Variable {varname!r} in the subproject {subproject.subdir!r} is',
'not found' if var_dep is None else 'not a dependency object')
return None
return var_dep
- def _verify_fallback_consistency(self, cached_dep: DependencyHolder):
+ def _verify_fallback_consistency(self, cached_dep: Dependency):
subp_name = self.subproject_name
varname = self.subproject_varname
subproject = self._get_subproject(subp_name)
if subproject and varname:
var_dep = self._get_subproject_variable(subproject, varname)
- if var_dep and cached_dep.found() and var_dep.held_object != cached_dep.held_object:
+ if var_dep and cached_dep.found() and var_dep != cached_dep:
mlog.warning(f'Inconsistency: Subproject has overridden the dependency with another variable than {varname!r}')
def _handle_featurenew_dependencies(self, name: str) -> None:
@@ -249,8 +256,8 @@ class DependencyFallbacksHolder(InterpreterObject):
elif name == 'openmp':
FeatureNew.single_use('OpenMP Dependency', '0.46.0', self.subproject)
- def _notfound_dependency(self) -> DependencyHolder:
- return DependencyHolder(NotFoundDependency(self.environment), self.subproject)
+ def _notfound_dependency(self) -> NotFoundDependency:
+ return NotFoundDependency(self.environment)
@staticmethod
def _check_version(wanted: T.Optional[str], found: str) -> bool:
@@ -260,7 +267,7 @@ class DependencyFallbacksHolder(InterpreterObject):
return False
return True
- def _get_candidates(self) -> T.List[T.Tuple[T.Callable[[TYPE_nkwargs, TYPE_nvar, TYPE_nkwargs], T.Optional[DependencyHolder]], TYPE_nvar, TYPE_nkwargs]]:
+ def _get_candidates(self) -> T.List[T.Tuple[T.Callable[[TYPE_nkwargs, TYPE_nvar, TYPE_nkwargs], T.Optional[Dependency]], TYPE_nvar, TYPE_nkwargs]]:
candidates = []
# 1. check if any of the names is cached already.
for name in self.names:
@@ -277,7 +284,7 @@ class DependencyFallbacksHolder(InterpreterObject):
candidates.append((self._do_subproject, [self.subproject_name], self.subproject_kwargs))
return candidates
- def lookup(self, kwargs: TYPE_nkwargs, force_fallback: bool = False) -> DependencyHolder:
+ def lookup(self, kwargs: TYPE_nkwargs, force_fallback: bool = False) -> Dependency:
self.display_name = self.names[0] if self.names else '(anonymous)'
mods = extract_as_list(kwargs, 'modules')
if mods:
@@ -325,7 +332,7 @@ class DependencyFallbacksHolder(InterpreterObject):
func_kwargs['required'] = required and (i == last)
kwargs['required'] = required and (i == last)
dep = func(kwargs, func_args, func_kwargs)
- if dep and dep.held_object.found():
+ if dep and dep.found():
# Override this dependency to have consistent results in subsequent
# dependency lookups.
for name in self.names:
@@ -333,7 +340,7 @@ class DependencyFallbacksHolder(InterpreterObject):
identifier = dependencies.get_dep_identifier(name, kwargs)
if identifier not in self.build.dependency_overrides[for_machine]:
self.build.dependency_overrides[for_machine][identifier] = \
- build.DependencyOverride(dep.held_object, self.interpreter.current_node, explicit=False)
+ build.DependencyOverride(dep, self.interpreter.current_node, explicit=False)
return dep
elif required and (dep or i == last):
# This was the last candidate or the dependency has been cached
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index b5335e6..5be99b4 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -21,33 +21,35 @@ 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 ..mesonlib import HoldableObject, FileMode, MachineChoice, OptionKey, listify, extract_as_list, has_path_sep
from ..programs import ExternalProgram, NonExistingExternalProgram
from ..dependencies import Dependency
from ..depfile import DepFile
from ..interpreterbase import ContainerTypeInfo, InterpreterBase, KwargInfo, typed_kwargs, typed_pos_args
-from ..interpreterbase import noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening
+from ..interpreterbase import noPosargs, noKwargs, stringArgs, permittedKwargs, noArgsFlattening, unholder_return
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 ..interpreterbase import TYPE_nkwargs, TYPE_nvar, TYPE_var
from ..modules import ModuleObject, MutableModuleObject
from ..cmake import CMakeInterpreter
from ..backend.backends import Backend, ExecutableSerialisation
+from . import interpreterobjects as OBJ
+from . import compiler as compilerOBJ
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, MutableModuleObjectHolder)
from .dependencyfallbacks import DependencyFallbacksHolder
+from .interpreterobjects import (
+ SubprojectHolder,
+ EnvironmentVariablesObject,
+ ConfigurationDataObject,
+ Test,
+ RunProcess,
+ extract_required_kwarg,
+ extract_search_dirs,
+ NullSubprojectInterpreter,
+)
from pathlib import Path
import os
@@ -57,18 +59,18 @@ import re
import stat
import collections
import typing as T
-
+import textwrap
import importlib
if T.TYPE_CHECKING:
from . import kwargs
# Input source types passed to Targets
- SourceInputs = T.Union[mesonlib.File, GeneratedListHolder, TargetHolder,
- CustomTargetIndexHolder, GeneratedObjectsHolder, str]
+ SourceInputs = T.Union[mesonlib.File, build.GeneratedList, build.BuildTarget,
+ build.CustomTargetIndex, build.CustomTarget, build.GeneratedList, str]
# Input source types passed to the build.Target5 classes
SourceOutputs = T.Union[mesonlib.File, build.GeneratedList,
- build.BuildTarget, build.CustomTargetIndex,
+ build.BuildTarget, build.CustomTargetIndex, build.CustomTarget,
build.GeneratedList]
@@ -126,7 +128,6 @@ class Summary:
raise InterpreterException(f'Summary section {section!r} already have key {k!r}')
formatted_values = []
for i in listify(v):
- i = unholder(i)
if isinstance(i, bool) and bool_yn:
formatted_values.append(mlog.green('YES') if i else mlog.red('NO'))
elif isinstance(i, (str, int, bool)):
@@ -190,7 +191,7 @@ known_build_target_kwargs = (
)
TEST_KWARGS: T.List[KwargInfo] = [
- KwargInfo('args', ContainerTypeInfo(list, (str, mesonlib.File, TargetHolder)),
+ KwargInfo('args', ContainerTypeInfo(list, (str, mesonlib.File, build.Target)),
listify=True, default=[]),
KwargInfo('should_fail', bool, default=False),
KwargInfo('timeout', int, default=30),
@@ -200,11 +201,11 @@ TEST_KWARGS: T.List[KwargInfo] = [
default='exitcode',
validator=lambda x: 'value must be one of "exitcode", "tap", "gtest", "rust"' if x not in {'exitcode', 'tap', 'gtest', 'rust'} else None,
since_values={'gtest': '0.55.0', 'rust': '0.57.0'}),
- KwargInfo('depends', ContainerTypeInfo(list, (CustomTargetHolder, BuildTargetHolder)),
+ KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.BuildTarget)),
listify=True, default=[], since='0.46.0'),
KwargInfo('priority', int, default=0, since='0.52.0'),
# TODO: env needs reworks of the way the environment variable holder itself works probably
- KwargInfo('env', (EnvironmentVariablesHolder, list, dict, str)),
+ KwargInfo('env', (EnvironmentVariablesObject, list, dict, str)),
KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=['']), # yes, a list of empty string
]
@@ -230,11 +231,11 @@ permitted_dependency_kwargs = {
'version',
}
-class Interpreter(InterpreterBase):
+class Interpreter(InterpreterBase, HoldableObject):
def __init__(
self,
- build: build.Build,
+ _build: build.Build,
backend: T.Optional[Backend] = None,
subproject: str = '',
subdir: str = '',
@@ -245,10 +246,10 @@ class Interpreter(InterpreterBase):
ast: T.Optional[mparser.CodeBlockNode] = None,
is_translated: bool = False,
) -> None:
- super().__init__(build.environment.get_source_dir(), subdir, subproject)
+ super().__init__(_build.environment.get_source_dir(), subdir, subproject)
self.an_unpicklable_object = mesonlib.an_unpicklable_object
- self.build = build
- self.environment = build.environment
+ self.build = _build
+ self.environment = self.build.environment
self.coredata = self.environment.get_coredata()
self.backend = backend
self.summary = {}
@@ -267,12 +268,12 @@ class Interpreter(InterpreterBase):
elif ast is not None:
self.ast = ast
self.sanity_check_ast()
- self.builtin.update({'meson': MesonMain(build, self)})
- self.generators: T.List['GeneratorHolder'] = []
+ self.builtin.update({'meson': MesonMain(self.build, self)})
+ self.generators: T.List[build.Generator] = []
self.processed_buildfiles = set() # type: T.Set[str]
self.project_args_frozen = False
self.global_args_frozen = False # implies self.project_args_frozen
- self.subprojects = {}
+ self.subprojects: T.Dict[str, SubprojectHolder] = {}
self.subproject_stack = []
self.configure_file_outputs = {}
# Passed from the outside, only used in subprojects.
@@ -282,6 +283,7 @@ class Interpreter(InterpreterBase):
self.default_project_options = {}
self.project_default_options = {}
self.build_func_dict()
+ self.build_holder_map()
# build_def_files needs to be defined before parse_project is called
#
@@ -308,11 +310,11 @@ class Interpreter(InterpreterBase):
assert self.build.environment.machines.target.cpu is not None
self.builtin['build_machine'] = \
- MachineHolder(self.build.environment.machines.build)
+ OBJ.MachineHolder(self.build.environment.machines.build, self)
self.builtin['host_machine'] = \
- MachineHolder(self.build.environment.machines.host)
+ OBJ.MachineHolder(self.build.environment.machines.host, self)
self.builtin['target_machine'] = \
- MachineHolder(self.build.environment.machines.target)
+ OBJ.MachineHolder(self.build.environment.machines.target, self)
# TODO: Why is this in interpreter.py and not CoreData or Environment?
def get_non_matching_default_options(self) -> T.Iterator[T.Tuple[str, str, coredata.UserOption]]:
@@ -386,45 +388,70 @@ class Interpreter(InterpreterBase):
if 'MESON_UNIT_TEST' in os.environ:
self.funcs.update({'exception': self.func_exception})
- def holderify(self, item):
- if isinstance(item, list):
- return [self.holderify(x) for x in item]
- if isinstance(item, dict):
- return {k: self.holderify(v) for k, v in item.items()}
-
- if isinstance(item, build.CustomTarget):
- return CustomTargetHolder(item, self)
- elif isinstance(item, (int, str, bool, Disabler, InterpreterObject, mesonlib.File)) or item is None:
- return item
- elif isinstance(item, build.Executable):
- return ExecutableHolder(item, self)
- elif isinstance(item, build.GeneratedList):
- return GeneratedListHolder(item)
- elif isinstance(item, build.RunTarget):
- raise RuntimeError('This is not a pipe.')
- elif isinstance(item, ExecutableSerialisation):
- raise RuntimeError('Do not do this.')
- elif isinstance(item, build.Data):
- return DataHolder(item)
- elif isinstance(item, dependencies.Dependency):
- return DependencyHolder(item, self.subproject)
- elif isinstance(item, ExternalProgram):
- return ExternalProgramHolder(item, self.subproject)
- elif isinstance(item, MutableModuleObject):
- return MutableModuleObjectHolder(item, self)
- elif isinstance(item, ModuleObject):
- return ModuleObjectHolder(item, self)
- elif isinstance(item, (InterpreterObject, ObjectHolder)):
- return item
- else:
- raise InterpreterException('Module returned a value of unknown type.')
-
- def process_new_values(self, invalues):
+ def build_holder_map(self) -> None:
+ '''
+ Build a mapping of `HoldableObject` types to their corresponding
+ `ObjectHolder`s. This mapping is used in `InterpreterBase` to automatically
+ holderify all returned values from methods and functions.
+ '''
+ self.holder_map.update({
+ mesonlib.File: OBJ.FileHolder,
+ build.SharedLibrary: OBJ.SharedLibraryHolder,
+ build.StaticLibrary: OBJ.StaticLibraryHolder,
+ build.BothLibraries: OBJ.BothLibrariesHolder,
+ build.SharedModule: OBJ.SharedModuleHolder,
+ build.Executable: OBJ.ExecutableHolder,
+ build.Jar: OBJ.JarHolder,
+ build.CustomTarget: OBJ.CustomTargetHolder,
+ build.CustomTargetIndex: OBJ.CustomTargetIndexHolder,
+ build.Generator: OBJ.GeneratorHolder,
+ build.GeneratedList: OBJ.GeneratedListHolder,
+ build.ExtractedObjects: OBJ.GeneratedObjectsHolder,
+ build.RunTarget: OBJ.RunTargetHolder,
+ build.AliasTarget: OBJ.AliasTargetHolder,
+ build.Headers: OBJ.HeadersHolder,
+ build.Man: OBJ.ManHolder,
+ build.Data: OBJ.DataHolder,
+ build.InstallDir: OBJ.InstallDirHolder,
+ build.IncludeDirs: OBJ.IncludeDirsHolder,
+ compilers.RunResult: compilerOBJ.TryRunResultHolder,
+ dependencies.ExternalLibrary: OBJ.ExternalLibraryHolder,
+ coredata.UserFeatureOption: OBJ.FeatureOptionHolder,
+ })
+
+ '''
+ Build a mapping of `HoldableObject` base classes to their
+ corresponding `ObjectHolder`s. The difference to `self.holder_map`
+ is that the keys here define an upper bound instead of requireing an
+ exact match.
+
+ The mappings defined here are only used when there was no direct hit
+ found in `self.holder_map`.
+ '''
+ self.bound_holder_map.update({
+ dependencies.Dependency: OBJ.DependencyHolder,
+ ExternalProgram: OBJ.ExternalProgramHolder,
+ compilers.Compiler: compilerOBJ.CompilerHolder,
+ ModuleObject: OBJ.ModuleObjectHolder,
+ MutableModuleObject: OBJ.MutableModuleObjectHolder,
+ })
+
+ def append_holder_map(self, held_type: T.Type[mesonlib.HoldableObject], holder_type: T.Type[ObjectHolder]) -> None:
+ '''
+ Adds one additional mapping to the `holder_map`.
+
+ The intended use for this function is in the `initialize` method of
+ modules to register custom object holders.
+ '''
+ self.holder_map.update({
+ held_type: holder_type
+ })
+
+ def process_new_values(self, invalues: T.List[TYPE_var]) -> None:
invalues = listify(invalues)
for v in invalues:
- if isinstance(v, (RunTargetHolder, CustomTargetHolder, BuildTargetHolder)):
- v = v.held_object
-
+ if isinstance(v, ObjectHolder):
+ raise InterpreterException('Modules must not return ObjectHolders')
if isinstance(v, (build.BuildTarget, build.CustomTarget, build.RunTarget)):
self.add_target(v.name, v)
elif isinstance(v, list):
@@ -538,7 +565,7 @@ class Interpreter(InterpreterBase):
mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node)
modname = 'unstable_' + plainname
self.import_module(modname)
- return ModuleObjectHolder(self.modules[modname], self)
+ return self.modules[modname]
@stringArgs
@noKwargs
@@ -582,20 +609,16 @@ class Interpreter(InterpreterBase):
if not isinstance(version, str):
raise InterpreterException('Version must be a string.')
incs = self.extract_incdirs(kwargs)
- libs = unholder(extract_as_list(kwargs, 'link_with'))
- libs_whole = unholder(extract_as_list(kwargs, 'link_whole'))
+ libs = extract_as_list(kwargs, 'link_with')
+ libs_whole = extract_as_list(kwargs, 'link_whole')
sources = extract_as_list(kwargs, 'sources')
- sources = unholder(listify(self.source_strings_to_files(sources)))
- deps = unholder(extract_as_list(kwargs, 'dependencies'))
+ sources = listify(self.source_strings_to_files(sources))
+ deps = extract_as_list(kwargs, 'dependencies')
compile_args = mesonlib.stringlistify(kwargs.get('compile_args', []))
link_args = mesonlib.stringlistify(kwargs.get('link_args', []))
variables = self.extract_variables(kwargs, list_new=True)
final_deps = []
for d in deps:
- try:
- d = d.held_object
- except Exception:
- pass
if not isinstance(d, (dependencies.Dependency, dependencies.ExternalLibrary, dependencies.InternalDependency)):
raise InterpreterException('Dependencies must be external deps')
final_deps.append(d)
@@ -606,7 +629,7 @@ external dependencies (including libraries) must go to "dependencies".''')
dep = dependencies.InternalDependency(version, incs, compile_args,
link_args, libs, libs_whole, sources, final_deps,
variables)
- return DependencyHolder(dep, self.subproject)
+ return dep
@noKwargs
def func_assert(self, node, args, kwargs):
@@ -646,7 +669,11 @@ external dependencies (including libraries) must go to "dependencies".''')
def func_run_command(self, node, args, kwargs):
return self.run_command_impl(node, args, kwargs)
- def run_command_impl(self, node, args, kwargs, in_builddir=False):
+ def run_command_impl(self,
+ node: mparser.BaseNode,
+ args: T.Sequence[TYPE_nvar],
+ kwargs: TYPE_nkwargs,
+ in_builddir: bool = False) -> RunProcess:
if len(args) < 1:
raise InterpreterException('Not enough arguments')
cmd, *cargs = args
@@ -663,17 +690,16 @@ external dependencies (including libraries) must go to "dependencies".''')
m = 'must be a string, or the output of find_program(), files() '\
'or configure_file(), or a compiler object; not {!r}'
expanded_args = []
- if isinstance(cmd, ExternalProgramHolder):
- cmd = cmd.held_object
- if isinstance(cmd, build.Executable):
- progname = node.args.arguments[0].value
- msg = 'Program {!r} was overridden with the compiled executable {!r}'\
- ' and therefore cannot be used during configuration'
- raise InterpreterException(msg.format(progname, cmd.description()))
+ if isinstance(cmd, build.Executable):
+ progname = node.args.arguments[0].value
+ msg = 'Program {!r} was overridden with the compiled executable {!r}'\
+ ' and therefore cannot be used during configuration'
+ raise InterpreterException(msg.format(progname, cmd.description()))
+ if isinstance(cmd, ExternalProgram):
if not cmd.found():
raise InterpreterException(f'command {cmd.get_name()!r} not found or not executable')
- elif isinstance(cmd, CompilerHolder):
- exelist = cmd.compiler.get_exelist()
+ elif isinstance(cmd, compilers.Compiler):
+ exelist = cmd.get_exelist()
cmd = exelist[0]
prog = ExternalProgram(cmd, silent=True)
if not prog.found():
@@ -698,8 +724,8 @@ external dependencies (including libraries) must go to "dependencies".''')
expanded_args.append(a)
elif isinstance(a, mesonlib.File):
expanded_args.append(a.absolute_path(srcdir, builddir))
- elif isinstance(a, ExternalProgramHolder):
- expanded_args.append(a.held_object.get_path())
+ elif isinstance(a, ExternalProgram):
+ expanded_args.append(a.get_path())
else:
raise InterpreterException('Arguments ' + m.format(a))
# If any file that was used as an argument to the command
@@ -727,10 +753,16 @@ external dependencies (including libraries) must go to "dependencies".''')
if len(args) != 1:
raise InterpreterException('Subproject takes exactly one argument')
subp_name = args[0]
- return self.do_subproject(subp_name, 'meson', kwargs)
+ subp = self.do_subproject(subp_name, 'meson', kwargs)
+ # Update the holder maps from the subproject. Additional entries to the
+ # holder maps can be added through imported Meson modules
+ if isinstance(subp.held_object, Interpreter):
+ self.holder_map.update(subp.held_object.holder_map)
+ self.bound_holder_map.update(subp.held_object.bound_holder_map)
+ return subp
def disabled_subproject(self, subp_name, disabled_feature=None, exception=None):
- sub = SubprojectHolder(None, os.path.join(self.subproject_dir, subp_name),
+ sub = SubprojectHolder(NullSubprojectInterpreter(), os.path.join(self.subproject_dir, subp_name),
disabled_feature=disabled_feature, exception=exception)
self.subprojects[subp_name] = sub
self.coredata.initialized_subprojects.add(subp_name)
@@ -815,6 +847,8 @@ external dependencies (including libraries) must go to "dependencies".''')
subi = Interpreter(new_build, self.backend, subp_name, subdir, self.subproject_dir,
self.modules, default_options, ast=ast, is_translated=is_translated)
subi.subprojects = self.subprojects
+ subi.holder_map.update(self.holder_map)
+ subi.bound_holder_map.update(self.bound_holder_map)
subi.subproject_stack = self.subproject_stack + [subp_name]
current_active = self.active_projectname
@@ -853,7 +887,7 @@ external dependencies (including libraries) must go to "dependencies".''')
prefix = self.coredata.options[OptionKey('prefix')].value
from ..modules.cmake import CMakeSubprojectOptions
- options = unholder(kwargs.get('options', CMakeSubprojectOptions()))
+ options = kwargs.get('options', CMakeSubprojectOptions())
if not isinstance(options, CMakeSubprojectOptions):
raise InterpreterException('"options" kwarg must be CMakeSubprojectOptions'
' object (created by cmake.subproject_options())')
@@ -937,7 +971,8 @@ external dependencies (including libraries) must go to "dependencies".''')
'options of other subprojects.')
opt = self.get_option_internal(optname)
if isinstance(opt, coredata.UserFeatureOption):
- return FeatureOptionHolder(self.environment, optname, opt)
+ opt.name = optname
+ return opt
elif isinstance(opt, coredata.UserOption):
return opt.value
return opt
@@ -953,7 +988,7 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('configuration_data first argument must be a dictionary')
else:
initial_values = {}
- return ConfigurationDataHolder(self.subproject, initial_values)
+ return ConfigurationDataObject(self.subproject, initial_values)
def set_backend(self):
# The backend is already set when parsing subprojects
@@ -1263,14 +1298,14 @@ external dependencies (including libraries) must go to "dependencies".''')
return success
def program_from_file_for(self, for_machine, prognames):
- for p in unholder(prognames):
+ for p in prognames:
if isinstance(p, mesonlib.File):
continue # Always points to a local (i.e. self generated) file.
if not isinstance(p, str):
raise InterpreterException('Executable name must be a string')
prog = ExternalProgram.from_bin_list(self.environment, for_machine, p)
if prog.found():
- return ExternalProgramHolder(prog, self.subproject)
+ return prog
return None
def program_from_system(self, args, search_dirs, extra_info):
@@ -1297,10 +1332,9 @@ external dependencies (including libraries) must go to "dependencies".''')
extprog = ExternalProgram(exename, search_dir=search_dir,
extra_search_dirs=extra_search_dirs,
silent=True)
- progobj = ExternalProgramHolder(extprog, self.subproject)
- if progobj.found():
- extra_info.append(f"({' '.join(progobj.get_command())})")
- return progobj
+ if extprog.found():
+ extra_info.append(f"({' '.join(extprog.get_command())})")
+ return extprog
def program_from_overrides(self, command_names, extra_info):
for name in command_names:
@@ -1309,7 +1343,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if name in self.build.find_overrides:
exe = self.build.find_overrides[name]
extra_info.append(mlog.blue('(overridden)'))
- return ExternalProgramHolder(exe, self.subproject, self.backend)
+ return exe
return None
def store_name_lookups(self, command_names):
@@ -1327,7 +1361,7 @@ external dependencies (including libraries) must go to "dependencies".''')
self.build.find_overrides[name] = exe
def notfound_program(self, args):
- return ExternalProgramHolder(NonExistingExternalProgram(' '.join(args)), self.subproject)
+ return NonExistingExternalProgram(' '.join(args))
# TODO update modules to always pass `for_machine`. It is bad-form to assume
# the host machine.
@@ -1341,7 +1375,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if progobj is None:
progobj = self.notfound_program(args)
- if not progobj.found():
+ if isinstance(progobj, ExternalProgram) and not progobj.found():
mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'))
if required:
m = 'Program {!r} not found'
@@ -1351,22 +1385,28 @@ external dependencies (including libraries) must go to "dependencies".''')
if wanted:
if version_func:
version = version_func(progobj)
- else:
+ elif isinstance(progobj, build.Executable):
+ interp = self
+ if progobj.subproject:
+ interp = self.subprojects[progobj.subproject].held_object
+ assert isinstance(interp, Interpreter)
+ version = interp.project_version
+ elif isinstance(progobj, ExternalProgram):
version = progobj.get_version(self)
is_found, not_found, found = mesonlib.version_compare_many(version, wanted)
if not is_found:
- mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.red('NO'),
+ mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.red('NO'),
'found', mlog.normal_cyan(version), 'but need:',
mlog.bold(', '.join([f"'{e}'" for e in not_found])), *extra_info)
if required:
m = 'Invalid version of program, need {!r} {!r} found {!r}.'
- raise InterpreterException(m.format(progobj.get_name(), not_found, version))
+ raise InterpreterException(m.format(progobj.name, not_found, version))
return self.notfound_program(args)
extra_info.insert(0, mlog.normal_cyan(version))
# Only store successful lookups
self.store_name_lookups(args)
- mlog.log('Program', mlog.bold(progobj.get_name()), 'found:', mlog.green('YES'), *extra_info)
+ mlog.log('Program', mlog.bold(progobj.name), 'found:', mlog.green('YES'), *extra_info)
return progobj
def program_lookup(self, args, for_machine, required, search_dirs, extra_info):
@@ -1386,7 +1426,7 @@ external dependencies (including libraries) must go to "dependencies".''')
progobj = self.program_from_system(args, search_dirs, extra_info)
if progobj is None and args[0].endswith('python3'):
prog = ExternalProgram('python3', mesonlib.python_command, silent=True)
- progobj = ExternalProgramHolder(prog, self.subproject) if prog.found() else None
+ progobj = prog if prog.found() else None
if progobj is None and fallback and required:
progobj = self.find_program_fallback(fallback, args, required, extra_info)
@@ -1457,17 +1497,19 @@ external dependencies (including libraries) must go to "dependencies".''')
if not_found_message:
self.message_impl([not_found_message])
raise
- assert isinstance(d, DependencyHolder)
+ assert isinstance(d, Dependency)
if not d.found() and not_found_message:
self.message_impl([not_found_message])
self.message_impl([not_found_message])
# Ensure the correct include type
if 'include_type' in kwargs:
wanted = kwargs['include_type']
- actual = d.include_type_method([], {})
+ if not isinstance(wanted, str):
+ raise InvalidArguments('The `include_type` kwarg must be a string')
+ actual = d.get_include_type()
if wanted != actual:
mlog.debug(f'Current include type of {names[0]} is {actual}. Converting to requested {wanted}')
- d = d.as_system_method([wanted], {})
+ d = d.generate_system_dependency(wanted)
return d
@FeatureNew('disabler', '0.44.0')
@@ -1481,16 +1523,16 @@ external dependencies (including libraries) must go to "dependencies".''')
@FeatureDeprecatedKwargs('executable', '0.56.0', ['gui_app'], extra_message="Use 'win_subsystem' instead.")
@permittedKwargs(build.known_exe_kwargs)
def func_executable(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, ExecutableHolder)
+ return self.build_target(node, args, kwargs, build.Executable)
@permittedKwargs(build.known_stlib_kwargs)
def func_static_lib(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
+ return self.build_target(node, args, kwargs, build.StaticLibrary)
@permittedKwargs(build.known_shlib_kwargs)
def func_shared_lib(self, node, args, kwargs):
- holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
- holder.held_object.shared_library_only = True
+ holder = self.build_target(node, args, kwargs, build.SharedLibrary)
+ holder.shared_library_only = True
return holder
@permittedKwargs(known_library_kwargs)
@@ -1500,7 +1542,7 @@ external dependencies (including libraries) must go to "dependencies".''')
@FeatureNew('shared_module', '0.37.0')
@permittedKwargs(build.known_shmod_kwargs)
def func_shared_module(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, SharedModuleHolder)
+ return self.build_target(node, args, kwargs, build.SharedModule)
@permittedKwargs(known_library_kwargs)
def func_library(self, node, args, kwargs):
@@ -1508,7 +1550,7 @@ external dependencies (including libraries) must go to "dependencies".''')
@permittedKwargs(build.known_jar_kwargs)
def func_jar(self, node, args, kwargs):
- return self.build_target(node, args, kwargs, JarHolder)
+ return self.build_target(node, args, kwargs, build.Jar)
@FeatureNewKwargs('build_target', '0.40.0', ['link_whole', 'override_options'])
@permittedKwargs(known_build_target_kwargs)
@@ -1517,21 +1559,21 @@ external dependencies (including libraries) must go to "dependencies".''')
raise InterpreterException('Missing target_type keyword argument')
target_type = kwargs.pop('target_type')
if target_type == 'executable':
- return self.build_target(node, args, kwargs, ExecutableHolder)
+ return self.build_target(node, args, kwargs, build.Executable)
elif target_type == 'shared_library':
- return self.build_target(node, args, kwargs, SharedLibraryHolder)
+ return self.build_target(node, args, kwargs, build.SharedLibrary)
elif target_type == 'shared_module':
FeatureNew('build_target(target_type: \'shared_module\')',
'0.51.0').use(self.subproject)
- return self.build_target(node, args, kwargs, SharedModuleHolder)
+ return self.build_target(node, args, kwargs, build.SharedModule)
elif target_type == 'static_library':
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
+ return self.build_target(node, args, kwargs, build.StaticLibrary)
elif target_type == 'both_libraries':
return self.build_both_libraries(node, args, kwargs)
elif target_type == 'library':
return self.build_library(node, args, kwargs)
elif target_type == 'jar':
- return self.build_target(node, args, kwargs, JarHolder)
+ return self.build_target(node, args, kwargs, build.Jar)
else:
raise InterpreterException('Unknown target_type.')
@@ -1613,8 +1655,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if 'command' in kwargs and isinstance(kwargs['command'], list) and kwargs['command']:
if isinstance(kwargs['command'][0], str):
kwargs['command'][0] = self.func_find_program(node, kwargs['command'][0], {})
- tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend), self)
- self.add_target(name, tg.held_object)
+ tg = build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend)
+ self.add_target(name, tg)
return tg
@FeatureNewKwargs('run_target', '0.57.0', ['env'])
@@ -1626,12 +1668,12 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if 'command' not in kwargs:
raise InterpreterException('Missing "command" keyword argument')
all_args = extract_as_list(kwargs, 'command')
- deps = unholder(extract_as_list(kwargs, 'depends'))
+ deps = extract_as_list(kwargs, 'depends')
else:
raise InterpreterException('Run_target needs at least one positional argument.')
cleaned_args = []
- for i in unholder(listify(all_args)):
+ for i in listify(all_args):
if not isinstance(i, (str, build.BuildTarget, build.CustomTarget, ExternalProgram, mesonlib.File)):
mlog.debug('Wrong type:', str(i))
raise InterpreterException('Invalid argument to run_target.')
@@ -1649,8 +1691,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
raise InterpreterException('Depends items must be build targets.')
cleaned_deps.append(d)
env = self.unpack_env_kwarg(kwargs)
- tg = RunTargetHolder(build.RunTarget(name, cleaned_args, cleaned_deps, self.subdir, self.subproject, env), self)
- self.add_target(name, tg.held_object)
+ tg = build.RunTarget(name, cleaned_args, cleaned_deps, self.subdir, self.subproject, env)
+ self.add_target(name, tg)
full_name = (self.subproject, name)
assert(full_name not in self.build.run_target_names)
self.build.run_target_names.add(full_name)
@@ -1664,28 +1706,28 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
name = args[0]
if not isinstance(name, str):
raise InterpreterException('First argument must be a string.')
- deps = unholder(listify(args[1:]))
+ deps = listify(args[1:])
for d in deps:
if not isinstance(d, (build.BuildTarget, build.CustomTarget)):
raise InterpreterException('Depends items must be build targets.')
- tg = RunTargetHolder(build.AliasTarget(name, deps, self.subdir, self.subproject), self)
- self.add_target(name, tg.held_object)
+ tg = build.AliasTarget(name, deps, self.subdir, self.subproject)
+ self.add_target(name, tg)
return tg
@permittedKwargs({'arguments', 'output', 'depends', 'depfile', 'capture',
'preserve_path_from'})
- @typed_pos_args('generator', (ExecutableHolder, ExternalProgramHolder))
+ @typed_pos_args('generator', (build.Executable, ExternalProgram))
@typed_kwargs(
'generator',
KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True),
KwargInfo('output', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True),
KwargInfo('depfile', str, validator=lambda x: 'Depfile must be a plain filename with a subdirectory' if has_path_sep(x) else None),
KwargInfo('capture', bool, default=False, since='0.43.0'),
- KwargInfo('depends', ContainerTypeInfo(list, (BuildTargetHolder, CustomTargetHolder)), default=[], listify=True),
+ KwargInfo('depends', ContainerTypeInfo(list, (build.BuildTarget, build.CustomTarget)), default=[], listify=True),
)
def func_generator(self, node: mparser.FunctionNode,
- args: T.Tuple[T.Union[ExecutableHolder, ExternalProgramHolder]],
- kwargs: 'kwargs.FuncGenerator') -> GeneratorHolder:
+ args: T.Tuple[T.Union[build.Executable, ExternalProgram]],
+ kwargs: 'kwargs.FuncGenerator') -> build.Generator:
for rule in kwargs['output']:
if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule:
raise InvalidArguments('Every element of "output" must contain @BASENAME@ or @PLAINNAME@.')
@@ -1696,43 +1738,40 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if '@OUTPUT@' in o:
raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.')
- depends = [d.held_object for d in kwargs.pop('depends')]
-
- gen = build.Generator(args[0].held_object, depends=depends, **kwargs)
- holder = GeneratorHolder(gen, self)
- self.generators.append(holder)
- return holder
+ gen = build.Generator(args[0], **kwargs)
+ self.generators.append(gen)
+ return gen
- @typed_pos_args('benchmark', str, (ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File))
+ @typed_pos_args('benchmark', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File))
@typed_kwargs('benchmark', *TEST_KWARGS)
def func_benchmark(self, node: mparser.BaseNode,
- args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]],
+ args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]],
kwargs: 'kwargs.FuncBenchmark') -> None:
self.add_test(node, args, kwargs, False)
- @typed_pos_args('test', str, (ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File))
+ @typed_pos_args('test', str, (build.Executable, build.Jar, ExternalProgram, mesonlib.File))
@typed_kwargs('benchmark', *TEST_KWARGS, KwargInfo('is_parallel', bool, default=True))
def func_test(self, node: mparser.BaseNode,
- args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]],
+ args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]],
kwargs: 'kwargs.FuncTest') -> None:
self.add_test(node, args, kwargs, True)
- def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesHolder, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables:
- envlist = kwargs.get('env', EnvironmentVariablesHolder())
- if isinstance(envlist, EnvironmentVariablesHolder):
- env = envlist.held_object
+ def unpack_env_kwarg(self, kwargs: T.Union[EnvironmentVariablesObject, T.Dict[str, str], T.List[str]]) -> build.EnvironmentVariables:
+ envlist = kwargs.get('env', EnvironmentVariablesObject())
+ if isinstance(envlist, EnvironmentVariablesObject):
+ env = envlist.vars
elif isinstance(envlist, dict):
FeatureNew.single_use('environment dictionary', '0.52.0', self.subproject)
- env = EnvironmentVariablesHolder(envlist)
- env = env.held_object
+ env = EnvironmentVariablesObject(envlist)
+ env = env.vars
else:
# Convert from array to environment object
- env = EnvironmentVariablesHolder(envlist)
- env = env.held_object
+ env = EnvironmentVariablesObject(envlist)
+ env = env.vars
return env
def make_test(self, node: mparser.BaseNode,
- args: T.Tuple[str, T.Union[ExecutableHolder, JarHolder, ExternalProgramHolder, mesonlib.File]],
+ args: T.Tuple[str, T.Union[build.Executable, build.Jar, ExternalProgram, mesonlib.File]],
kwargs: 'kwargs.BaseTest') -> Test:
name = args[0]
if ':' in name:
@@ -1759,10 +1798,10 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
return Test(name,
prj,
suite,
- exe.held_object,
- [d.held_object for d in kwargs['depends']],
+ exe,
+ kwargs['depends'],
kwargs.get('is_parallel', False),
- [c.held_object if isinstance(c, ObjectHolder) else c for c in kwargs['args']],
+ kwargs['args'],
env,
kwargs['should_fail'],
kwargs['timeout'],
@@ -1798,7 +1837,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
h = build.Headers(source_files, install_subdir, install_dir, install_mode, self.subproject)
self.build.headers.append(h)
- return HeadersHolder(h)
+ return h
@FeatureNewKwargs('install_man', '0.47.0', ['install_mode'])
@FeatureNewKwargs('install_man', '0.58.0', ['locale'])
@@ -1821,7 +1860,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
m = build.Man(sources, custom_install_dir, custom_install_mode, self.subproject, locale)
self.build.man.append(m)
- return ManHolder(m)
+ return m
@FeatureNewKwargs('subdir', '0.44.0', ['if_found'])
@permittedKwargs({'if_found'})
@@ -1835,9 +1874,9 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
if self.subdir == '' and args[0].startswith('meson-'):
raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().')
for i in mesonlib.extract_as_list(kwargs, 'if_found'):
- if not hasattr(i, 'found_method'):
+ if not hasattr(i, 'found'):
raise InterpreterException('Object used in if_found does not have a found method.')
- if not i.found_method([], {}):
+ if not i.found():
return
prev_subdir = self.subdir
subdir = os.path.join(prev_subdir, args[0])
@@ -1919,8 +1958,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
'"rename" and "sources" argument lists must be the same length if "rename" is given. '
f'Rename has {len(rename)} elements and sources has {len(sources)}.')
- data = DataHolder(build.Data(sources, install_dir, install_mode, self.subproject, rename))
- self.build.data.append(data.held_object)
+ data = build.Data(sources, install_dir, install_mode, self.subproject, rename)
+ self.build.data.append(data)
return data
@FeatureNewKwargs('install_subdir', '0.42.0', ['exclude_files', 'exclude_directories'])
@@ -1968,7 +2007,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
install_mode = self._get_kwarg_install_mode(kwargs)
idir = build.InstallDir(self.subdir, subdir, install_dir, install_mode, exclude, strip_directory, self.subproject)
self.build.install_dirs.append(idir)
- return InstallDirHolder(idir)
+ return idir
@FeatureNewKwargs('configure_file', '0.47.0', ['copy', 'output_format', 'install_mode', 'encoding'])
@FeatureNewKwargs('configure_file', '0.46.0', ['format'])
@@ -2065,8 +2104,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
conf = kwargs['configuration']
if isinstance(conf, dict):
FeatureNew.single_use('configure_file.configuration dictionary', '0.49.0', self.subproject)
- conf = ConfigurationDataHolder(self.subproject, conf)
- elif not isinstance(conf, ConfigurationDataHolder):
+ conf = ConfigurationDataObject(self.subproject, conf)
+ elif not isinstance(conf, ConfigurationDataObject):
raise InterpreterException('Argument "configuration" is not of type configuration_data')
mlog.log('Configuring', mlog.bold(output), 'using configuration')
if len(inputs) > 1:
@@ -2075,7 +2114,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True)
file_encoding = kwargs.setdefault('encoding', 'utf-8')
missing_variables, confdata_useless = \
- mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf.held_object,
+ mesonlib.do_conf_file(inputs_abs[0], ofile_abs, conf.conf_data,
fmt, file_encoding)
if missing_variables:
var_list = ", ".join(map(repr, sorted(missing_variables)))
@@ -2090,7 +2129,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
'copy a file to the build dir, use the \'copy:\' keyword '
'argument added in 0.47.0'.format(ifbase), location=node)
else:
- mesonlib.dump_conf_header(ofile_abs, conf.held_object, output_format)
+ mesonlib.dump_conf_header(ofile_abs, conf.conf_data, output_format)
conf.mark_used()
elif 'command' in kwargs:
if len(inputs) > 1:
@@ -2162,13 +2201,13 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
return mesonlib.File.from_built_file(self.subdir, output)
def extract_incdirs(self, kwargs):
- prospectives = unholder(extract_as_list(kwargs, 'include_directories'))
+ prospectives = extract_as_list(kwargs, 'include_directories')
result = []
for p in prospectives:
if isinstance(p, build.IncludeDirs):
result.append(p)
elif isinstance(p, str):
- result.append(self.build_incdir_object([p]).held_object)
+ result.append(self.build_incdir_object([p]))
else:
raise InterpreterException('Include directory objects can only be created from strings or include directories.')
return result
@@ -2178,7 +2217,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
def func_include_directories(self, node, args, kwargs):
return self.build_incdir_object(args, kwargs.get('is_system', False))
- def build_incdir_object(self, incdir_strings, is_system=False):
+ def build_incdir_object(self, incdir_strings: T.List[str], is_system: bool = False) -> build.IncludeDirs:
if not isinstance(is_system, bool):
raise InvalidArguments('Is_system must be boolean.')
src_root = self.environment.get_source_dir()
@@ -2188,51 +2227,52 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
for a in incdir_strings:
if a.startswith(src_root):
- raise InvalidArguments('Tried to form an absolute path to a source dir. '
- 'You should not do that but use relative paths instead.'
- '''
+ raise InvalidArguments(textwrap.dedent('''\
+ Tried to form an absolute path to a source dir.
+ You should not do that but use relative paths instead.
-To get include path to any directory relative to the current dir do
+ To get include path to any directory relative to the current dir do
-incdir = include_directories(dirname)
+ incdir = include_directories(dirname)
-After this incdir will contain both the current source dir as well as the
-corresponding build dir. It can then be used in any subdirectory and
-Meson will take care of all the busywork to make paths work.
+ After this incdir will contain both the current source dir as well as the
+ corresponding build dir. It can then be used in any subdirectory and
+ Meson will take care of all the busywork to make paths work.
-Dirname can even be '.' to mark the current directory. Though you should
-remember that the current source and build directories are always
-put in the include directories by default so you only need to do
-include_directories('.') if you intend to use the result in a
-different subdirectory.
-''')
+ Dirname can even be '.' to mark the current directory. Though you should
+ remember that the current source and build directories are always
+ put in the include directories by default so you only need to do
+ include_directories('.') if you intend to use the result in a
+ different subdirectory.
+ '''))
else:
try:
self.validate_within_subproject(self.subdir, a)
except InterpreterException:
mlog.warning('include_directories sandbox violation!')
- print(f'''The project is trying to access the directory {a} which belongs to a different
-subproject. This is a problem as it hardcodes the relative paths of these two projeccts.
-This makes it impossible to compile the project in any other directory layout and also
-prevents the subproject from changing its own directory layout.
+ print(textwrap.dedent(f'''\
+ The project is trying to access the directory {a} which belongs to a different
+ subproject. This is a problem as it hardcodes the relative paths of these two projeccts.
+ This makes it impossible to compile the project in any other directory layout and also
+ prevents the subproject from changing its own directory layout.
-Instead of poking directly at the internals the subproject should be executed and
-it should set a variable that the caller can then use. Something like:
+ Instead of poking directly at the internals the subproject should be executed and
+ it should set a variable that the caller can then use. Something like:
-# In subproject
-some_dep = declare_depencency(include_directories: include_directories('include'))
+ # In subproject
+ some_dep = declare_depencency(include_directories: include_directories('include'))
-# In parent project
-some_dep = depencency('some')
-executable(..., dependencies: [some_dep])
+ # In parent project
+ some_dep = depencency('some')
+ executable(..., dependencies: [some_dep])
-This warning will become a hard error in a future Meson release.
-''')
+ This warning will become a hard error in a future Meson release.
+ '''))
absdir_src = os.path.join(absbase_src, a)
absdir_build = os.path.join(absbase_build, a)
if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build):
raise InvalidArguments('Include dir %s does not exist.' % a)
- i = IncludeDirsHolder(build.IncludeDirs(self.subdir, incdir_strings, is_system))
+ i = build.IncludeDirs(self.subdir, incdir_strings, is_system)
return i
@permittedKwargs({'exe_wrapper', 'gdb', 'timeout_multiplier', 'env', 'is_default',
@@ -2247,7 +2287,7 @@ This warning will become a hard error in a future Meson release.
if ":" not in setup_name:
setup_name = (self.subproject if self.subproject else self.build.project_name) + ":" + setup_name
try:
- inp = unholder(extract_as_list(kwargs, 'exe_wrapper'))
+ inp = extract_as_list(kwargs, 'exe_wrapper')
exe_wrapper = []
for i in inp:
if isinstance(i, str):
@@ -2292,7 +2332,7 @@ This warning will become a hard error in a future Meson release.
self._add_global_arguments(node, self.build.global_link_args[kwargs['native']], args[0], kwargs)
@typed_pos_args('add_project_arguments', varargs=str)
- @typed_kwargs('add_global_arguments', _NATIVE_KW, _LANGUAGE_KW)
+ @typed_kwargs('add_project_arguments', _NATIVE_KW, _LANGUAGE_KW)
def func_add_project_arguments(self, node: mparser.FunctionNode, args: T.Tuple[T.List[str]], kwargs: 'kwargs.FuncAddProjectArgs') -> None:
self._add_project_arguments(node, self.build.projects_args[kwargs['native']], args[0], kwargs)
@@ -2373,7 +2413,7 @@ This warning will become a hard error in a future Meson release.
raise InterpreterException('environment first argument must be a dictionary or a list')
else:
initial_values = {}
- return EnvironmentVariablesHolder(initial_values, self.subproject)
+ return EnvironmentVariablesObject(initial_values, self.subproject)
@stringArgs
@noKwargs
@@ -2458,10 +2498,10 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
results.append(mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, s))
elif isinstance(s, mesonlib.File):
results.append(s)
- elif isinstance(s, (GeneratedListHolder, TargetHolder,
- CustomTargetIndexHolder,
- GeneratedObjectsHolder)):
- results.append(unholder(s))
+ elif isinstance(s, (build.GeneratedList, build.BuildTarget,
+ build.CustomTargetIndex, build.CustomTarget,
+ build.GeneratedList)):
+ results.append(s)
else:
raise InterpreterException(f'Source item is {s!r} instead of '
'string or File-type object')
@@ -2489,7 +2529,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
@FeatureNew('both_libraries', '0.46.0')
def build_both_libraries(self, node, args, kwargs):
- shared_holder = self.build_target(node, args, kwargs, SharedLibraryHolder)
+ shared_lib = self.build_target(node, args, kwargs, build.SharedLibrary)
# Check if user forces non-PIC static library.
pic = True
@@ -2515,27 +2555,27 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
static_args = [args[0]]
static_kwargs = kwargs.copy()
static_kwargs['sources'] = []
- static_kwargs['objects'] = shared_holder.held_object.extract_all_objects()
+ static_kwargs['objects'] = shared_lib.extract_all_objects()
else:
static_args = args
static_kwargs = kwargs
- static_holder = self.build_target(node, static_args, static_kwargs, StaticLibraryHolder)
+ static_lib = self.build_target(node, static_args, static_kwargs, build.StaticLibrary)
- return BothLibrariesHolder(shared_holder, static_holder, self)
+ return build.BothLibraries(shared_lib, static_lib)
def build_library(self, node, args, kwargs):
default_library = self.coredata.get_option(OptionKey('default_library', subproject=self.subproject))
if default_library == 'shared':
- return self.build_target(node, args, kwargs, SharedLibraryHolder)
+ return self.build_target(node, args, kwargs, build.SharedLibrary)
elif default_library == 'static':
- return self.build_target(node, args, kwargs, StaticLibraryHolder)
+ return self.build_target(node, args, kwargs, build.StaticLibrary)
elif default_library == 'both':
return self.build_both_libraries(node, args, kwargs)
else:
raise InterpreterException('Unknown default_library value: %s.', default_library)
- def build_target(self, node, args, kwargs, targetholder):
+ def build_target(self, node, args, kwargs, targetclass):
@FeatureNewKwargs('build target', '0.42.0', ['rust_crate_type', 'build_rpath', 'implicit_include_directories'])
@FeatureNewKwargs('build target', '0.41.0', ['rust_args'])
@FeatureNewKwargs('build target', '0.40.0', ['build_by_default'])
@@ -2559,18 +2599,8 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
ef = extract_as_list(kwargs, 'extra_files')
kwargs['extra_files'] = self.source_strings_to_files(ef)
self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources)
- if targetholder == ExecutableHolder:
- targetclass = build.Executable
- elif targetholder == SharedLibraryHolder:
- targetclass = build.SharedLibrary
- elif targetholder == SharedModuleHolder:
- targetclass = build.SharedModule
- elif targetholder == StaticLibraryHolder:
- targetclass = build.StaticLibrary
- elif targetholder == JarHolder:
- targetclass = build.Jar
- else:
- mlog.debug('Unknown target type:', str(targetholder))
+ if targetclass not in {build.Executable, build.SharedLibrary, build.SharedModule, build.StaticLibrary, build.Jar}:
+ mlog.debug('Unknown target type:', str(targetclass))
raise RuntimeError('Unreachable code')
self.kwarg_strings_to_includedirs(kwargs)
@@ -2583,10 +2613,9 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
target.project_version = self.project_version
self.add_stdlib_info(target)
- l = targetholder(target, self)
- self.add_target(name, l.held_object)
+ self.add_target(name, target)
self.project_args_frozen = True
- return l
+ return target
def kwarg_strings_to_includedirs(self, kwargs):
if 'd_import_dirs' in kwargs:
@@ -2646,6 +2675,7 @@ This will become a hard error in the future.''', location=self.current_node)
@noKwargs
@noArgsFlattening
+ @unholder_return
def func_get_variable(self, node, args, kwargs):
if len(args) < 1 or len(args) > 2:
raise InvalidCode('Get_variable takes one or two arguments.')
diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py
index 7b59a24..744f69c 100644
--- a/mesonbuild/interpreter/interpreterobjects.py
+++ b/mesonbuild/interpreter/interpreterobjects.py
@@ -1,8 +1,8 @@
import os
import shlex
import subprocess
-import re
import copy
+import textwrap
from pathlib import Path, PurePath
@@ -13,42 +13,42 @@ from .. import mlog
from ..modules import ModuleReturnValue, ModuleObject, ModuleState, ExtensionModule
from ..backend.backends import TestProtocol
-from ..interpreterbase import (ContainerTypeInfo, InterpreterObject, KwargInfo,
- ObjectHolder, MutableInterpreterObject,
- FeatureNewKwargs, FeatureNew, FeatureDeprecated,
- typed_kwargs, typed_pos_args, stringArgs,
- permittedKwargs, noArgsFlattening, noPosargs,
- TYPE_var, TYPE_nkwargs, flatten,
- InterpreterException, InvalidArguments,
- InvalidCode)
-from ..interpreterbase.decorators import FeatureCheckBase
+from ..interpreterbase import (
+ ContainerTypeInfo, KwargInfo,
+ InterpreterObject, MesonInterpreterObject, ObjectHolder, MutableInterpreterObject,
+ FeatureCheckBase, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
+ typed_pos_args, typed_kwargs, KwargInfo, stringArgs, permittedKwargs,
+ noArgsFlattening, noPosargs, noKwargs, unholder_return, TYPE_var, TYPE_kwargs, TYPE_nvar, TYPE_nkwargs,
+ flatten, InterpreterException, InvalidArguments, InvalidCode)
from ..dependencies import Dependency, ExternalLibrary, InternalDependency
from ..programs import ExternalProgram
-from ..mesonlib import FileMode, OptionKey, listify, Popen_safe
+from ..mesonlib import HoldableObject, MesonException, OptionKey, listify, Popen_safe
import typing as T
if T.TYPE_CHECKING:
from . import kwargs
from .interpreter import Interpreter
+ from ..environment import Environment
+ from ..envconfig import MachineInfo
-def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str,
- feature_check: T.Optional['FeatureCheckBase'] = None,
+def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
+ subproject: str,
+ feature_check: T.Optional[FeatureCheckBase] = None,
default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]:
val = kwargs.get('required', default)
disabled = False
required = False
feature: T.Optional[str] = None
- if isinstance(val, FeatureOptionHolder):
+ if isinstance(val, coredata.UserFeatureOption):
if not feature_check:
feature_check = FeatureNew('User option "feature"', '0.47.0')
feature_check.use(subproject)
- option = val.held_object
feature = val.name
- if option.is_disabled():
+ if val.is_disabled():
disabled = True
- elif option.is_enabled():
+ elif val.is_enabled():
required = True
elif isinstance(val, bool):
required = val
@@ -62,9 +62,9 @@ def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', subproject: str,
return disabled, required, feature
-def extract_search_dirs(kwargs):
- search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
- search_dirs = [Path(d).expanduser() for d in search_dirs]
+def extract_search_dirs(kwargs: T.Dict[str, T.Any]) -> T.List[str]:
+ search_dirs_str = mesonlib.stringlistify(kwargs.get('dirs', []))
+ search_dirs = [Path(d).expanduser() for d in search_dirs_str]
for d in search_dirs:
if mesonlib.is_windows() and d.root.startswith('\\'):
# a Unix-path starting with `/` that is not absolute on Windows.
@@ -74,14 +74,13 @@ def extract_search_dirs(kwargs):
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)
+class FeatureOptionHolder(ObjectHolder[coredata.UserFeatureOption]):
+ def __init__(self, option: coredata.UserFeatureOption, interpreter: 'Interpreter'):
+ super().__init__(option, interpreter)
if option and option.is_auto():
# TODO: we need to case here because options is not a TypedDict
- self.held_object = T.cast(coredata.UserFeatureOption, env.coredata.options[OptionKey('auto_features')])
- self.name = name
+ self.held_object = T.cast(coredata.UserFeatureOption, self.env.coredata.options[OptionKey('auto_features')])
+ self.held_object.name = option.name
self.methods.update({'enabled': self.enabled_method,
'disabled': self.disabled_method,
'allowed': self.allowed_method,
@@ -91,34 +90,36 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOp
})
@property
- def value(self):
+ def value(self) -> str:
return 'disabled' if not self.held_object else self.held_object.value
- def as_disabled(self):
- return FeatureOptionHolder(None, self.name, None)
+ def as_disabled(self) -> coredata.UserFeatureOption:
+ disabled = copy.deepcopy(self.held_object)
+ disabled.value = 'disabled'
+ return disabled
@noPosargs
- @permittedKwargs({})
- def enabled_method(self, args, kwargs):
+ @noKwargs
+ def enabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'enabled'
@noPosargs
- @permittedKwargs({})
- def disabled_method(self, args, kwargs):
+ @noKwargs
+ def disabled_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'disabled'
@noPosargs
- @permittedKwargs({})
- def allowed_method(self, args, kwargs):
+ @noKwargs
+ def allowed_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return not self.value == 'disabled'
@noPosargs
- @permittedKwargs({})
- def auto_method(self, args, kwargs):
+ @noKwargs
+ def auto_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.value == 'auto'
@permittedKwargs({'error_message'})
- def require_method(self, args, kwargs):
+ def require_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
if len(args) != 1:
raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), ))
if not isinstance(args[0], bool):
@@ -127,38 +128,57 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder[coredata.UserFeatureOp
if error_message and not isinstance(error_message, str):
raise InterpreterException("Error message must be a string.")
if args[0]:
- return self
+ return copy.deepcopy(self.held_object)
+ assert isinstance(error_message, str)
if self.value == 'enabled':
- prefix = 'Feature {} cannot be enabled'.format(self.name)
+ prefix = 'Feature {} cannot be enabled'.format(self.held_object.name)
prefix = prefix + ': ' if error_message else ''
raise InterpreterException(prefix + error_message)
return self.as_disabled()
- @permittedKwargs({})
- def disable_auto_if_method(self, args, kwargs):
+ @noKwargs
+ def disable_auto_if_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> coredata.UserFeatureOption:
if len(args) != 1:
raise InvalidArguments('Expected 1 argument, got %d.' % (len(args), ))
if not isinstance(args[0], bool):
raise InvalidArguments('boolean argument expected.')
- return self if self.value != 'auto' or not args[0] else self.as_disabled()
-
-
-class RunProcess(InterpreterObject):
-
- def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
+ return copy.deepcopy(self.held_object) if self.value != 'auto' or not args[0] else self.as_disabled()
+
+
+class RunProcess(MesonInterpreterObject):
+
+ def __init__(self,
+ cmd: ExternalProgram,
+ args: T.List[str],
+ env: build.EnvironmentVariables,
+ source_dir: str,
+ build_dir: str,
+ subdir: str,
+ mesonintrospect: T.List[str],
+ in_builddir: bool = False,
+ check: bool = False,
+ capture: bool = True) -> None:
super().__init__()
if not isinstance(cmd, ExternalProgram):
raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
self.capture = capture
- pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
- self.returncode = pc.returncode
+ self.returncode, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
self.methods.update({'returncode': self.returncode_method,
'stdout': self.stdout_method,
'stderr': self.stderr_method,
})
- def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
+ def run_command(self,
+ cmd: ExternalProgram,
+ args: T.List[str],
+ env: build.EnvironmentVariables,
+ source_dir: str,
+ build_dir: str,
+ subdir: str,
+ mesonintrospect: T.List[str],
+ in_builddir: bool,
+ check: bool = False) -> T.Tuple[int, str, str]:
command_array = cmd.get_command() + args
menv = {'MESON_SOURCE_ROOT': source_dir,
'MESON_BUILD_ROOT': build_dir,
@@ -189,29 +209,33 @@ class RunProcess(InterpreterObject):
if check and p.returncode != 0:
raise InterpreterException('Command "{}" failed with status {}.'.format(' '.join(command_array), p.returncode))
- return p, o, e
+ return p.returncode, o, e
except FileNotFoundError:
raise InterpreterException('Could not execute command "%s".' % ' '.join(command_array))
@noPosargs
- @permittedKwargs({})
- def returncode_method(self, args, kwargs):
+ @noKwargs
+ def returncode_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> int:
return self.returncode
@noPosargs
- @permittedKwargs({})
- def stdout_method(self, args, kwargs):
+ @noKwargs
+ def stdout_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stdout
@noPosargs
- @permittedKwargs({})
- def stderr_method(self, args, kwargs):
+ @noKwargs
+ def stderr_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.stderr
-class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.EnvironmentVariables]):
- def __init__(self, initial_values=None, subproject: str = ''):
- MutableInterpreterObject.__init__(self)
- ObjectHolder.__init__(self, build.EnvironmentVariables(), subproject)
+# TODO: Parsing the initial values should be either done directly in the
+# `Interpreter` or in `build.EnvironmentVariables`. This way, this class
+# can be converted into a pure object holder.
+class EnvironmentVariablesObject(MutableInterpreterObject, MesonInterpreterObject):
+ # TODO: Move the type cheking for initial_values out of this class and replace T.Any
+ def __init__(self, initial_values: T.Optional[T.Any] = None, subproject: str = ''):
+ super().__init__(subproject=subproject)
+ self.vars = build.EnvironmentVariables()
self.methods.update({'set': self.set_method,
'append': self.append_method,
'prepend': self.prepend_method,
@@ -234,18 +258,18 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
def __repr__(self) -> str:
repr_str = "<{0}: {1}>"
- return repr_str.format(self.__class__.__name__, self.held_object.envvars)
+ return repr_str.format(self.__class__.__name__, self.vars.envvars)
def unpack_separator(self, kwargs: T.Dict[str, T.Any]) -> str:
separator = kwargs.get('separator', os.pathsep)
if not isinstance(separator, str):
- raise InterpreterException("EnvironmentVariablesHolder methods 'separator'"
+ raise InterpreterException("EnvironmentVariablesObject methods 'separator'"
" argument needs to be a string.")
return separator
def warn_if_has_name(self, name: str) -> None:
# Multiple append/prepend operations was not supported until 0.58.0.
- if self.held_object.has_name(name):
+ if self.vars.has_name(name):
m = f'Overriding previous value of environment variable {name!r} with a new one'
FeatureNew('0.58.0', m).use(self.subproject)
@@ -255,7 +279,7 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
def set_method(self, args: T.Tuple[str, T.List[str]], kwargs: T.Dict[str, T.Any]) -> None:
name, values = args
separator = self.unpack_separator(kwargs)
- self.held_object.set(name, values, separator)
+ self.vars.set(name, values, separator)
@stringArgs
@permittedKwargs({'separator'})
@@ -264,7 +288,7 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
name, values = args
separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name)
- self.held_object.append(name, values, separator)
+ self.vars.append(name, values, separator)
@stringArgs
@permittedKwargs({'separator'})
@@ -273,14 +297,14 @@ class EnvironmentVariablesHolder(MutableInterpreterObject, ObjectHolder[build.En
name, values = args
separator = self.unpack_separator(kwargs)
self.warn_if_has_name(name)
- self.held_object.prepend(name, values, separator)
+ self.vars.prepend(name, values, separator)
-class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.ConfigurationData]):
- def __init__(self, pv, initial_values=None):
- MutableInterpreterObject.__init__(self)
+class ConfigurationDataObject(MutableInterpreterObject, MesonInterpreterObject):
+ def __init__(self, subproject: str, initial_values: T.Optional[T.Dict[str, T.Any]] = None) -> None:
self.used = False # These objects become immutable after use in configure_file.
- ObjectHolder.__init__(self, build.ConfigurationData(), pv)
+ super().__init__(subproject=subproject)
+ self.conf_data = build.ConfigurationData()
self.methods.update({'set': self.set_method,
'set10': self.set10_method,
'set_quoted': self.set_quoted_method,
@@ -294,15 +318,15 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.Confi
for k, v in initial_values.items():
self.set_method([k, v], {})
elif initial_values:
- raise AssertionError('Unsupported ConfigurationDataHolder initial_values')
+ raise AssertionError('Unsupported ConfigurationDataObject initial_values')
- def is_used(self):
+ def is_used(self) -> bool:
return self.used
- def mark_used(self):
+ def mark_used(self) -> None:
self.used = True
- def validate_args(self, args, kwargs):
+ def validate_args(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[str, T.Union[str, int, bool], T.Optional[str]]:
if len(args) == 1 and isinstance(args[0], list) and len(args[0]) == 2:
mlog.deprecation('Passing a list as the single argument to '
'configuration_data.set is deprecated. This will '
@@ -326,86 +350,101 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder[build.Confi
if desc is not None and not isinstance(desc, str):
raise InterpreterException('Description must be a string.')
- return name, val, desc
+ # TODO: Remove the cast once we get rid of the deprecation
+ return name, T.cast(T.Union[str, bool, int], val), desc
@noArgsFlattening
- def set_method(self, args, kwargs):
+ def set_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
(name, val, desc) = self.validate_args(args, kwargs)
- self.held_object.values[name] = (val, desc)
+ self.conf_data.values[name] = (val, desc)
- def set_quoted_method(self, args, kwargs):
+ def set_quoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
(name, val, desc) = self.validate_args(args, kwargs)
if not isinstance(val, str):
raise InterpreterException("Second argument to set_quoted must be a string.")
escaped_val = '\\"'.join(val.split('"'))
- self.held_object.values[name] = ('"' + escaped_val + '"', desc)
+ self.conf_data.values[name] = ('"' + escaped_val + '"', desc)
- def set10_method(self, args, kwargs):
+ def set10_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
(name, val, desc) = self.validate_args(args, kwargs)
if val:
- self.held_object.values[name] = (1, desc)
+ self.conf_data.values[name] = (1, desc)
else:
- self.held_object.values[name] = (0, desc)
+ self.conf_data.values[name] = (0, desc)
- def has_method(self, args, kwargs):
- return args[0] in self.held_object.values
+ def has_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return args[0] in self.conf_data.values
@FeatureNew('configuration_data.get()', '0.38.0')
@noArgsFlattening
- def get_method(self, args, kwargs):
+ def get_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get method takes one or two arguments.')
+ if not isinstance(args[0], str):
+ raise InterpreterException('The variable name must be a string.')
name = args[0]
- if name in self.held_object:
- return self.held_object.get(name)[0]
+ if name in self.conf_data:
+ return self.conf_data.get(name)[0]
if len(args) > 1:
- return args[1]
+ # Assertion does not work because setting other values is still
+ # supported, but deprecated. Use T.cast in the meantime (even though
+ # this is a lie).
+ # TODO: Fix this once the deprecation is removed
+ # assert isinstance(args[1], (int, str, bool))
+ return T.cast(T.Union[str, int, bool], args[1])
raise InterpreterException('Entry %s not in configuration data.' % name)
@FeatureNew('configuration_data.get_unquoted()', '0.44.0')
- def get_unquoted_method(self, args, kwargs):
+ def get_unquoted_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool]:
if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get method takes one or two arguments.')
+ if not isinstance(args[0], str):
+ raise InterpreterException('The variable name must be a string.')
name = args[0]
- if name in self.held_object:
- val = self.held_object.get(name)[0]
+ if name in self.conf_data:
+ val = self.conf_data.get(name)[0]
elif len(args) > 1:
+ assert isinstance(args[1], (str, int, bool))
val = args[1]
else:
raise InterpreterException('Entry %s not in configuration data.' % name)
- if val[0] == '"' and val[-1] == '"':
+ if isinstance(val, str) and val[0] == '"' and val[-1] == '"':
return val[1:-1]
return val
- def get(self, name):
- return self.held_object.values[name] # (val, desc)
+ def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
+ return self.conf_data.values[name]
@FeatureNew('configuration_data.keys()', '0.57.0')
@noPosargs
- def keys_method(self, args, kwargs):
+ def keys_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[str]:
return sorted(self.keys())
- def keys(self):
- return self.held_object.values.keys()
+ def keys(self) -> T.List[str]:
+ return list(self.conf_data.values.keys())
- def merge_from_method(self, args, kwargs):
+ def merge_from_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> None:
if len(args) != 1:
raise InterpreterException('Merge_from takes one positional argument.')
- from_object = args[0]
- if not isinstance(from_object, ConfigurationDataHolder):
+ from_object_holder = args[0]
+ if not isinstance(from_object_holder, ConfigurationDataObject):
raise InterpreterException('Merge_from argument must be a configuration data object.')
- from_object = from_object.held_object
+ from_object = from_object_holder.conf_data
for k, v in from_object.values.items():
- self.held_object.values[k] = v
+ self.conf_data.values[k] = v
-permitted_partial_dependency_kwargs = {
- 'compile_args', 'link_args', 'links', 'includes', 'sources'
-}
-class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
- def __init__(self, dep: Dependency, pv: str):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, dep, pv)
+_PARTIAL_DEP_KWARGS = [
+ KwargInfo('compile_args', bool, default=False),
+ KwargInfo('link_args', bool, default=False),
+ KwargInfo('links', bool, default=False),
+ KwargInfo('includes', bool, default=False),
+ KwargInfo('sources', bool, default=False),
+]
+
+class DependencyHolder(ObjectHolder[Dependency]):
+ def __init__(self, dep: Dependency, interpreter: 'Interpreter'):
+ super().__init__(dep, interpreter)
self.methods.update({'found': self.found_method,
'type_name': self.type_name_method,
'version': self.version_method,
@@ -419,35 +458,35 @@ class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
'as_link_whole': self.as_link_whole_method,
})
- def found(self):
+ def found(self) -> bool:
return self.found_method([], {})
@noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
+ @noKwargs
+ def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name
@noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
if self.held_object.type_name == 'internal':
return True
return self.held_object.found()
@noPosargs
- @permittedKwargs({})
- def version_method(self, args, kwargs):
+ @noKwargs
+ def version_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_version()
@noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
+ @noKwargs
+ def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_name()
@FeatureDeprecated('Dependency.get_pkgconfig_variable', '0.56.0',
'use Dependency.get_variable(pkgconfig : ...) instead')
@permittedKwargs({'define_variable', 'default'})
- def pkgconfig_method(self, args, kwargs):
+ def pkgconfig_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
args = listify(args)
if len(args) != 1:
raise InterpreterException('get_pkgconfig_variable takes exactly one argument.')
@@ -459,8 +498,8 @@ class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
@FeatureNew('dep.get_configtool_variable', '0.44.0')
@FeatureDeprecated('Dependency.get_configtool_variable', '0.56.0',
'use Dependency.get_variable(configtool : ...) instead')
- @permittedKwargs({})
- def configtool_method(self, args, kwargs):
+ @noKwargs
+ def configtool_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
args = listify(args)
if len(args) != 1:
raise InterpreterException('get_configtool_variable takes exactly one argument.')
@@ -471,16 +510,16 @@ class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
@FeatureNew('dep.partial_dependency', '0.46.0')
@noPosargs
- @permittedKwargs(permitted_partial_dependency_kwargs)
- def partial_dependency_method(self, args, kwargs):
+ @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
+ return pdep
@FeatureNew('dep.get_variable', '0.51.0')
@typed_pos_args('dep.get_variable', optargs=[str])
@permittedKwargs({'cmake', 'pkgconfig', 'configtool', 'internal', 'default_value', 'pkgconfig_define'})
@FeatureNewKwargs('dep.get_variable', '0.54.0', ['internal'])
- def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> str:
+ def variable_method(self, args: T.Tuple[T.Optional[str]], kwargs: T.Dict[str, T.Any]) -> T.Union[str, T.List[str]]:
default_varname = args[0]
if default_varname is not None:
FeatureNew('0.58.0', 'Positional argument to dep.get_variable()').use(self.subproject)
@@ -490,155 +529,97 @@ class DependencyHolder(InterpreterObject, ObjectHolder[Dependency]):
@FeatureNew('dep.include_type', '0.52.0')
@noPosargs
- @permittedKwargs({})
- def include_type_method(self, args, kwargs):
+ @noKwargs
+ def include_type_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.get_include_type()
@FeatureNew('dep.as_system', '0.52.0')
- @permittedKwargs({})
- def as_system_method(self, args, kwargs):
+ @noKwargs
+ def as_system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
args = listify(args)
new_is_system = 'system'
if len(args) > 1:
raise InterpreterException('as_system takes only one optional value')
if len(args) == 1:
+ if not isinstance(args[0], str):
+ raise InterpreterException('as_system takes exactly one string parameter')
new_is_system = args[0]
new_dep = self.held_object.generate_system_dependency(new_is_system)
- return DependencyHolder(new_dep, self.subproject)
+ return new_dep
@FeatureNew('dep.as_link_whole', '0.56.0')
- @permittedKwargs({})
+ @noKwargs
@noPosargs
- def as_link_whole_method(self, args, kwargs):
+ def as_link_whole_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> Dependency:
if not isinstance(self.held_object, InternalDependency):
raise InterpreterException('as_link_whole method is only supported on declare_dependency() objects')
new_dep = self.held_object.generate_link_whole_dependency()
- return DependencyHolder(new_dep, self.subproject)
-
-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
+ return new_dep
+
+class ExternalProgramHolder(ObjectHolder[ExternalProgram]):
+ def __init__(self, ep: ExternalProgram, interpreter: 'Interpreter') -> None:
+ super().__init__(ep, interpreter)
self.methods.update({'found': self.found_method,
'path': self.path_method,
'full_path': self.full_path_method})
- self.cached_version = None
@noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found()
@noPosargs
- @permittedKwargs({})
+ @noKwargs
@FeatureDeprecated('ExternalProgram.path', '0.55.0',
'use ExternalProgram.full_path() instead')
- def path_method(self, args, kwargs):
+ def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._full_path()
@noPosargs
- @permittedKwargs({})
+ @noKwargs
@FeatureNew('ExternalProgram.full_path', '0.55.0')
- def full_path_method(self, args, kwargs):
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self._full_path()
- def _full_path(self):
- 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)
+ def _full_path(self) -> str:
+ if not self.found():
+ raise InterpreterException('Unable to get the path of a not-found external program')
+ path = self.held_object.get_path()
+ assert path is not None
+ return path
+
+ def found(self) -> bool:
+ return self.held_object.found()
+
+class ExternalLibraryHolder(ObjectHolder[ExternalLibrary]):
+ def __init__(self, el: ExternalLibrary, interpreter: 'Interpreter'):
+ super().__init__(el, interpreter)
self.methods.update({'found': self.found_method,
'type_name': self.type_name_method,
'partial_dependency': self.partial_dependency_method,
})
- def found(self):
- return self.held_object.found()
-
@noPosargs
- @permittedKwargs({})
- def type_name_method(self, args, kwargs):
+ @noKwargs
+ def type_name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.type_name
@noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
- return self.found()
-
- def get_name(self):
- return self.held_object.name
-
- def get_compile_args(self):
- return self.held_object.get_compile_args()
-
- def get_link_args(self):
- return self.held_object.get_link_args()
-
- def get_exe_args(self):
- return self.held_object.get_exe_args()
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return self.held_object.found()
@FeatureNew('dep.partial_dependency', '0.46.0')
@noPosargs
- @permittedKwargs(permitted_partial_dependency_kwargs)
- def partial_dependency_method(self, args, kwargs):
+ @typed_kwargs('dep.partial_dependency', *_PARTIAL_DEP_KWARGS)
+ def partial_dependency_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.DependencyMethodPartialDependency') -> Dependency:
pdep = self.held_object.get_partial_dependency(**kwargs)
- return DependencyHolder(pdep, self.subproject)
-
-
-class GeneratedListHolder(InterpreterObject, ObjectHolder[build.GeneratedList]):
- def __init__(self, arg1: 'build.GeneratedList'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, arg1)
-
- def __repr__(self) -> str:
- r = '<{}: {!r}>'
- return r.format(self.__class__.__name__, self.held_object.get_outputs())
-
+ return pdep
# A machine that's statically known from the cross file
-class MachineHolder(InterpreterObject, ObjectHolder['MachineInfo']):
- def __init__(self, machine_info: 'MachineInfo'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, machine_info)
+class MachineHolder(ObjectHolder['MachineInfo']):
+ def __init__(self, machine_info: 'MachineInfo', interpreter: 'Interpreter'):
+ super().__init__(machine_info, interpreter)
self.methods.update({'system': self.system_method,
'cpu': self.cpu_method,
'cpu_family': self.cpu_family_method,
@@ -646,101 +627,53 @@ class MachineHolder(InterpreterObject, ObjectHolder['MachineInfo']):
})
@noPosargs
- @permittedKwargs({})
- def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ @noKwargs
+ def cpu_family_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu_family
@noPosargs
- @permittedKwargs({})
- def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ @noKwargs
+ def cpu_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.cpu
@noPosargs
- @permittedKwargs({})
- def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ @noKwargs
+ def system_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.system
@noPosargs
- @permittedKwargs({})
- def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_nkwargs) -> str:
+ @noKwargs
+ def endian_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.held_object.endian
-class IncludeDirsHolder(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]):
+class IncludeDirsHolder(ObjectHolder[build.IncludeDirs]):
+ pass
- def __init__(self, obj: build.Man):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, obj)
+class FileHolder(ObjectHolder[mesonlib.File]):
+ pass
- def get_custom_install_dir(self) -> T.Optional[str]:
- return self.held_object.custom_install_dir
+class HeadersHolder(ObjectHolder[build.Headers]):
+ pass
- def get_custom_install_mode(self) -> T.Optional[FileMode]:
- return self.held_object.custom_install_mode
+class DataHolder(ObjectHolder[build.Data]):
+ pass
- def locale(self) -> T.Optional[str]:
- return self.held_object.locale
+class InstallDirHolder(ObjectHolder[build.InstallDir]):
+ pass
- def get_sources(self) -> T.List[mesonlib.File]:
- return self.held_object.sources
+class ManHolder(ObjectHolder[build.Man]):
+ pass
-class GeneratedObjectsHolder(InterpreterObject, ObjectHolder[build.ExtractedObjects]):
- def __init__(self, held_object: build.ExtractedObjects):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, held_object)
+class GeneratedObjectsHolder(ObjectHolder[build.ExtractedObjects]):
+ pass
-class Test(InterpreterObject):
+class Test(MesonInterpreterObject):
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)
+ super().__init__()
self.name = name
self.suite = suite
self.project_name = project
@@ -755,18 +688,27 @@ class Test(InterpreterObject):
self.protocol = TestProtocol.from_str(protocol)
self.priority = priority
- def get_exe(self):
+ def get_exe(self) -> build.Executable:
return self.exe
- def get_name(self):
+ def get_name(self) -> str:
return self.name
-class SubprojectHolder(InterpreterObject, ObjectHolder[T.Optional['Interpreter']]):
+class NullSubprojectInterpreter(HoldableObject):
+ pass
+
+# TODO: This should really be an `ObjectHolder`, but the additional stuff in this
+# class prevents this. Thus, this class should be split into a pure
+# `ObjectHolder` and a class specifically for stroing in `Interpreter`.
+class SubprojectHolder(MesonInterpreterObject):
- def __init__(self, subinterpreter: T.Optional['Interpreter'], subdir: str, warnings=0, disabled_feature=None,
- exception=None):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, subinterpreter)
+ def __init__(self, subinterpreter: T.Union['Interpreter', NullSubprojectInterpreter],
+ subdir: str,
+ warnings: int = 0,
+ disabled_feature: T.Optional[str] = None,
+ exception: T.Optional[MesonException] = None) -> None:
+ super().__init__()
+ self.held_object = subinterpreter
self.warnings = warnings
self.disabled_feature = disabled_feature
self.exception = exception
@@ -776,19 +718,20 @@ class SubprojectHolder(InterpreterObject, ObjectHolder[T.Optional['Interpreter']
})
@noPosargs
- @permittedKwargs({})
- def found_method(self, args, kwargs):
+ @noKwargs
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
return self.found()
- def found(self):
- return self.held_object is not None
+ def found(self) -> bool:
+ return not isinstance(self.held_object, NullSubprojectInterpreter)
- @permittedKwargs({})
+ @noKwargs
@noArgsFlattening
- def get_variable_method(self, args, kwargs):
+ @unholder_return
+ def get_variable_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
if len(args) < 1 or len(args) > 2:
raise InterpreterException('Get_variable takes one or two arguments.')
- if not self.found():
+ if isinstance(self.held_object, NullSubprojectInterpreter): # == not self.found()
raise InterpreterException('Subproject "%s" disabled can\'t get_variable on it.' % (self.subdir))
varname = args[0]
if not isinstance(varname, str):
@@ -803,13 +746,8 @@ class SubprojectHolder(InterpreterObject, ObjectHolder[T.Optional['Interpreter']
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):
+class ModuleObjectHolder(ObjectHolder[ModuleObject]):
+ def method_call(self, method_name: str, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> TYPE_var:
modobj = self.held_object
method = modobj.methods.get(method_name)
if not method:
@@ -827,27 +765,18 @@ class ModuleObjectHolder(InterpreterObject, ObjectHolder['ModuleObject']):
if isinstance(ret, ModuleReturnValue):
self.interpreter.process_new_values(ret.new_objects)
ret = ret.return_value
- return self.interpreter.holderify(ret)
+ return ret
class MutableModuleObjectHolder(ModuleObjectHolder, MutableInterpreterObject):
- def __deepcopy__(self, memo):
+ def __deepcopy__(self, memo: T.Dict[int, T.Any]) -> 'MutableModuleObjectHolder':
# Deepcopy only held object, not interpreter
modobj = copy.deepcopy(self.held_object, memo)
return MutableModuleObjectHolder(modobj, self.interpreter)
-_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=T.Union[build.BuildTarget, build.BothLibraries])
-_BuildTarget = T.TypeVar('_BuildTarget', bound=build.BuildTarget)
-
-class BuildTargetHolder(TargetHolder[_BuildTarget]):
+class BuildTargetHolder(ObjectHolder[_BuildTarget]):
def __init__(self, target: _BuildTarget, interp: 'Interpreter'):
super().__init__(target, interp)
self.methods.update({'extract_objects': self.extract_objects_method,
@@ -856,62 +785,84 @@ class BuildTargetHolder(TargetHolder[_BuildTarget]):
'get_id': self.get_id_method,
'outdir': self.outdir_method,
'full_path': self.full_path_method,
+ 'path': self.path_method,
+ 'found': self.found_method,
'private_dir_include': self.private_dir_include_method,
})
- def __repr__(self):
+ def __repr__(self) -> str:
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)
+ @property
+ def _target_object(self) -> build.BuildTarget:
+ if isinstance(self.held_object, build.BothLibraries):
+ return self.held_object.get_preferred_library()
+ assert isinstance(self.held_object, build.BuildTarget)
+ return self.held_object
+
+ def is_cross(self) -> bool:
+ return not self._target_object.environment.machines.matches_build_machine(self._target_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)]))
+ @noKwargs
+ @FeatureNew('BuildTarget.found', '0.59.0')
+ def found_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> bool:
+ return True
@noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
- return self.interpreter.backend.get_target_filename_abs(self.held_object)
+ @noKwargs
+ def private_dir_include_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.IncludeDirs:
+ return build.IncludeDirs('', [], False, [self.interpreter.backend.get_target_private_dir(self._target_object)])
@noPosargs
- @permittedKwargs({})
- def outdir_method(self, args, kwargs):
- return self.interpreter.backend.get_target_dir(self.held_object)
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_filename_abs(self._target_object)
- @permittedKwargs({})
- def extract_objects_method(self, args, kwargs):
- gobjs = self.held_object.extract_objects(args)
- return GeneratedObjectsHolder(gobjs)
+ @noPosargs
+ @noKwargs
+ @FeatureDeprecated('BuildTarget.path', '0.55.0', 'Use BuildTarget.full_path instead')
+ def path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_filename_abs(self._target_object)
- @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)
+ @noKwargs
+ def outdir_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self.interpreter.backend.get_target_dir(self._target_object)
+
+ @noKwargs
+ @typed_pos_args('extract_objects', varargs=(mesonlib.File, str))
+ def extract_objects_method(self, args: T.Tuple[T.List[mesonlib.FileOrString]], kwargs: TYPE_nkwargs) -> build.ExtractedObjects:
+ return self._target_object.extract_objects(args[0])
+
+ @noPosargs
+ @typed_kwargs(
+ 'extract_all_objects',
+ KwargInfo(
+ 'recursive', bool, default=False, since='0.46.0',
+ not_set_warning=textwrap.dedent('''\
+ 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.
+ ''')
+ )
+ )
+ def extract_all_objects_method(self, args: T.List[TYPE_nvar], kwargs: 'kwargs.BuildTargeMethodExtractAllObjects') -> build.ExtractedObjects:
+ return self._target_object.extract_all_objects(kwargs['recursive'])
@noPosargs
- @permittedKwargs({})
- def get_id_method(self, args, kwargs):
- return self.held_object.get_id()
+ @noKwargs
+ def get_id_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._target_object.get_id()
@FeatureNew('name', '0.54.0')
@noPosargs
- @permittedKwargs({})
- def name_method(self, args, kwargs):
- return self.held_object.name
+ @noKwargs
+ def name_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ return self._target_object.name
class ExecutableHolder(BuildTargetHolder[build.Executable]):
pass
@@ -920,37 +871,32 @@ 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
+ pass
-class BothLibrariesHolder(BuildTargetHolder):
- def __init__(self, shared_holder, static_holder, interp):
+class BothLibrariesHolder(BuildTargetHolder[build.BothLibraries]):
+ def __init__(self, libs: build.BothLibraries, interp: 'Interpreter'):
# 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
+ super().__init__(libs, interp)
self.methods.update({'get_shared_lib': self.get_shared_lib_method,
'get_static_lib': self.get_static_lib_method,
})
- def __repr__(self):
+ def __repr__(self) -> str:
r = '<{} {}: {}, {}: {}>'
- h1 = self.shared_holder.held_object
- h2 = self.static_holder.held_object
+ h1 = self.held_object.shared
+ h2 = self.held_object.static
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
+ @noKwargs
+ def get_shared_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.SharedLibrary:
+ return self.held_object.shared
@noPosargs
- @permittedKwargs({})
- def get_static_lib_method(self, args, kwargs):
- return self.static_holder
+ @noKwargs
+ def get_static_lib_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> build.StaticLibrary:
+ return self.held_object.static
class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
pass
@@ -958,7 +904,7 @@ class SharedModuleHolder(BuildTargetHolder[build.SharedModule]):
class JarHolder(BuildTargetHolder[build.Jar]):
pass
-class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]):
+class CustomTargetIndexHolder(ObjectHolder[build.CustomTargetIndex]):
def __init__(self, target: build.CustomTargetIndex, interp: 'Interpreter'):
super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method,
@@ -966,75 +912,69 @@ class CustomTargetIndexHolder(TargetHolder[build.CustomTargetIndex]):
@FeatureNew('custom_target[i].full_path', '0.54.0')
@noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
+ assert self.interpreter.backend is not None
return self.interpreter.backend.get_target_filename_abs(self.held_object)
-class CustomTargetHolder(TargetHolder[build.CustomTarget]):
+class CustomTargetHolder(ObjectHolder[build.CustomTarget]):
def __init__(self, target: 'build.CustomTarget', interp: 'Interpreter'):
super().__init__(target, interp)
self.methods.update({'full_path': self.full_path_method,
'to_list': self.to_list_method,
})
- def __repr__(self):
+ def __repr__(self) -> str:
r = '<{} {}: {}>'
h = self.held_object
return r.format(self.__class__.__name__, h.get_id(), h.command)
@noPosargs
- @permittedKwargs({})
- def full_path_method(self, args, kwargs):
+ @noKwargs
+ def full_path_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> str:
return self.interpreter.backend.get_target_filename_abs(self.held_object)
@FeatureNew('custom_target.to_list', '0.54.0')
@noPosargs
- @permittedKwargs({})
- def to_list_method(self, args, kwargs):
+ @noKwargs
+ def to_list_method(self, args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.List[build.CustomTargetIndex]:
result = []
for i in self.held_object:
- result.append(CustomTargetIndexHolder(i, self.interpreter))
+ result.append(i)
return result
- def __getitem__(self, index):
- return CustomTargetIndexHolder(self.held_object[index], self.interpreter)
+ def __getitem__(self, index: int) -> build.CustomTargetIndex:
+ return self.held_object[index]
- def __setitem__(self, index, value): # lgtm[py/unexpected-raise-in-special-method]
+ def __setitem__(self, index: int, value: T.Any) -> None: # lgtm[py/unexpected-raise-in-special-method]
raise InterpreterException('Cannot set a member of a CustomTarget')
- def __delitem__(self, index): # lgtm[py/unexpected-raise-in-special-method]
+ def __delitem__(self, index: int) -> None: # lgtm[py/unexpected-raise-in-special-method]
raise InterpreterException('Cannot delete a member of a CustomTarget')
- def outdir_include(self):
- return IncludeDirsHolder(build.IncludeDirs('', [], False,
- [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(self.held_object))]))
-
-class RunTargetHolder(TargetHolder):
- def __init__(self, target, interp):
- super().__init__(target, interp)
-
- def __repr__(self):
- r = '<{} {}: {}>'
- h = self.held_object
- return r.format(self.__class__.__name__, h.get_id(), h.command)
+class RunTargetHolder(ObjectHolder[build.RunTarget]):
+ pass
+class AliasTargetHolder(ObjectHolder[build.AliasTarget]):
+ pass
-class GeneratorHolder(InterpreterObject, ObjectHolder[build.Generator]):
+class GeneratedListHolder(ObjectHolder[build.GeneratedList]):
+ pass
- def __init__(self, gen: 'build.Generator', interpreter: 'Interpreter'):
- InterpreterObject.__init__(self)
- ObjectHolder.__init__(self, gen, interpreter.subproject)
- self.interpreter = interpreter
+class GeneratorHolder(ObjectHolder[build.Generator]):
+ def __init__(self, gen: build.Generator, interpreter: 'Interpreter'):
+ super().__init__(gen, interpreter)
self.methods.update({'process': self.process_method})
- @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder))
+ @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList))
@typed_kwargs(
'generator.process',
KwargInfo('preserve_path_from', str, since='0.45.0'),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
)
- def process_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder]]],
- kwargs: 'kwargs.GeneratorProcess') -> GeneratedListHolder:
+ def process_method(self,
+ args: T.Tuple[T.List[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]]],
+ kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList:
preserve_path_from = kwargs['preserve_path_from']
if preserve_path_from is not None:
preserve_path_from = os.path.normpath(preserve_path_from)
@@ -1042,12 +982,12 @@ class GeneratorHolder(InterpreterObject, ObjectHolder[build.Generator]):
# This is a bit of a hack. Fix properly before merging.
raise InvalidArguments('Preserve_path_from must be an absolute path for now. Sorry.')
- if any(isinstance(a, (CustomTargetHolder, CustomTargetIndexHolder, GeneratedListHolder)) for a in args[0]):
+ if any(isinstance(a, (build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) for a in args[0]):
FeatureNew.single_use(
f'Calling generator.process with CustomTaget or Index of CustomTarget.',
'0.57.0', self.interpreter.subproject)
- gl = self.held_object.process_files(mesonlib.unholder(args[0]), self.interpreter,
+ gl = self.held_object.process_files(args[0], self.interpreter,
preserve_path_from, extra_args=kwargs['extra_args'])
- return GeneratedListHolder(gl)
+ return gl
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 9734caa..1cc2082 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -4,15 +4,14 @@
"""Keyword Argument type annotations."""
+from mesonbuild import coredata
import typing as T
from typing_extensions import TypedDict, Literal
+from .. import build
from ..mesonlib import MachineChoice, File
-from .interpreterobjects import (
- BuildTargetHolder, CustomTargetHolder, EnvironmentVariablesHolder,
- FeatureOptionHolder, TargetHolder
-)
+from .interpreterobjects import EnvironmentVariablesObject
class FuncAddProjectArgs(TypedDict):
@@ -34,13 +33,13 @@ class BaseTest(TypedDict):
"""Shared base for the Rust module."""
- args: T.List[T.Union[str, File, TargetHolder]]
+ args: T.List[T.Union[str, File, build.Target]]
should_fail: bool
timeout: int
workdir: T.Optional[str]
- depends: T.List[T.Union[CustomTargetHolder, BuildTargetHolder]]
+ depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]]
priority: int
- env: T.Union[EnvironmentVariablesHolder, T.List[str], T.Dict[str, str], str]
+ env: T.Union[EnvironmentVariablesObject, T.List[str], T.Dict[str, str], str]
suite: T.List[str]
@@ -70,7 +69,7 @@ class ExtractRequired(TypedDict):
a boolean or a feature option should inherit it's arguments from this class.
"""
- required: T.Union[bool, 'FeatureOptionHolder']
+ required: T.Union[bool, coredata.UserFeatureOption]
class FuncGenerator(TypedDict):
@@ -81,7 +80,7 @@ class FuncGenerator(TypedDict):
output: T.List[str]
depfile: bool
capture: bool
- depends: T.List[T.Union['BuildTargetHolder', 'CustomTargetHolder']]
+ depends: T.List[T.Union[build.BuildTarget, build.CustomTarget]]
class GeneratorProcess(TypedDict):
@@ -90,3 +89,16 @@ class GeneratorProcess(TypedDict):
preserve_path_from: T.Optional[str]
extra_args: T.List[str]
+
+class DependencyMethodPartialDependency(TypedDict):
+
+ """ Keyword Arguments for the dep.partial_dependency methods """
+
+ compile_args: bool
+ link_args: bool
+ links: bool
+ includes: bool
+ sources: bool
+
+class BuildTargeMethodExtractAllObjects(TypedDict):
+ recursive: bool
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index e76ad2e..c3cc0d2 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -5,22 +5,24 @@ from .. import dependencies
from .. import build
from .. import mlog
-from ..mesonlib import unholder, MachineChoice, OptionKey
+from ..mesonlib import MachineChoice, OptionKey
from ..programs import OverrideProgram, ExternalProgram
-from ..interpreterbase import (InterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
+from ..interpreterbase import (MesonInterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs,
MesonVersionString, InterpreterException)
-from .compiler import CompilerHolder
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder,
- EnvironmentVariablesHolder)
+ EnvironmentVariablesObject)
import typing as T
-class MesonMain(InterpreterObject):
+if T.TYPE_CHECKING:
+ from .interpreter import Interpreter
+
+class MesonMain(MesonInterpreterObject):
def __init__(self, build: 'build.Build', interpreter: 'Interpreter'):
- InterpreterObject.__init__(self)
+ super().__init__()
self.build = build
self.interpreter = interpreter
self.methods.update({'get_compiler': self.get_compiler_method,
@@ -54,11 +56,11 @@ class MesonMain(InterpreterObject):
'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
+ def _find_source_script(self, prog: T.Union[str, mesonlib.File, build.Executable, ExternalProgram], args):
+
+ if isinstance(prog, (build.Executable, ExternalProgram)):
+ return self.interpreter.backend.get_executable_serialisation([prog] + args)
+ found = self.interpreter.func_find_program({}, prog, {})
es = self.interpreter.backend.get_executable_serialisation([found] + args)
es.subproject = self.interpreter.subproject
return es
@@ -72,7 +74,6 @@ class MesonMain(InterpreterObject):
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):
@@ -252,7 +253,7 @@ class MesonMain(InterpreterObject):
for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
clist = self.interpreter.coredata.compilers[for_machine]
if cname in clist:
- return CompilerHolder(clist[cname], self.build.environment, self.interpreter.subproject)
+ return clist[cname]
raise InterpreterException(f'Tried to access compiler for language "{cname}", not specified for {for_machine.get_lower_case_name()} machine.')
@noPosargs
@@ -284,7 +285,6 @@ class MesonMain(InterpreterObject):
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)
@@ -304,7 +304,6 @@ class MesonMain(InterpreterObject):
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)
@@ -375,9 +374,9 @@ class MesonMain(InterpreterObject):
@FeatureNew('add_devenv', '0.58.0')
@noKwargs
- @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesHolder))
- def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesHolder], kwargs: T.Dict[str, T.Any]) -> None:
+ @typed_pos_args('add_devenv', (str, list, dict, EnvironmentVariablesObject))
+ def add_devenv_method(self, args: T.Union[str, list, dict, EnvironmentVariablesObject], kwargs: T.Dict[str, T.Any]) -> None:
env = args[0]
if isinstance(env, (str, list, dict)):
- env = EnvironmentVariablesHolder(env)
- self.build.devenv.append(env.held_object)
+ env = EnvironmentVariablesObject(env)
+ self.build.devenv.append(env.vars)
diff --git a/mesonbuild/interpreterbase/__init__.py b/mesonbuild/interpreterbase/__init__.py
index d5ef367..d776ae1 100644
--- a/mesonbuild/interpreterbase/__init__.py
+++ b/mesonbuild/interpreterbase/__init__.py
@@ -14,6 +14,7 @@
__all__ = [
'InterpreterObject',
+ 'MesonInterpreterObject',
'ObjectHolder',
'RangeHolder',
'MesonVersionString',
@@ -38,12 +39,14 @@ __all__ = [
'noKwargs',
'stringArgs',
'noArgsFlattening',
+ 'unholder_return',
'disablerIfNotFound',
'permittedKwargs',
'typed_pos_args',
'ContainerTypeInfo',
'KwargInfo',
'typed_kwargs',
+ 'FeatureCheckBase',
'FeatureNew',
'FeatureDeprecated',
'FeatureNewKwargs',
@@ -58,12 +61,14 @@ __all__ = [
'TYPE_elementary',
'TYPE_var',
'TYPE_nvar',
+ 'TYPE_kwargs',
'TYPE_nkwargs',
'TYPE_key_resolver',
]
from .baseobjects import (
InterpreterObject,
+ MesonInterpreterObject,
ObjectHolder,
RangeHolder,
MutableInterpreterObject,
@@ -75,6 +80,7 @@ from .baseobjects import (
TYPE_elementary,
TYPE_var,
TYPE_nvar,
+ TYPE_kwargs,
TYPE_nkwargs,
TYPE_key_resolver,
)
@@ -85,12 +91,14 @@ from .decorators import (
noKwargs,
stringArgs,
noArgsFlattening,
+ unholder_return,
disablerIfNotFound,
permittedKwargs,
typed_pos_args,
ContainerTypeInfo,
KwargInfo,
typed_kwargs,
+ FeatureCheckBase,
FeatureNew,
FeatureDeprecated,
FeatureNewKwargs,
diff --git a/mesonbuild/interpreterbase/_unholder.py b/mesonbuild/interpreterbase/_unholder.py
new file mode 100644
index 0000000..b5663a5
--- /dev/null
+++ b/mesonbuild/interpreterbase/_unholder.py
@@ -0,0 +1,37 @@
+# Copyright 2013-2021 The Meson development team
+
+# 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.
+
+from .baseobjects import InterpreterObject, MesonInterpreterObject, ObjectHolder, TYPE_var
+from .exceptions import InvalidArguments
+from ..mesonlib import HoldableObject, MesonBugException
+
+import typing as T
+
+def _unholder(obj: T.Union[TYPE_var, InterpreterObject]) -> TYPE_var:
+ if isinstance(obj, (int, bool, str)):
+ return obj
+ elif isinstance(obj, list):
+ return [_unholder(x) for x in obj]
+ elif isinstance(obj, dict):
+ return {k: _unholder(v) for k, v in obj.items()}
+ elif isinstance(obj, ObjectHolder):
+ assert isinstance(obj.held_object, HoldableObject)
+ return obj.held_object
+ elif isinstance(obj, MesonInterpreterObject):
+ return obj
+ elif isinstance(obj, HoldableObject):
+ raise MesonBugException(f'Argument {obj} of type {type(obj).__name__} is not held by an ObjectHolder.')
+ elif isinstance(obj, InterpreterObject):
+ raise InvalidArguments(f'Argument {obj} of type {type(obj).__name__} cannot be passed to a method or function')
+ raise MesonBugException(f'Unknown object {obj} of type {type(obj).__name__} in the parameters.')
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py
index f48ab9b..d82aad2 100644
--- a/mesonbuild/interpreterbase/baseobjects.py
+++ b/mesonbuild/interpreterbase/baseobjects.py
@@ -15,58 +15,73 @@
from .. import mparser
from .exceptions import InvalidCode
from .helpers import flatten
+from ..mesonlib import HoldableObject
import typing as T
-TV_fw_var = T.Union[str, int, float, bool, list, dict, 'InterpreterObject', 'ObjectHolder']
+if T.TYPE_CHECKING:
+ # Object holders need the actual interpreter
+ from ..interpreter import Interpreter
+
+TV_fw_var = T.Union[str, int, bool, list, dict, 'InterpreterObject']
TV_fw_args = T.List[T.Union[mparser.BaseNode, TV_fw_var]]
TV_fw_kwargs = T.Dict[str, T.Union[mparser.BaseNode, TV_fw_var]]
TV_func = T.TypeVar('TV_func', bound=T.Callable[..., T.Any])
-TYPE_elementary = T.Union[str, int, float, bool]
-TYPE_var = T.Union[TYPE_elementary, T.List[T.Any], T.Dict[str, T.Any], 'InterpreterObject', 'ObjectHolder']
+TYPE_elementary = T.Union[str, int, bool, T.List[T.Any], T.Dict[str, T.Any]]
+TYPE_var = T.Union[TYPE_elementary, HoldableObject, 'MesonInterpreterObject']
TYPE_nvar = T.Union[TYPE_var, mparser.BaseNode]
+TYPE_kwargs = T.Dict[str, TYPE_var]
TYPE_nkwargs = T.Dict[str, TYPE_nvar]
TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
class InterpreterObject:
- def __init__(self) -> None:
- self.methods = {} # type: T.Dict[str, T.Callable[[T.List[TYPE_nvar], TYPE_nkwargs], TYPE_var]]
+ def __init__(self, *, subproject: T.Optional[str] = None) -> None:
+ self.methods: T.Dict[
+ str,
+ T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var]
+ ] = {}
# Current node set during a method call. This can be used as location
# when printing a warning message during a method call.
- self.current_node = None # type: mparser.BaseNode
+ self.current_node: mparser.BaseNode = None
+ self.subproject: str = subproject or ''
def method_call(
self,
method_name: str,
- args: TV_fw_args,
- kwargs: TV_fw_kwargs
+ args: T.List[TYPE_var],
+ kwargs: TYPE_kwargs
) -> TYPE_var:
if method_name in self.methods:
method = self.methods[method_name]
if not getattr(method, 'no-args-flattening', False):
args = flatten(args)
return method(args, kwargs)
- raise InvalidCode('Unknown method "%s" in object.' % method_name)
+ raise InvalidCode(f'Unknown method "{method_name}" in object {self} of type {type(self).__name__}.')
+
+class MesonInterpreterObject(InterpreterObject):
+ ''' All non-elementary objects and non-object-holders should be derived from this '''
-class MutableInterpreterObject(InterpreterObject):
- def __init__(self) -> None:
- super().__init__()
+class MutableInterpreterObject:
+ ''' Dummy class to mark the object type as mutable '''
-TV_InterpreterObject = T.TypeVar('TV_InterpreterObject')
+InterpreterObjectTypeVar = T.TypeVar('InterpreterObjectTypeVar', bound=HoldableObject)
-class ObjectHolder(T.Generic[TV_InterpreterObject]):
- def __init__(self, obj: TV_InterpreterObject, subproject: str = '') -> None:
+class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]):
+ def __init__(self, obj: InterpreterObjectTypeVar, interpreter: 'Interpreter') -> None:
+ super().__init__(subproject=interpreter.subproject)
+ assert isinstance(obj, HoldableObject), f'This is a bug: Trying to hold object of type `{type(obj).__name__}` that is not an `HoldableObject`'
self.held_object = obj
- self.subproject = subproject
+ self.interpreter = interpreter
+ self.env = self.interpreter.environment
def __repr__(self) -> str:
- return f'<Holder: {self.held_object!r}>'
+ return f'<[{type(self).__name__}] holds [{type(self.held_object).__name__}]: {self.held_object!r}>'
-class RangeHolder(InterpreterObject):
- def __init__(self, start: int, stop: int, step: int) -> None:
- super().__init__()
+class RangeHolder(MesonInterpreterObject):
+ def __init__(self, start: int, stop: int, step: int, *, subproject: T.Optional[str] = None) -> None:
+ super().__init__(subproject=subproject)
self.range = range(start, stop, step)
def __iter__(self) -> T.Iterator[int]:
diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py
index b82a413..a011b66 100644
--- a/mesonbuild/interpreterbase/decorators.py
+++ b/mesonbuild/interpreterbase/decorators.py
@@ -13,10 +13,11 @@
# limitations under the License.
from .. import mesonlib, mlog
-from .baseobjects import TV_func, TYPE_nvar
+from .baseobjects import TV_func, TYPE_var
from .disabler import Disabler
from .exceptions import InterpreterException, InvalidArguments
from .helpers import check_stringlist, get_callee_args
+from ._unholder import _unholder
from functools import wraps
import abc
@@ -67,13 +68,20 @@ def noArgsFlattening(f: TV_func) -> TV_func:
setattr(f, 'no-args-flattening', True) # noqa: B010
return f
+def unholder_return(f: TV_func) -> T.Callable[..., TYPE_var]:
+ @wraps(f)
+ def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
+ res = f(*wrapped_args, **wrapped_kwargs)
+ return _unholder(res)
+ return T.cast(T.Callable[..., TYPE_var], wrapped)
+
def disablerIfNotFound(f: TV_func) -> TV_func:
@wraps(f)
def wrapped(*wrapped_args: T.Any, **wrapped_kwargs: T.Any) -> T.Any:
kwargs = get_callee_args(wrapped_args)[3]
disabler = kwargs.pop('disabler', False)
ret = f(*wrapped_args, **wrapped_kwargs)
- if disabler and not ret.held_object.found():
+ if disabler and not ret.found():
return Disabler()
return ret
return T.cast(TV_func, wrapped)
@@ -292,8 +300,10 @@ class KwargInfo(T.Generic[_T]):
validation, just converstion.
:param deprecated_values: a dictionary mapping a value to the version of
meson it was deprecated in.
- :param since: a dictionary mapping a value to the version of meson it was
+ :param since_values: a dictionary mapping a value to the version of meson it was
added in.
+ :param not_set_warning: A warning messsage that is logged if the kwarg is not
+ set by the user.
"""
def __init__(self, name: str, types: T.Union[T.Type[_T], T.Tuple[T.Type[_T], ...], ContainerTypeInfo],
@@ -304,7 +314,8 @@ class KwargInfo(T.Generic[_T]):
deprecated: T.Optional[str] = None,
deprecated_values: T.Optional[T.Dict[str, str]] = None,
validator: T.Optional[T.Callable[[_T], T.Optional[str]]] = None,
- convertor: T.Optional[T.Callable[[_T], TYPE_nvar]] = None):
+ convertor: T.Optional[T.Callable[[_T], TYPE_var]] = None,
+ not_set_warning: T.Optional[str] = None):
self.name = name
self.types = types
self.required = required
@@ -316,6 +327,7 @@ class KwargInfo(T.Generic[_T]):
self.deprecated_values = deprecated_values
self.validator = validator
self.convertor = convertor
+ self.not_set_warning = not_set_warning
def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]:
@@ -410,6 +422,8 @@ def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]:
kwargs[info.name] = info.types.container(info.default)
else:
kwargs[info.name] = info.default
+ if info.not_set_warning:
+ mlog.warning(info.not_set_warning)
if info.convertor:
kwargs[info.name] = info.convertor(kwargs[info.name])
diff --git a/mesonbuild/interpreterbase/disabler.py b/mesonbuild/interpreterbase/disabler.py
index 50bc5bb..81f5264 100644
--- a/mesonbuild/interpreterbase/disabler.py
+++ b/mesonbuild/interpreterbase/disabler.py
@@ -12,10 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from .baseobjects import InterpreterObject
+from .baseobjects import MesonInterpreterObject
import typing as T
-class Disabler(InterpreterObject):
+class Disabler(MesonInterpreterObject):
def __init__(self) -> None:
super().__init__()
self.methods.update({'found': self.found_method})
diff --git a/mesonbuild/interpreterbase/helpers.py b/mesonbuild/interpreterbase/helpers.py
index 1070a9e..2602e80 100644
--- a/mesonbuild/interpreterbase/helpers.py
+++ b/mesonbuild/interpreterbase/helpers.py
@@ -19,15 +19,15 @@ import collections.abc
import typing as T
if T.TYPE_CHECKING:
- from .baseobjects import TYPE_nvar, TV_fw_args, TV_fw_kwargs
+ from .baseobjects import TYPE_var, TYPE_kwargs
-def flatten(args: T.Union['TYPE_nvar', T.List['TYPE_nvar']]) -> T.List['TYPE_nvar']:
+def flatten(args: T.Union['TYPE_var', T.List['TYPE_var']]) -> T.List['TYPE_var']:
if isinstance(args, mparser.StringNode):
assert isinstance(args.value, str)
return [args.value]
if not isinstance(args, collections.abc.Sequence):
return [args]
- result: T.List['TYPE_nvar'] = []
+ result: T.List['TYPE_var'] = []
for a in args:
if isinstance(a, list):
rest = flatten(a)
@@ -51,7 +51,7 @@ def default_resolve_key(key: mparser.BaseNode) -> str:
raise InterpreterException('Invalid kwargs format.')
return key.value
-def get_callee_args(wrapped_args: T.Sequence[T.Any], want_subproject: bool = False) -> T.Tuple[T.Any, mparser.BaseNode, 'TV_fw_args', 'TV_fw_kwargs', T.Optional[str]]:
+def get_callee_args(wrapped_args: T.Sequence[T.Any], want_subproject: bool = False) -> T.Tuple[T.Any, mparser.BaseNode, T.List['TYPE_var'], 'TYPE_kwargs', T.Optional[str]]:
s = wrapped_args[0]
n = len(wrapped_args)
# Raise an error if the codepaths are not there
diff --git a/mesonbuild/interpreterbase/interpreterbase.py b/mesonbuild/interpreterbase/interpreterbase.py
index 1e4be01..0acb699 100644
--- a/mesonbuild/interpreterbase/interpreterbase.py
+++ b/mesonbuild/interpreterbase/interpreterbase.py
@@ -20,13 +20,15 @@ from .. import environment, dependencies
from .baseobjects import (
InterpreterObject,
+ MesonInterpreterObject,
MutableInterpreterObject,
+ InterpreterObjectTypeVar,
ObjectHolder,
RangeHolder,
+ TYPE_elementary,
TYPE_var,
- TYPE_nvar,
- TYPE_nkwargs,
+ TYPE_kwargs,
)
from .exceptions import (
@@ -41,25 +43,43 @@ from .exceptions import (
from .decorators import FeatureNew, builtinMethodNoKwargs
from .disabler import Disabler, is_disabled
from .helpers import check_stringlist, default_resolve_key, flatten
+from ._unholder import _unholder
import os, copy, re
import typing as T
+if T.TYPE_CHECKING:
+ from ..interpreter import Interpreter
+
+HolderMapType = T.Dict[
+ T.Type[mesonlib.HoldableObject],
+ # For some reason, this has to be a callable and can't just be ObjectHolder[InterpreterObjectTypeVar]
+ T.Callable[[InterpreterObjectTypeVar, 'Interpreter'], ObjectHolder[InterpreterObjectTypeVar]]
+]
+
+FunctionType = T.Dict[
+ str,
+ T.Callable[[mparser.BaseNode, T.List[TYPE_var], T.Dict[str, TYPE_var]], TYPE_var]
+]
class MesonVersionString(str):
pass
class InterpreterBase:
- elementary_types = (int, float, str, bool, list)
+ elementary_types = (int, str, bool, list)
def __init__(self, source_root: str, subdir: str, subproject: str):
self.source_root = source_root
- self.funcs = {} # type: T.Dict[str, T.Callable[[mparser.BaseNode, T.List[TYPE_nvar], T.Dict[str, TYPE_nvar]], TYPE_var]]
- self.builtin = {} # type: T.Dict[str, InterpreterObject]
+ self.funcs: FunctionType = {}
+ self.builtin: T.Dict[str, InterpreterObject] = {}
+ # Holder maps store a mapping from an HoldableObject to a class ObjectHolder
+ self.holder_map: HolderMapType = {}
+ self.bound_holder_map: HolderMapType = {}
self.subdir = subdir
self.root_subdir = subdir
self.subproject = subproject
- self.variables = {} # type: T.Dict[str, TYPE_var]
+ # TODO: This should actually be more strict: T.Union[TYPE_elementary, InterpreterObject]
+ self.variables: T.Dict[str, T.Union[TYPE_var, InterpreterObject]] = {}
self.argument_depth = 0
self.current_lineno = -1
# Current node set during a function call. This can be used as location
@@ -137,7 +157,7 @@ class InterpreterBase:
raise e
i += 1 # In THE FUTURE jump over blocks and stuff.
- def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[TYPE_var]:
+ def evaluate_statement(self, cur: mparser.BaseNode) -> T.Optional[T.Union[TYPE_var, InterpreterObject]]:
self.current_node = cur
if isinstance(cur, mparser.FunctionNode):
return self.function_call(cur)
@@ -191,14 +211,14 @@ class InterpreterBase:
raise InvalidCode("Unknown statement.")
return None
- def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> list:
+ def evaluate_arraystatement(self, cur: mparser.ArrayNode) -> T.List[T.Union[TYPE_var, InterpreterObject]]:
(arguments, kwargs) = self.reduce_arguments(cur.args)
if len(kwargs) > 0:
raise InvalidCode('Keyword arguments are invalid in array construction.')
return arguments
@FeatureNew('dict', '0.47.0')
- def evaluate_dictstatement(self, cur: mparser.DictNode) -> TYPE_nkwargs:
+ def evaluate_dictstatement(self, cur: mparser.DictNode) -> T.Union[TYPE_var, InterpreterObject]:
def resolve_key(key: mparser.BaseNode) -> str:
if not isinstance(key, mparser.StringNode):
FeatureNew.single_use('Dictionary entry using non literal key', '0.53.0', self.subproject)
@@ -387,7 +407,7 @@ The result of this is undefined and will become a hard error in a future Meson r
else:
raise InvalidCode('You broke me.')
- def evaluate_ternary(self, node: mparser.TernaryNode) -> TYPE_var:
+ def evaluate_ternary(self, node: mparser.TernaryNode) -> T.Union[TYPE_var, InterpreterObject]:
assert(isinstance(node, mparser.TernaryNode))
result = self.evaluate_statement(node.condition)
if isinstance(result, Disabler):
@@ -479,7 +499,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InvalidArguments('The += operator currently only works with arrays, dicts, strings or ints')
self.set_variable(varname, new_value)
- def evaluate_indexing(self, node: mparser.IndexNode) -> TYPE_var:
+ def evaluate_indexing(self, node: mparser.IndexNode) -> T.Union[TYPE_elementary, InterpreterObject]:
assert(isinstance(node, mparser.IndexNode))
iobject = self.evaluate_statement(node.iobject)
if isinstance(iobject, Disabler):
@@ -494,7 +514,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException('Key is not a string')
try:
# The cast is required because we don't have recursive types...
- return T.cast(TYPE_var, iobject[index])
+ return T.cast(T.Union[TYPE_elementary, InterpreterObject], iobject[index])
except KeyError:
raise InterpreterException('Key %s is not in dict' % index)
else:
@@ -504,35 +524,45 @@ The result of this is undefined and will become a hard error in a future Meson r
# Ignore the MyPy error, since we don't know all indexable types here
# and we handle non indexable types with an exception
# TODO maybe find a better solution
- return iobject[index] # type: ignore
+ res = iobject[index] # type: ignore
+ # Only holderify if we are dealing with `InterpreterObject`, since raw
+ # lists already store ObjectHolders
+ if isinstance(iobject, InterpreterObject):
+ return self._holderify(res)
+ else:
+ return res
except IndexError:
# We are already checking for the existence of __getitem__, so this should be save
raise InterpreterException('Index %d out of bounds of array of size %d.' % (index, len(iobject))) # type: ignore
- def function_call(self, node: mparser.FunctionNode) -> T.Optional[TYPE_var]:
+ def function_call(self, node: mparser.FunctionNode) -> T.Optional[T.Union[TYPE_elementary, InterpreterObject]]:
func_name = node.func_name
- (posargs, kwargs) = self.reduce_arguments(node.args)
+ (h_posargs, h_kwargs) = self.reduce_arguments(node.args)
+ (posargs, kwargs) = self._unholder_args(h_posargs, h_kwargs)
if is_disabled(posargs, kwargs) and func_name not in {'get_variable', 'set_variable', 'is_disabler'}:
return Disabler()
if func_name in self.funcs:
func = self.funcs[func_name]
- func_args = posargs # type: T.Any
+ func_args = posargs
if not getattr(func, 'no-args-flattening', False):
func_args = flatten(posargs)
- return func(node, func_args, kwargs)
+ res = func(node, func_args, kwargs)
+ return self._holderify(res)
else:
self.unknown_function_called(func_name)
return None
- def method_call(self, node: mparser.MethodNode) -> TYPE_var:
+ def method_call(self, node: mparser.MethodNode) -> T.Optional[T.Union[TYPE_var, InterpreterObject]]:
invokable = node.source_object
+ obj: T.Union[TYPE_var, InterpreterObject]
if isinstance(invokable, mparser.IdNode):
object_name = invokable.value
obj = self.get_variable(object_name)
else:
obj = self.evaluate_statement(invokable)
method_name = node.name
- (args, kwargs) = self.reduce_arguments(node.args)
+ (h_args, h_kwargs) = self.reduce_arguments(node.args)
+ (args, kwargs) = self._unholder_args(h_args, h_kwargs)
if is_disabled(args, kwargs):
return Disabler()
if isinstance(obj, str):
@@ -545,8 +575,6 @@ The result of this is undefined and will become a hard error in a future Meson r
return self.array_method_call(obj, method_name, args, kwargs)
if isinstance(obj, dict):
return self.dict_method_call(obj, method_name, args, kwargs)
- if isinstance(obj, mesonlib.File):
- raise InvalidArguments('File object "%s" is not callable.' % obj)
if not isinstance(obj, InterpreterObject):
raise InvalidArguments('Variable "%s" is not callable.' % object_name)
# Special case. This is the only thing you can do with a disabler
@@ -556,15 +584,48 @@ The result of this is undefined and will become a hard error in a future Meson r
return False
else:
return Disabler()
+ # TODO: InterpreterBase **really** shouldn't be in charge of checking this
if method_name == 'extract_objects':
if not isinstance(obj, ObjectHolder):
- raise InvalidArguments(f'Invalid operation "extract_objects" on variable "{object_name}"')
+ raise InvalidArguments(f'Invalid operation "extract_objects" on variable "{object_name}" of type {type(obj).__name__}')
self.validate_extraction(obj.held_object)
obj.current_node = node
- return obj.method_call(method_name, args, kwargs)
+ return self._holderify(obj.method_call(method_name, args, kwargs))
+
+ def _holderify(self, res: T.Optional[TYPE_var]) -> T.Union[TYPE_elementary, InterpreterObject]:
+ if res is None:
+ return None
+ if isinstance(res, (int, bool, str)):
+ return res
+ elif isinstance(res, list):
+ return [self._holderify(x) for x in res]
+ elif isinstance(res, dict):
+ return {k: self._holderify(v) for k, v in res.items()}
+ elif isinstance(res, mesonlib.HoldableObject):
+ # Always check for an exact match first.
+ cls = self.holder_map.get(type(res), None)
+ if cls is not None:
+ # Casts to Interpreter are required here since an assertion would
+ # not work for the `ast` module.
+ return cls(res, T.cast('Interpreter', self))
+ # Try the boundary types next.
+ for typ, cls in self.bound_holder_map.items():
+ if isinstance(res, typ):
+ return cls(res, T.cast('Interpreter', self))
+ raise mesonlib.MesonBugException(f'Object {res} of type {type(res).__name__} is neither in self.holder_map nor self.bound_holder_map.')
+ elif isinstance(res, ObjectHolder):
+ raise mesonlib.MesonBugException(f'Returned object {res} of type {type(res).__name__} is an object holder.')
+ elif isinstance(res, MesonInterpreterObject):
+ return res
+ raise mesonlib.MesonBugException(f'Unknown returned object {res} of type {type(res).__name__} in the parameters.')
+
+ def _unholder_args(self,
+ args: T.List[T.Union[TYPE_var, InterpreterObject]],
+ kwargs: T.Dict[str, T.Union[TYPE_var, InterpreterObject]]) -> T.Tuple[T.List[TYPE_var], TYPE_kwargs]:
+ return [_unholder(x) for x in args], {k: _unholder(v) for k, v in kwargs.items()}
@builtinMethodNoKwargs
- def bool_method_call(self, obj: bool, method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> T.Union[str, int]:
+ def bool_method_call(self, obj: bool, method_name: str, posargs: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int]:
if method_name == 'to_string':
if not posargs:
if obj:
@@ -587,7 +648,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException('Unknown method "%s" for a boolean.' % method_name)
@builtinMethodNoKwargs
- def int_method_call(self, obj: int, method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> T.Union[str, bool]:
+ def int_method_call(self, obj: int, method_name: str, posargs: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, bool]:
if method_name == 'is_even':
if not posargs:
return obj % 2 == 0
@@ -607,7 +668,7 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException('Unknown method "%s" for an integer.' % method_name)
@staticmethod
- def _get_one_string_posarg(posargs: T.List[TYPE_nvar], method_name: str) -> str:
+ def _get_one_string_posarg(posargs: T.List[TYPE_var], method_name: str) -> str:
if len(posargs) > 1:
m = '{}() must have zero or one arguments'
raise InterpreterException(m.format(method_name))
@@ -620,7 +681,7 @@ The result of this is undefined and will become a hard error in a future Meson r
return None
@builtinMethodNoKwargs
- def string_method_call(self, obj: str, method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> T.Union[str, int, bool, T.List[str]]:
+ def string_method_call(self, obj: str, method_name: str, posargs: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Union[str, int, bool, T.List[str]]:
if method_name == 'strip':
s1 = self._get_one_string_posarg(posargs, 'strip')
if s1 is not None:
@@ -692,7 +753,7 @@ The result of this is undefined and will become a hard error in a future Meson r
return obj.replace(posargs[0], posargs[1])
raise InterpreterException('Unknown method "%s" for a string.' % method_name)
- def format_string(self, templ: str, args: T.List[TYPE_nvar]) -> str:
+ def format_string(self, templ: str, args: T.List[TYPE_var]) -> str:
arg_strings = []
for arg in args:
if isinstance(arg, mparser.BaseNode):
@@ -713,7 +774,11 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InvalidCode('Unknown function "%s".' % func_name)
@builtinMethodNoKwargs
- def array_method_call(self, obj: T.List[TYPE_var], method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> TYPE_var:
+ def array_method_call(self,
+ obj: T.List[T.Union[TYPE_elementary, InterpreterObject]],
+ method_name: str,
+ posargs: T.List[TYPE_var],
+ kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
if method_name == 'contains':
def check_contains(el: list) -> bool:
if len(posargs) != 1:
@@ -754,7 +819,11 @@ The result of this is undefined and will become a hard error in a future Meson r
raise InterpreterException(m.format(method_name))
@builtinMethodNoKwargs
- def dict_method_call(self, obj: T.Dict[str, TYPE_var], method_name: str, posargs: T.List[TYPE_nvar], kwargs: T.Dict[str, T.Any]) -> TYPE_var:
+ def dict_method_call(self,
+ obj: T.Dict[str, T.Union[TYPE_elementary, InterpreterObject]],
+ method_name: str,
+ posargs: T.List[TYPE_var],
+ kwargs: TYPE_kwargs) -> T.Union[TYPE_var, InterpreterObject]:
if method_name in ('has_key', 'get'):
if method_name == 'has_key':
if len(posargs) != 1:
@@ -795,18 +864,20 @@ The result of this is undefined and will become a hard error in a future Meson r
args: mparser.ArgumentNode,
key_resolver: T.Callable[[mparser.BaseNode], str] = default_resolve_key,
duplicate_key_error: T.Optional[str] = None,
- ) -> T.Tuple[T.List[TYPE_nvar], TYPE_nkwargs]:
+ ) -> T.Tuple[
+ T.List[T.Union[TYPE_var, InterpreterObject]],
+ T.Dict[str, T.Union[TYPE_var, InterpreterObject]]
+ ]:
assert(isinstance(args, mparser.ArgumentNode))
if args.incorrect_order():
raise InvalidArguments('All keyword arguments must be after positional arguments.')
self.argument_depth += 1
- reduced_pos = [self.evaluate_statement(arg) for arg in args.arguments] # type: T.List[TYPE_nvar]
- reduced_kw = {} # type: TYPE_nkwargs
+ reduced_pos: T.List[T.Union[TYPE_var, InterpreterObject]] = [self.evaluate_statement(arg) for arg in args.arguments]
+ reduced_kw: T.Dict[str, T.Union[TYPE_var, InterpreterObject]] = {}
for key, val in args.kwargs.items():
reduced_key = key_resolver(key)
- reduced_val = val # type: TYPE_nvar
- if isinstance(reduced_val, mparser.BaseNode):
- reduced_val = self.evaluate_statement(reduced_val)
+ assert isinstance(val, mparser.BaseNode)
+ reduced_val = self.evaluate_statement(val)
if duplicate_key_error and reduced_key in reduced_kw:
raise InvalidArguments(duplicate_key_error.format(reduced_key))
reduced_kw[reduced_key] = reduced_val
@@ -814,7 +885,7 @@ The result of this is undefined and will become a hard error in a future Meson r
final_kw = self.expand_default_kwargs(reduced_kw)
return reduced_pos, final_kw
- def expand_default_kwargs(self, kwargs: TYPE_nkwargs) -> TYPE_nkwargs:
+ def expand_default_kwargs(self, kwargs: T.Dict[str, T.Union[TYPE_var, InterpreterObject]]) -> T.Dict[str, T.Union[TYPE_var, InterpreterObject]]:
if 'kwargs' not in kwargs:
return kwargs
to_expand = kwargs.pop('kwargs')
@@ -845,7 +916,7 @@ To specify a keyword argument, use : instead of =.''')
self.set_variable(var_name, value)
return None
- def set_variable(self, varname: str, variable: TYPE_var) -> None:
+ def set_variable(self, varname: str, variable: T.Union[TYPE_var, InterpreterObject]) -> None:
if variable is None:
raise InvalidCode('Can not assign None to variable.')
if not isinstance(varname, str):
@@ -858,7 +929,7 @@ To specify a keyword argument, use : instead of =.''')
raise InvalidCode('Tried to overwrite internal variable "%s"' % varname)
self.variables[varname] = variable
- def get_variable(self, varname: str) -> TYPE_var:
+ def get_variable(self, varname: str) -> T.Union[TYPE_var, InterpreterObject]:
if varname in self.builtin:
return self.builtin[varname]
if varname in self.variables:
@@ -869,5 +940,5 @@ To specify a keyword argument, use : instead of =.''')
return isinstance(value, (InterpreterObject, dependencies.Dependency,
str, int, list, dict, mesonlib.File))
- def validate_extraction(self, buildtarget: InterpreterObject) -> None:
+ def validate_extraction(self, buildtarget: mesonlib.HoldableObject) -> None:
raise InterpreterException('validate_extraction is not implemented in this context (please file a bug)')
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py
index 08e8b9e..3714ecd 100644
--- a/mesonbuild/mesonlib/universal.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -18,6 +18,7 @@ import enum
import sys
import stat
import time
+import abc
import platform, subprocess, operator, os, shlex, shutil, re
import collections
from functools import lru_cache, wraps, total_ordering
@@ -46,12 +47,14 @@ __all__ = [
'an_unpicklable_object',
'python_command',
'project_meson_versions',
+ 'HoldableObject',
'File',
'FileMode',
'GitException',
'LibType',
'MachineChoice',
'MesonException',
+ 'MesonBugException',
'EnvironmentException',
'FileOrString',
'GitException',
@@ -128,7 +131,6 @@ __all__ = [
'substitute_values',
'substring_is_in_list',
'typeslistify',
- 'unholder',
'verbose_git',
'version_compare',
'version_compare_condition_with_min',
@@ -164,6 +166,14 @@ class MesonException(Exception):
self.colno = colno
+class MesonBugException(MesonException):
+ '''Exceptions thrown when there is a clear Meson bug that should be reported'''
+
+ def __init__(self, msg: str, file: T.Optional[str] = None,
+ lineno: T.Optional[int] = None, colno: T.Optional[int] = None):
+ super().__init__(msg + '\n\n This is a Meson bug and should be reported!',
+ file=file, lineno=lineno, colno=colno)
+
class EnvironmentException(MesonException):
'''Exceptions thrown while processing and creating the build environment'''
@@ -258,6 +268,10 @@ def check_direntry_issues(direntry_array: T.Union[T.List[T.Union[str, bytes]], s
import threading
an_unpicklable_object = threading.Lock()
+class HoldableObject(metaclass=abc.ABCMeta):
+ ''' Dummy base class for all objects that can be
+ held by an interpreter.baseobjects.ObjectHolder '''
+
class FileMode:
# The first triad is for owner permissions, the second for group permissions,
# and the third for others (everyone else).
@@ -357,7 +371,7 @@ dot_C_dot_H_warning = """You are using .C or .H files in your project. This is d
Visual Studio compiler, as it treats .C files as C code, unless you add
the /TP compiler flag, but this is unreliable.
See https://github.com/mesonbuild/meson/pull/8747 for the discussions."""
-class File:
+class File(HoldableObject):
def __init__(self, is_built: bool, subdir: str, fname: str):
if fname.endswith(".C") or fname.endswith(".H"):
mlog.warning(dot_C_dot_H_warning, once=True)
@@ -1231,26 +1245,6 @@ def replace_if_different(dst: str, dst_tmp: str) -> None:
os.unlink(dst_tmp)
-@T.overload
-def unholder(item: 'ObjectHolder[_T]') -> _T: ...
-
-@T.overload
-def unholder(item: T.List['ObjectHolder[_T]']) -> T.List[_T]: ...
-
-@T.overload
-def unholder(item: T.List[_T]) -> T.List[_T]: ...
-
-@T.overload
-def unholder(item: T.List[T.Union[_T, 'ObjectHolder[_T]']]) -> T.List[_T]: ...
-
-def unholder(item): # type: ignore # TODO fix overload (somehow)
- """Get the held item of an object holder or list of object holders."""
- if isinstance(item, list):
- return [i.held_object if hasattr(i, 'held_object') else i for i in item]
- if hasattr(item, 'held_object'):
- return item.held_object
- return item
-
def listify(item: T.Any, flatten: bool = True) -> T.List[T.Any]:
'''
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py
index 69bb552..2b53de5 100644
--- a/mesonbuild/modules/__init__.py
+++ b/mesonbuild/modules/__init__.py
@@ -18,13 +18,12 @@
import os
from .. import build
-from ..mesonlib import unholder, relpath
+from ..mesonlib import relpath, HoldableObject
import typing as T
if T.TYPE_CHECKING:
from ..interpreter import Interpreter
- from ..interpreter.interpreterobjects import IncludeDirsHolder, ExternalProgramHolder
- from ..interpreterbase import TYPE_var, TYPE_nvar, TYPE_nkwargs
+ from ..interpreterbase import TYPE_var, TYPE_kwargs
from ..programs import ExternalProgram
class ModuleState:
@@ -61,7 +60,7 @@ class ModuleState:
self.target_machine = interpreter.builtin['target_machine'].held_object
self.current_node = interpreter.current_node
- def get_include_args(self, include_dirs: T.Iterable[T.Union[str, 'IncludeDirsHolder']], prefix: str = '-I') -> T.List[str]:
+ def get_include_args(self, include_dirs: T.Iterable[T.Union[str, build.IncludeDirs]], prefix: str = '-I') -> T.List[str]:
if not include_dirs:
return []
@@ -69,7 +68,7 @@ class ModuleState:
builddir = self.environment.get_build_dir()
dirs_str: T.List[str] = []
- for dirs in unholder(include_dirs):
+ for dirs in include_dirs:
if isinstance(dirs, str):
dirs_str += [f'{prefix}{dirs}']
continue
@@ -89,14 +88,17 @@ class ModuleState:
def find_program(self, prog: T.Union[str, T.List[str]], required: bool = True,
version_func: T.Optional[T.Callable[['ExternalProgram'], str]] = None,
- wanted: T.Optional[str] = None) -> 'ExternalProgramHolder':
+ wanted: T.Optional[str] = None) -> 'ExternalProgram':
return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted)
-class ModuleObject:
+class ModuleObject(HoldableObject):
"""Base class for all objects returned by modules
"""
def __init__(self) -> None:
- self.methods = {} # type: T.Dict[str, T.Callable[[ModuleState, T.List[TYPE_nvar], TYPE_nkwargs], T.Union[ModuleReturnValue, TYPE_var]]]
+ self.methods: T.Dict[
+ str,
+ T.Callable[[ModuleState, T.List[TYPE_var], TYPE_kwargs], T.Union[ModuleReturnValue, TYPE_var]]
+ ] = {}
class MutableModuleObject(ModuleObject):
pass
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
index fac3b24..cb37edc 100644
--- a/mesonbuild/modules/cmake.py
+++ b/mesonbuild/modules/cmake.py
@@ -18,9 +18,9 @@ import typing as T
from . import ExtensionModule, ModuleReturnValue, ModuleObject
-from .. import build, mesonlib, mlog
+from .. import build, mesonlib, mlog, dependencies
from ..cmake import SingleTargetOptions, TargetOptions, cmake_defines_to_args
-from ..interpreter import ConfigurationDataHolder, SubprojectHolder, DependencyHolder
+from ..interpreter import ConfigurationDataObject, SubprojectHolder
from ..interpreterbase import (
FeatureNew,
FeatureNewKwargs,
@@ -32,6 +32,7 @@ from ..interpreterbase import (
noKwargs,
InvalidArguments,
+ InterpreterException,
)
from ..programs import ExternalProgram
@@ -109,11 +110,11 @@ class CMakeSubproject(ModuleObject):
def dependency(self, state, args, kwargs):
info = self._args_to_info(args)
orig = self.get_variable(state, [info['dep']], {})
- assert isinstance(orig, DependencyHolder)
- actual = orig.include_type_method([], {})
+ assert isinstance(orig, dependencies.Dependency)
+ actual = orig.include_type
if 'include_type' in kwargs and kwargs['include_type'] != actual:
mlog.debug('Current include type is {}. Converting to requested {}'.format(actual, kwargs['include_type']))
- return orig.as_system_method([kwargs['include_type']], {})
+ return orig.generate_system_dependency(kwargs['include_type'])
return orig
@noKwargs
@@ -351,7 +352,7 @@ class CmakeModule(ExtensionModule):
if 'configuration' not in kwargs:
raise mesonlib.MesonException('"configuration" not specified.')
conf = kwargs['configuration']
- if not isinstance(conf, ConfigurationDataHolder):
+ if not isinstance(conf, ConfigurationDataObject):
raise mesonlib.MesonException('Argument "configuration" is not of type configuration_data')
prefix = state.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
@@ -365,7 +366,7 @@ class CmakeModule(ExtensionModule):
extra = PACKAGE_INIT_EXT.replace('@absInstallDir@', abs_install_dir)
extra = extra.replace('@installPrefix@', prefix)
- self.create_package_file(ifile_abs, ofile_abs, PACKAGE_RELATIVE_PATH, extra, conf.held_object)
+ self.create_package_file(ifile_abs, ofile_abs, PACKAGE_RELATIVE_PATH, extra, conf.conf_data)
conf.mark_used()
conffile = os.path.normpath(inputfile.relative_name())
@@ -389,7 +390,7 @@ class CmakeModule(ExtensionModule):
raise InterpreterException('"options" cannot be used together with "cmake_options"')
dirname = args[0]
subp = self.interpreter.do_subproject(dirname, 'cmake', kwargs)
- if not subp.held_object:
+ if not subp.found():
return subp
return CMakeSubproject(subp, dirname)
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 0a72ad6..0ae1c70 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -30,7 +30,7 @@ from . import ExtensionModule
from . import ModuleReturnValue
from ..mesonlib import (
MachineChoice, MesonException, OrderedSet, Popen_safe, extract_as_list,
- join_args, unholder,
+ join_args, HoldableObject
)
from ..dependencies import Dependency, PkgConfigDependency, InternalDependency
from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, FeatureNew, FeatureNewKwargs, FeatureDeprecatedKwargs
@@ -39,6 +39,7 @@ from ..build import CustomTarget, CustomTargetIndex, GeneratedList
if T.TYPE_CHECKING:
from ..compilers import Compiler
+ from ..interpreter import Interpreter
# gresource compilation is broken due to the way
# the resource compiler and Ninja clash about it
@@ -103,14 +104,13 @@ class GnomeModule(ExtensionModule):
def _get_dep(self, state, depname, native=False, required=True):
kwargs = {'native': native, 'required': required}
- holder = self.interpreter.func_dependency(state.current_node, [depname], kwargs)
- return holder.held_object
+ return self.interpreter.func_dependency(state.current_node, [depname], kwargs)
def _get_native_binary(self, state, name, depname, varname, required=True):
# Look in overrides in case glib/gtk/etc are built as subproject
prog = self.interpreter.program_from_overrides([name], [])
if prog is not None:
- return unholder(prog)
+ return prog
# Look in machine file
prog = state.environment.lookup_binary_entry(MachineChoice.HOST, name)
@@ -125,7 +125,7 @@ class GnomeModule(ExtensionModule):
return ExternalProgram(name, value)
# Normal program lookup
- return unholder(state.find_program(name, required=required))
+ return state.find_program(name, required=required)
@permittedKwargs({'glib_compile_schemas', 'gio_querymodules', 'gtk_update_icon_cache'})
@noPosargs
@@ -179,7 +179,7 @@ class GnomeModule(ExtensionModule):
# Validate dependencies
subdirs = []
depends = []
- for (ii, dep) in enumerate(unholder(dependencies)):
+ for (ii, dep) in enumerate(dependencies):
if isinstance(dep, mesonlib.File):
subdirs.append(dep.subdir)
elif isinstance(dep, (build.CustomTarget, build.CustomTargetIndex)):
@@ -323,7 +323,7 @@ class GnomeModule(ExtensionModule):
subdirs = []
for resfile in dep_files[:]:
resbasename = os.path.basename(resfile)
- for dep in unholder(dependencies):
+ for dep in dependencies:
if isinstance(dep, mesonlib.File):
if dep.fname != resbasename:
continue
@@ -399,7 +399,7 @@ class GnomeModule(ExtensionModule):
# require two args in order, such as -framework AVFoundation
external_ldflags_nodedup = []
gi_includes = OrderedSet()
- deps = mesonlib.unholder(mesonlib.listify(deps))
+ deps = mesonlib.listify(deps)
for dep in deps:
if isinstance(dep, Dependency):
@@ -409,7 +409,7 @@ class GnomeModule(ExtensionModule):
if isinstance(dep, InternalDependency):
cflags.update(dep.get_compile_args())
cflags.update(state.get_include_args(dep.include_directories))
- for lib in unholder(dep.libraries):
+ for lib in dep.libraries:
if isinstance(lib, build.SharedLibrary):
internal_ldflags.update(self._get_link_args(state, lib, depends, include_rpath))
libdepflags = self._get_dependencies_flags(lib.get_external_deps(), state, depends, include_rpath,
@@ -426,7 +426,7 @@ class GnomeModule(ExtensionModule):
external_ldflags.update(extdepflags[2])
external_ldflags_nodedup += extdepflags[3]
gi_includes.update(extdepflags[4])
- for source in unholder(dep.sources):
+ for source in dep.sources:
if isinstance(source, GirTarget):
gi_includes.update([os.path.join(state.environment.get_build_dir(),
source.get_subdir())])
@@ -480,9 +480,6 @@ class GnomeModule(ExtensionModule):
return cflags, internal_ldflags, external_ldflags, external_ldflags_nodedup, gi_includes
def _unwrap_gir_target(self, girtarget, state):
- while hasattr(girtarget, 'held_object'):
- girtarget = girtarget.held_object
-
if not isinstance(girtarget, (build.Executable, build.SharedLibrary,
build.StaticLibrary)):
raise MesonException('Gir target must be an executable or library')
@@ -512,8 +509,6 @@ class GnomeModule(ExtensionModule):
@functools.lru_cache(maxsize=None)
def _gir_has_option(self, option) -> bool:
exe = self.giscanner
- if hasattr(exe, 'held_object'):
- exe = exe.held_object
if isinstance(exe, OverrideProgram):
# Handle overridden g-ir-scanner
assert option in ['--extra-library', '--sources-top-dirs']
@@ -539,7 +534,7 @@ class GnomeModule(ExtensionModule):
link_with = mesonlib.extract_as_list(kwargs, 'link_with', pop = True)
for link in link_with:
- ret += self._get_link_args(state, link.held_object, depends,
+ ret += self._get_link_args(state, link, depends,
use_gir_args=True)
return ret
@@ -549,7 +544,7 @@ class GnomeModule(ExtensionModule):
if 'includes' in kwargs:
includes = mesonlib.extract_as_list(kwargs, 'includes', pop = True)
- for inc in unholder(includes):
+ for inc in includes:
if isinstance(inc, str):
ret += [f'--include={inc}']
elif isinstance(inc, GirTarget):
@@ -605,7 +600,7 @@ class GnomeModule(ExtensionModule):
def _scan_inc_dirs(self, kwargs):
ret = mesonlib.extract_as_list(kwargs, 'include_directories', pop = True)
for incd in ret:
- if not isinstance(incd.held_object, (str, build.IncludeDirs)):
+ if not isinstance(incd, (str, build.IncludeDirs)):
raise MesonException(
'Gir include dirs should be include_directories().')
return ret
@@ -708,7 +703,7 @@ class GnomeModule(ExtensionModule):
gir_filelist_filename = os.path.join(gir_filelist_dir, f'{ns}_{nsversion}_gir_filelist')
with open(gir_filelist_filename, 'w', encoding='utf-8') as gir_filelist:
- for s in unholder(libsources):
+ for s in libsources:
if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
for custom_output in s.get_outputs():
gir_filelist.write(os.path.join(state.environment.get_build_dir(),
@@ -763,11 +758,11 @@ class GnomeModule(ExtensionModule):
# dependencies and also find the include directories needed for the
# typelib generation custom target below.
typelib_includes = []
- for dep in unholder(deps):
+ for dep in deps:
# Add a dependency on each GirTarget listed in dependencies and add
# the directory where it will be generated to the typelib includes
if isinstance(dep, InternalDependency):
- for source in unholder(dep.sources):
+ for source in dep.sources:
if isinstance(source, GirTarget) and source not in depends:
depends.append(source)
subdir = os.path.join(state.environment.get_build_dir(),
@@ -846,7 +841,7 @@ class GnomeModule(ExtensionModule):
langs_compilers = self._get_girtargets_langs_compilers(girtargets)
cflags, internal_ldflags, external_ldflags = self._get_langs_compilers_flags(state, langs_compilers)
deps = self._get_gir_targets_deps(girtargets)
- deps += mesonlib.unholder(extract_as_list(kwargs, 'dependencies', pop=True))
+ deps += extract_as_list(kwargs, 'dependencies', pop=True)
deps += [gir_dep]
typelib_includes = self._gather_typelib_includes_and_update_depends(state, deps, depends)
# ldflags will be misinterpreted by gir scanner (showing
@@ -898,7 +893,7 @@ class GnomeModule(ExtensionModule):
if fatal_warnings:
scan_command.append('--warn-error')
- generated_files = [unholder(f) for f in libsources if isinstance(unholder(f), (GeneratedList, CustomTarget, CustomTargetIndex))]
+ generated_files = [f for f in libsources if isinstance(f, (GeneratedList, CustomTarget, CustomTargetIndex))]
scan_target = self._make_gir_target(state, girfile, scan_command, generated_files, depends, kwargs)
@@ -1044,8 +1039,7 @@ class GnomeModule(ExtensionModule):
src_dirs = mesonlib.extract_as_list(kwargs, 'src_dir')
header_dirs = []
for src_dir in src_dirs:
- if hasattr(src_dir, 'held_object'):
- src_dir = src_dir.held_object
+ if isinstance(src_dir, HoldableObject):
if not isinstance(src_dir, build.IncludeDirs):
raise MesonException('Invalid keyword argument for src_dir.')
for inc_dir in src_dir.get_incdirs():
@@ -1068,7 +1062,7 @@ class GnomeModule(ExtensionModule):
for tool in ['scan', 'scangobj', 'mkdb', 'mkhtml', 'fixxref']:
program_name = 'gtkdoc-' + tool
program = state.find_program(program_name)
- path = program.held_object.get_path()
+ path = program.get_path()
args.append(f'--{program_name}={path}')
if namespace:
args.append('--namespace=' + namespace)
@@ -1082,7 +1076,7 @@ class GnomeModule(ExtensionModule):
depends = []
content_files = []
- for s in unholder(mesonlib.extract_as_list(kwargs, 'content_files')):
+ for s in mesonlib.extract_as_list(kwargs, 'content_files'):
if isinstance(s, (build.CustomTarget, build.CustomTargetIndex)):
depends.append(s)
for o in s.get_outputs():
@@ -1134,14 +1128,14 @@ class GnomeModule(ExtensionModule):
def _get_build_args(self, kwargs, state, depends):
args = []
- deps = mesonlib.unholder(extract_as_list(kwargs, 'dependencies'))
+ deps = extract_as_list(kwargs, 'dependencies')
cflags = []
cflags.extend(mesonlib.stringlistify(kwargs.pop('c_args', [])))
deps_cflags, internal_ldflags, external_ldflags, gi_includes = \
self._get_dependencies_flags(deps, state, depends, include_rpath=True)
inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories')
for incd in inc_dirs:
- if not isinstance(incd.held_object, (str, build.IncludeDirs)):
+ if not isinstance(incd, (str, build.IncludeDirs)):
raise MesonException(
'Gir include dirs should be include_directories().')
@@ -1678,7 +1672,7 @@ G_END_DECLS'''
vapi_includes = []
ret = []
remaining_args = []
- for arg in unholder(arg_list):
+ for arg in arg_list:
if isinstance(arg, InternalDependency):
targets = [t for t in arg.sources if isinstance(t, VapiTarget)]
for target in targets:
@@ -1752,11 +1746,11 @@ G_END_DECLS'''
for i in inputs:
if isinstance(i, str):
cmd.append(os.path.join(source_dir, i))
- elif hasattr(i, 'held_object') and isinstance(i.held_object, GirTarget):
- link_with += self._get_vapi_link_with(i.held_object)
+ elif isinstance(i, GirTarget):
+ link_with += self._get_vapi_link_with(i)
subdir = os.path.join(state.environment.get_build_dir(),
- i.held_object.get_subdir())
- gir_file = os.path.join(subdir, i.held_object.get_outputs()[0])
+ i.get_subdir())
+ gir_file = os.path.join(subdir, i.get_outputs()[0])
cmd.append(gir_file)
else:
raise MesonException('Input must be a str or GirTarget')
@@ -1791,4 +1785,10 @@ G_END_DECLS'''
return ModuleReturnValue(rv, created_values)
def initialize(*args, **kwargs):
- return GnomeModule(*args, **kwargs)
+ mod = GnomeModule(*args, **kwargs)
+ mod.interpreter.append_holder_map(GResourceTarget, interpreter.CustomTargetHolder)
+ mod.interpreter.append_holder_map(GResourceHeaderTarget, interpreter.CustomTargetHolder)
+ mod.interpreter.append_holder_map(GirTarget, interpreter.CustomTargetHolder)
+ mod.interpreter.append_holder_map(TypelibTarget, interpreter.CustomTargetHolder)
+ mod.interpreter.append_holder_map(VapiTarget, interpreter.CustomTargetHolder)
+ return mod
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 90635e6..26026fb 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -105,7 +105,6 @@ class HotdocTargetBuilder:
self.cmd.extend([option, value])
def check_extra_arg_type(self, arg, value):
- value = getattr(value, 'held_object', value)
if isinstance(value, list):
for v in value:
self.check_extra_arg_type(arg, v)
@@ -188,7 +187,6 @@ class HotdocTargetBuilder:
def process_dependencies(self, deps):
cflags = set()
for dep in mesonlib.listify(ensure_list(deps)):
- dep = getattr(dep, "held_object", dep)
if isinstance(dep, InternalDependency):
inc_args = self.state.get_include_args(dep.include_directories)
cflags.update([self.replace_dirs_in_string(x)
@@ -232,7 +230,6 @@ class HotdocTargetBuilder:
def flatten_config_command(self):
cmd = []
for arg in mesonlib.listify(self.cmd, flatten=True):
- arg = getattr(arg, 'held_object', arg)
if isinstance(arg, mesonlib.File):
arg = arg.absolute_path(self.state.environment.get_source_dir(),
self.state.environment.get_build_dir())
@@ -371,7 +368,7 @@ class HotdocTargetHolder(CustomTargetHolder):
def config_path_method(self, *args, **kwargs):
conf = self.held_object.hotdoc_conf.absolute_path(self.interpreter.environment.source_dir,
self.interpreter.environment.build_dir)
- return self.interpreter.holderify(conf)
+ return conf
class HotdocTarget(build.CustomTarget):
@@ -422,7 +419,7 @@ class HotDocModule(ExtensionModule):
project_name = args[0]
builder = HotdocTargetBuilder(project_name, state, self.hotdoc, self.interpreter, kwargs)
target, install_script = builder.make_targets()
- targets = [HotdocTargetHolder(target, self.interpreter)]
+ targets = [target]
if install_script:
targets.append(install_script)
@@ -430,4 +427,6 @@ class HotDocModule(ExtensionModule):
def initialize(interpreter):
- return HotDocModule(interpreter)
+ mod = HotDocModule(interpreter)
+ mod.interpreter.append_holder_map(HotdocTarget, HotdocTargetHolder)
+ return mod
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index 98a8b0d..f10fdbe 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -108,7 +108,8 @@ class I18nModule(ExtensionModule):
kwargs['command'] = command
inputfile = kwargs['input']
- if hasattr(inputfile, 'held_object'):
+ # I have no idea why/how this if isinstance(inputfile, mesonlib.HoldableObject) works / used to work...
+ if isinstance(inputfile, mesonlib.HoldableObject):
ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, state.subproject, kwargs)
else:
if isinstance(inputfile, list):
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 5cebd28..f42c1e6 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os, types
+import os
from pathlib import PurePath
from .. import build
@@ -22,7 +22,9 @@ from .. import mesonlib
from .. import mlog
from . import ModuleReturnValue
from . import ExtensionModule
-from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs
+from ..interpreterbase import permittedKwargs, FeatureNew, FeatureNewKwargs, TYPE_var, TYPE_kwargs
+
+import typing as T
already_warned_objs = set()
@@ -75,7 +77,7 @@ class DependenciesHelper:
def _process_reqs(self, reqs):
'''Returns string names of requirements'''
processed_reqs = []
- for obj in mesonlib.unholder(mesonlib.listify(reqs)):
+ for obj in mesonlib.listify(reqs):
if not isinstance(obj, str):
FeatureNew.single_use('pkgconfig.generate requirement from non-string object', '0.46.0', self.state.subproject)
if hasattr(obj, 'generated_pc'):
@@ -108,13 +110,13 @@ class DependenciesHelper:
def add_cflags(self, cflags):
self.cflags += mesonlib.stringlistify(cflags)
- def _process_libs(self, libs, public):
- libs = mesonlib.unholder(mesonlib.listify(libs))
+ def _process_libs(self, libs, public: bool):
+ libs = mesonlib.listify(libs)
+ libs = [x.get_preferred_library() if isinstance(x, build.BothLibraries) else x for x in libs]
processed_libs = []
processed_reqs = []
processed_cflags = []
for obj in libs:
- shared_library_only = getattr(obj, 'shared_library_only', False)
if hasattr(obj, 'pcdep'):
pcdeps = mesonlib.listify(obj.pcdep)
for d in pcdeps:
@@ -136,7 +138,7 @@ class DependenciesHelper:
if obj.found():
processed_libs += obj.get_link_args()
processed_cflags += obj.get_compile_args()
- elif isinstance(obj, build.SharedLibrary) and shared_library_only:
+ elif isinstance(obj, build.SharedLibrary) and obj.shared_library_only:
# Do not pull dependencies for shared libraries because they are
# only required for static linking. Adding private requires has
# the side effect of exposing their cflags, which is the
@@ -161,7 +163,7 @@ class DependenciesHelper:
elif isinstance(obj, str):
processed_libs.append(obj)
else:
- raise mesonlib.MesonException('library argument not a string, library or dependency object.')
+ raise mesonlib.MesonException(f'library argument of type {type(obj).__name__} not a string, library or dependency object.')
return processed_libs, processed_reqs, processed_cflags
@@ -330,9 +332,9 @@ class PkgConfigModule(ExtensionModule):
except ValueError:
return subdir.as_posix()
- def generate_pkgconfig_file(self, state, deps, subdirs, name, description,
- url, version, pcfile, conflicts, variables,
- unescaped_variables, uninstalled=False, dataonly=False):
+ def _generate_pkgconfig_file(self, state, deps, subdirs, name, description,
+ url, version, pcfile, conflicts, variables,
+ unescaped_variables, uninstalled=False, dataonly=False):
coredata = state.environment.get_coredata()
if uninstalled:
outdir = os.path.join(state.environment.build_dir, 'meson-uninstalled')
@@ -452,6 +454,19 @@ class PkgConfigModule(ExtensionModule):
if cflags and not dataonly:
ofile.write('Cflags: {}\n'.format(' '.join(cflags)))
+
+ @staticmethod
+ def _handle_both_libraries(args: T.List[TYPE_var], kwargs: TYPE_kwargs) -> T.Tuple[T.List[TYPE_var], TYPE_kwargs]:
+ def _do_extract(arg: TYPE_var) -> TYPE_var:
+ if isinstance(arg, list):
+ return [_do_extract(x) for x in arg]
+ elif isinstance(arg, dict):
+ return {k: _do_extract(v) for k, v in arg.items()}
+ elif isinstance(arg, build.BothLibraries):
+ return arg.get_preferred_library()
+ return arg
+ return [_do_extract(x) for x in args], {k: _do_extract(v) for k, v in kwargs.items()}
+
@FeatureNewKwargs('pkgconfig.generate', '0.59.0', ['unescaped_variables', 'unescaped_uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.54.0', ['uninstalled_variables'])
@FeatureNewKwargs('pkgconfig.generate', '0.42.0', ['extra_cflags'])
@@ -469,11 +484,12 @@ class PkgConfigModule(ExtensionModule):
default_name = None
mainlib = None
default_subdirs = ['.']
+ args, kwargs = PkgConfigModule._handle_both_libraries(args, kwargs)
if not args and 'version' not in kwargs:
FeatureNew.single_use('pkgconfig.generate implicit version keyword', '0.46.0', state.subproject)
elif len(args) == 1:
FeatureNew.single_use('pkgconfig.generate optional positional argument', '0.46.0', state.subproject)
- mainlib = getattr(args[0], 'held_object', args[0])
+ mainlib = args[0]
if not isinstance(mainlib, (build.StaticLibrary, build.SharedLibrary)):
raise mesonlib.MesonException('Pkgconfig_gen first positional argument must be a library object')
default_name = mainlib.name
@@ -556,7 +572,7 @@ class PkgConfigModule(ExtensionModule):
pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'pkgconfig')
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
- self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
+ self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
unescaped_variables, False, dataonly)
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, None, state.subproject)
@@ -566,7 +582,7 @@ class PkgConfigModule(ExtensionModule):
unescaped_variables = parse_variable_list(unescaped_variables)
pcfile = filebase + '-uninstalled.pc'
- self.generate_pkgconfig_file(state, deps, subdirs, name, description, url,
+ self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
unescaped_variables, uninstalled=True, dataonly=dataonly)
# Associate the main library with this generated pc file. If the library
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index f3bcfab..46fd27b 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -50,6 +50,7 @@ class PythonDependency(SystemDependency):
self.variables = python_holder.variables
self.paths = python_holder.paths
self.link_libpython = python_holder.link_libpython
+ self.info: T.Optional[T.Dict[str, str]] = None
if mesonlib.version_compare(self.version, '>= 3.0'):
self.major_version = 3
else:
@@ -278,12 +279,20 @@ print (json.dumps ({
}))
'''
+class PythonExternalProgram(ExternalProgram):
+ def __init__(self, name: str, command: T.Optional[T.List[str]] = None, ext_prog: T.Optional[ExternalProgram] = None):
+ if ext_prog is None:
+ super().__init__(name, command=command, silent=True)
+ else:
+ self.name = ext_prog.name
+ self.command = ext_prog.command
+ self.path = ext_prog.path
+ self.info: T.Dict[str, str] = {}
class PythonInstallation(ExternalProgramHolder):
- def __init__(self, interpreter, python, info):
- ExternalProgramHolder.__init__(self, python, interpreter.subproject)
- self.interpreter = interpreter
- self.subproject = self.interpreter.subproject
+ def __init__(self, python, interpreter):
+ ExternalProgramHolder.__init__(self, python, interpreter)
+ info = python.info
prefix = self.interpreter.environment.coredata.get_option(mesonlib.OptionKey('prefix'))
self.variables = info['variables']
self.paths = info['paths']
@@ -325,11 +334,10 @@ class PythonInstallation(ExternalProgramHolder):
# behavior. See https://github.com/mesonbuild/meson/issues/4117
if not self.link_libpython:
new_deps = []
- for holder in mesonlib.extract_as_list(kwargs, 'dependencies'):
- dep = holder.held_object
+ for dep in mesonlib.extract_as_list(kwargs, 'dependencies'):
if isinstance(dep, PythonDependency):
- holder = self.interpreter.holderify(dep.get_partial_dependency(compile_args=True))
- new_deps.append(holder)
+ dep = dep.get_partial_dependency(compile_args=True)
+ new_deps.append(dep)
kwargs['dependencies'] = new_deps
suffix = self.variables.get('EXT_SUFFIX') or self.variables.get('SO') or self.variables.get('.so')
@@ -360,7 +368,7 @@ class PythonInstallation(ExternalProgramHolder):
dep = PythonDependency(self, self.interpreter.environment, kwargs)
if required and not dep.found():
raise mesonlib.MesonException('Python dependency not found')
- return self.interpreter.holderify(dep)
+ return dep
@permittedKwargs(['pure', 'subdir'])
def install_sources_method(self, args, kwargs):
@@ -377,7 +385,7 @@ class PythonInstallation(ExternalProgramHolder):
else:
kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir)
- return self.interpreter.holderify(self.interpreter.func_install_data(None, args, kwargs))
+ return self.interpreter.func_install_data(None, args, kwargs)
@noPosargs
@permittedKwargs(['pure', 'subdir'])
@@ -519,25 +527,26 @@ class PythonModule(ExtensionModule):
if disabled:
mlog.log('Program', name_or_path or 'python', 'found:', mlog.red('NO'), '(disabled by:', mlog.bold(feature), ')')
- return ExternalProgramHolder(NonExistingExternalProgram(), state.subproject)
+ return NonExistingExternalProgram()
if not name_or_path:
- python = ExternalProgram('python3', mesonlib.python_command, silent=True)
+ python = PythonExternalProgram('python3', mesonlib.python_command)
else:
- python = ExternalProgram.from_entry('python3', name_or_path)
+ tmp_python = ExternalProgram.from_entry('python3', name_or_path)
+ python = PythonExternalProgram('python3', ext_prog=tmp_python)
if not python.found() and mesonlib.is_windows():
pythonpath = self._get_win_pythonpath(name_or_path)
if pythonpath is not None:
name_or_path = pythonpath
- python = ExternalProgram(name_or_path, silent=True)
+ python = PythonExternalProgram(name_or_path)
# Last ditch effort, python2 or python3 can be named python
# on various platforms, let's not give up just yet, if an executable
# named python is available and has a compatible version, let's use
# it
if not python.found() and name_or_path in ['python2', 'python3']:
- python = ExternalProgram('python', silent=True)
+ python = PythonExternalProgram('python')
if python.found() and want_modules:
for mod in want_modules:
@@ -566,11 +575,11 @@ class PythonModule(ExtensionModule):
if not python.found():
if required:
raise mesonlib.MesonException('{} not found'.format(name_or_path or 'python'))
- res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject)
+ return NonExistingExternalProgram()
elif missing_modules:
if required:
raise mesonlib.MesonException('{} is missing modules: {}'.format(name_or_path or 'python', ', '.join(missing_modules)))
- res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject)
+ return NonExistingExternalProgram()
else:
# Sanity check, we expect to have something that at least quacks in tune
try:
@@ -586,14 +595,17 @@ class PythonModule(ExtensionModule):
mlog.debug(stderr)
if isinstance(info, dict) and 'version' in info and self._check_version(name_or_path, info['version']):
- res = PythonInstallation(self.interpreter, python, info)
+ python.info = info
+ return python
else:
- res = ExternalProgramHolder(NonExistingExternalProgram(), state.subproject)
if required:
raise mesonlib.MesonException(f'{python} is not a valid python or it is missing setuptools')
+ return NonExistingExternalProgram()
- return res
+ raise mesonlib.MesonBugException('Unreachable code was reached (PythonModule.find_installation).')
def initialize(*args, **kwargs):
- return PythonModule(*args, **kwargs)
+ mod = PythonModule(*args, **kwargs)
+ mod.interpreter.append_holder_map(PythonExternalProgram, PythonInstallation)
+ return mod
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 7d752db..ae45e4d 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from mesonbuild import coredata
import os
import shutil
import typing as T
@@ -20,21 +21,19 @@ import xml.etree.ElementTree as ET
from . import ModuleReturnValue, ExtensionModule
from .. import build
-from .. import mesonlib
from .. import mlog
-from ..dependencies import find_external_dependency
+from ..dependencies import find_external_dependency, Dependency, ExternalLibrary
+from ..mesonlib import MesonException, File, FileOrString, version_compare, Popen_safe
+from . import ModuleReturnValue, ExtensionModule
from ..interpreter import extract_required_kwarg
-from ..interpreter.interpreterobjects import DependencyHolder, ExternalLibraryHolder, IncludeDirsHolder, FeatureOptionHolder, GeneratedListHolder
from ..interpreterbase import ContainerTypeInfo, FeatureDeprecated, KwargInfo, noPosargs, FeatureNew, typed_kwargs
-from ..mesonlib import MesonException, File
-from ..programs import NonExistingExternalProgram
+from ..programs import ExternalProgram, NonExistingExternalProgram
if T.TYPE_CHECKING:
from . import ModuleState
from ..dependencies.qt import QtPkgConfigDependency, QmakeQtDependency
from ..interpreter import Interpreter
from ..interpreter import kwargs
- from ..programs import ExternalProgram
QtDependencyType = T.Union[QtPkgConfigDependency, QmakeQtDependency]
@@ -45,7 +44,7 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Resource Compiler method."""
name: T.Optional[str]
- sources: T.List[mesonlib.FileOrString]
+ sources: T.List[FileOrString]
extra_args: T.List[str]
method: str
@@ -53,7 +52,7 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Ui Compiler method."""
- sources: T.List[mesonlib.FileOrString]
+ sources: T.List[FileOrString]
extra_args: T.List[str]
method: str
@@ -61,25 +60,25 @@ if T.TYPE_CHECKING:
"""Keyword arguments for the Moc Compiler method."""
- sources: T.List[mesonlib.FileOrString]
- headers: T.List[mesonlib.FileOrString]
+ sources: T.List[FileOrString]
+ headers: T.List[FileOrString]
extra_args: T.List[str]
method: str
- include_directories: T.List[IncludeDirsHolder]
- dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]]
+ include_directories: T.List[T.Union[str, build.IncludeDirs]]
+ dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
class PreprocessKwArgs(TypedDict):
- sources: T.List[mesonlib.FileOrString]
- moc_sources: T.List[mesonlib.FileOrString]
- moc_headers: T.List[mesonlib.FileOrString]
- qresources: T.List[mesonlib.FileOrString]
- ui_files: T.List[mesonlib.FileOrString]
+ sources: T.List[FileOrString]
+ moc_sources: T.List[FileOrString]
+ moc_headers: T.List[FileOrString]
+ qresources: T.List[FileOrString]
+ ui_files: T.List[FileOrString]
moc_extra_arguments: T.List[str]
rcc_extra_arguments: T.List[str]
uic_extra_arguments: T.List[str]
- include_directories: T.List[IncludeDirsHolder]
- dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]]
+ include_directories: T.List[T.Union[str, build.IncludeDirs]]
+ dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
method: str
class HasToolKwArgs(kwargs.ExtractRequired):
@@ -104,10 +103,10 @@ class QtBaseModule(ExtensionModule):
def __init__(self, interpreter: 'Interpreter', qt_version: int = 5):
ExtensionModule.__init__(self, interpreter)
self.qt_version = qt_version
- self.moc: 'ExternalProgram' = NonExistingExternalProgram('moc')
- self.uic: 'ExternalProgram' = NonExistingExternalProgram('uic')
- self.rcc: 'ExternalProgram' = NonExistingExternalProgram('rcc')
- self.lrelease: 'ExternalProgram' = NonExistingExternalProgram('lrelease')
+ self.moc: ExternalProgram = NonExistingExternalProgram('moc')
+ self.uic: ExternalProgram = NonExistingExternalProgram('uic')
+ self.rcc: ExternalProgram = NonExistingExternalProgram('rcc')
+ self.lrelease: ExternalProgram = NonExistingExternalProgram('lrelease')
self.methods.update({
'has_tools': self.has_tools,
'preprocess': self.preprocess,
@@ -141,14 +140,14 @@ class QtBaseModule(ExtensionModule):
if name == 'lrelease':
arg = ['-version']
- elif mesonlib.version_compare(qt_dep.version, '>= 5'):
+ elif version_compare(qt_dep.version, '>= 5'):
arg = ['--version']
else:
arg = ['-v']
# Ensure that the version of qt and each tool are the same
- def get_version(p: 'ExternalProgram') -> str:
- _, out, err = mesonlib.Popen_safe(p.get_command() + arg)
+ def get_version(p: ExternalProgram) -> str:
+ _, out, err = Popen_safe(p.get_command() + arg)
if b.startswith('lrelease') or not qt_dep.version.startswith('4'):
care = out
else:
@@ -157,7 +156,7 @@ class QtBaseModule(ExtensionModule):
p = state.find_program(b, required=False,
version_func=get_version,
- wanted=wanted).held_object
+ wanted=wanted)
if p.found():
setattr(self, name, p)
@@ -172,7 +171,7 @@ class QtBaseModule(ExtensionModule):
if qt.found():
# Get all tools and then make sure that they are the right version
self.compilers_detect(state, qt)
- if mesonlib.version_compare(qt.version, '>=5.14.0'):
+ if version_compare(qt.version, '>=5.14.0'):
self._rcc_supports_depfiles = True
else:
mlog.warning('rcc dependencies will not work properly until you move to Qt >= 5.14:',
@@ -185,7 +184,7 @@ class QtBaseModule(ExtensionModule):
self.lrelease = NonExistingExternalProgram(name='lrelease' + suffix)
@staticmethod
- def _qrc_nodes(state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.Tuple[str, T.List[str]]:
+ def _qrc_nodes(state: 'ModuleState', rcc_file: 'FileOrString') -> T.Tuple[str, T.List[str]]:
abspath: str
if isinstance(rcc_file, str):
abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file)
@@ -210,7 +209,7 @@ class QtBaseModule(ExtensionModule):
except Exception:
raise MesonException(f'Unable to parse resource file {abspath}')
- def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'mesonlib.FileOrString') -> T.List[File]:
+ def _parse_qrc_deps(self, state: 'ModuleState', rcc_file: 'FileOrString') -> T.List[File]:
rcc_dirname, nodes = self._qrc_nodes(state, rcc_file)
result: T.List[File] = []
for resource_path in nodes:
@@ -243,7 +242,7 @@ class QtBaseModule(ExtensionModule):
@noPosargs
@typed_kwargs(
'qt.has_tools',
- KwargInfo('required', (bool, FeatureOptionHolder), default=False),
+ KwargInfo('required', (bool, coredata.UserFeatureOption), default=False),
KwargInfo('method', str, default='auto'),
)
def has_tools(self, state: 'ModuleState', args: T.Tuple, kwargs: 'HasToolKwArgs') -> bool:
@@ -351,7 +350,7 @@ class QtBaseModule(ExtensionModule):
kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'],
['ui_@BASENAME@.h'],
name=f'Qt{self.qt_version} ui')
- out = GeneratedListHolder(gen.process_files(kwargs['sources'], state))
+ out = gen.process_files(kwargs['sources'], state)
return ModuleReturnValue(out, [out])
@FeatureNew('qt.compile_moc', '0.59.0')
@@ -362,8 +361,8 @@ class QtBaseModule(ExtensionModule):
KwargInfo('headers', ContainerTypeInfo(list, (File, str)), listify=True, default=[]),
KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True, default=[]),
KwargInfo('method', str, default='auto'),
- KwargInfo('include_directories', ContainerTypeInfo(list, IncludeDirsHolder), listify=True, default=[]),
- KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]),
+ KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
+ KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
)
def compile_moc(self, state: 'ModuleState', args: T.Tuple, kwargs: 'MocCompilerKwArgs') -> ModuleReturnValue:
self._detect_tools(state, kwargs['method'])
@@ -378,7 +377,7 @@ class QtBaseModule(ExtensionModule):
inc = state.get_include_args(include_dirs=kwargs['include_directories'])
compile_args: T.List[str] = []
for dep in kwargs['dependencies']:
- compile_args.extend([a for a in dep.held_object.get_all_compile_args() if a.startswith(('-I', '-D'))])
+ compile_args.extend([a for a in dep.get_all_compile_args() if a.startswith(('-I', '-D'))])
output: T.List[build.GeneratedList] = []
@@ -408,8 +407,8 @@ class QtBaseModule(ExtensionModule):
KwargInfo('rcc_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'),
KwargInfo('uic_extra_arguments', ContainerTypeInfo(list, str), listify=True, default=[], since='0.49.0'),
KwargInfo('method', str, default='auto'),
- KwargInfo('include_directories', ContainerTypeInfo(list, IncludeDirsHolder), listify=True, default=[]),
- KwargInfo('dependencies', ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)), listify=True, default=[]),
+ KwargInfo('include_directories', ContainerTypeInfo(list, (build.IncludeDirs, str)), listify=True, default=[]),
+ KwargInfo('dependencies', ContainerTypeInfo(list, (Dependency, ExternalLibrary)), listify=True, default=[]),
)
def preprocess(self, state: 'ModuleState', args: T.List[T.Union[str, File]], kwargs: 'PreprocessKwArgs') -> ModuleReturnValue:
_sources = args[1:]
@@ -428,11 +427,11 @@ class QtBaseModule(ExtensionModule):
if not isinstance(args[0], str):
raise build.InvalidArguments('First argument to qt.preprocess must be a string')
rcc_kwargs['name'] = args[0]
- sources.extend(self.compile_resources(state, tuple(), rcc_kwargs).return_value)
+ sources.append(self.compile_resources(state, tuple(), rcc_kwargs).return_value)
if kwargs['ui_files']:
ui_kwargs: 'UICompilerKwArgs' = {'sources': kwargs['ui_files'], 'extra_args': kwargs['uic_extra_arguments'], 'method': method}
- sources.extend(self.compile_ui(state, tuple(), ui_kwargs).return_value)
+ sources.append(self.compile_ui(state, tuple(), ui_kwargs).return_value)
if kwargs['moc_headers'] or kwargs['moc_sources']:
moc_kwargs: 'MocCompilerKwArgs' = {
@@ -443,7 +442,7 @@ class QtBaseModule(ExtensionModule):
'dependencies': kwargs['dependencies'],
'method': method,
}
- sources.extend(self.compile_moc(state, tuple(), moc_kwargs).return_value)
+ sources.append(self.compile_moc(state, tuple(), moc_kwargs).return_value)
return ModuleReturnValue(sources, [sources])
diff --git a/mesonbuild/modules/sourceset.py b/mesonbuild/modules/sourceset.py
index e413e32..ba8b300 100644
--- a/mesonbuild/modules/sourceset.py
+++ b/mesonbuild/modules/sourceset.py
@@ -14,16 +14,13 @@
from collections import namedtuple
from .. import mesonlib
+from .. import build
from ..mesonlib import listify, OrderedSet
from . import ExtensionModule, ModuleObject, MutableModuleObject
from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs,
InterpreterException, InvalidArguments, InvalidCode, FeatureNew,
)
-from ..interpreter import (
- GeneratedListHolder, CustomTargetHolder,
- CustomTargetIndexHolder
-)
SourceSetRule = namedtuple('SourceSetRule', 'keys sources if_false sourcesets dependencies extra_deps')
SourceFiles = namedtuple('SourceFiles', 'sources dependencies')
@@ -49,8 +46,8 @@ class SourceSet(MutableModuleObject):
deps = []
for x in arg:
if isinstance(x, (str, mesonlib.File,
- GeneratedListHolder, CustomTargetHolder,
- CustomTargetIndexHolder)):
+ build.GeneratedList, build.CustomTarget,
+ build.CustomTargetIndex)):
sources.append(x)
elif hasattr(x, 'found'):
if not allow_deps:
@@ -101,7 +98,6 @@ class SourceSet(MutableModuleObject):
if_true = args
elif args:
raise InterpreterException('add_all called with both positional and keyword arguments')
- if_true = mesonlib.unholder(if_true)
keys, dependencies = self.check_conditions(when)
for s in if_true:
if not isinstance(s, SourceSet):
diff --git a/mesonbuild/modules/unstable_cuda.py b/mesonbuild/modules/unstable_cuda.py
index af65af3..a80f9ca 100644
--- a/mesonbuild/modules/unstable_cuda.py
+++ b/mesonbuild/modules/unstable_cuda.py
@@ -16,8 +16,7 @@ import typing as T
import re
from ..mesonlib import version_compare
-from ..interpreter import CompilerHolder
-from ..compilers import CudaCompiler
+from ..compilers import CudaCompiler, Compiler
from . import ModuleObject
@@ -83,7 +82,7 @@ class CudaModule(ModuleObject):
@permittedKwargs(['detected'])
def nvcc_arch_flags(self, state: 'ModuleState',
- args: T.Tuple[T.Union[CompilerHolder, CudaCompiler, str]],
+ args: T.Tuple[T.Union[Compiler, CudaCompiler, str]],
kwargs: T.Dict[str, T.Any]) -> T.List[str]:
nvcc_arch_args = self._validate_nvcc_arch_args(args, kwargs)
ret = self._nvcc_arch_flags(*nvcc_arch_args)[0]
@@ -91,7 +90,7 @@ class CudaModule(ModuleObject):
@permittedKwargs(['detected'])
def nvcc_arch_readable(self, state: 'ModuleState',
- args: T.Tuple[T.Union[CompilerHolder, CudaCompiler, str]],
+ args: T.Tuple[T.Union[Compiler, CudaCompiler, str]],
kwargs: T.Dict[str, T.Any]) -> T.List[str]:
nvcc_arch_args = self._validate_nvcc_arch_args(args, kwargs)
ret = self._nvcc_arch_flags(*nvcc_arch_args)[1]
@@ -105,16 +104,12 @@ class CudaModule(ModuleObject):
@staticmethod
def _detected_cc_from_compiler(c):
- if isinstance(c, CompilerHolder):
- c = c.compiler
if isinstance(c, CudaCompiler):
return c.detected_cc
return ''
@staticmethod
def _version_from_compiler(c):
- if isinstance(c, CompilerHolder):
- c = c.compiler
if isinstance(c, CudaCompiler):
return c.version
if isinstance(c, str):
diff --git a/mesonbuild/modules/unstable_external_project.py b/mesonbuild/modules/unstable_external_project.py
index f10c7aa..e997f6a 100644
--- a/mesonbuild/modules/unstable_external_project.py
+++ b/mesonbuild/modules/unstable_external_project.py
@@ -22,7 +22,6 @@ from ..mesonlib import (MesonException, Popen_safe, MachineChoice,
get_variable_regex, do_replacement, extract_as_list)
from ..interpreterbase import InterpreterException, FeatureNew
from ..interpreterbase import permittedKwargs, typed_pos_args
-from ..interpreter import DependencyHolder
from ..compilers.compilers import CFLAGS_MAPPING, CEXE_MAPPING
from ..dependencies import InternalDependency, PkgConfigDependency
from ..mesonlib import OptionKey
@@ -237,7 +236,7 @@ class ExternalProject(ModuleObject):
variables = []
dep = InternalDependency(version, incdir, compile_args, link_args, libs,
libs_whole, sources, final_deps, variables)
- return DependencyHolder(dep, self.subproject)
+ return dep
class ExternalProjectModule(ExtensionModule):
diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py
index f602e09..995370a 100644
--- a/mesonbuild/modules/unstable_rust.py
+++ b/mesonbuild/modules/unstable_rust.py
@@ -19,15 +19,11 @@ from . import ExtensionModule, ModuleReturnValue
from .. import mlog
from ..build import BuildTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, IncludeDirs, CustomTarget
from ..interpreter.interpreter import TEST_KWARGS
-from ..interpreter.interpreterobjects import (
- BuildTargetHolder,
- CustomTargetHolder,
- DependencyHolder,
- ExecutableHolder,
- ExternalLibraryHolder,
-)
from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, permittedKwargs, FeatureNew, typed_kwargs, typed_pos_args, noPosargs
-from ..mesonlib import stringlistify, unholder, listify, typeslistify, File
+from ..mesonlib import stringlistify, listify, typeslistify, File
+from ..dependencies import Dependency, ExternalLibrary
+from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew, typed_pos_args, noPosargs
+from ..mesonlib import stringlistify, listify, typeslistify, File
if T.TYPE_CHECKING:
from . import ModuleState
@@ -38,7 +34,7 @@ if T.TYPE_CHECKING:
class FuncTest(_kwargs.BaseTest):
- dependencies: T.List[T.Union[DependencyHolder, ExternalLibraryHolder]]
+ dependencies: T.List[T.Union[Dependency, ExternalLibrary]]
is_parallel: bool
@@ -55,18 +51,18 @@ class RustModule(ExtensionModule):
'bindgen': self.bindgen,
})
- @typed_pos_args('rust.test', str, BuildTargetHolder)
+ @typed_pos_args('rust.test', str, BuildTarget)
@typed_kwargs(
'rust.test',
*TEST_KWARGS,
KwargInfo('is_parallel', bool, default=False),
KwargInfo(
'dependencies',
- ContainerTypeInfo(list, (DependencyHolder, ExternalLibraryHolder)),
+ ContainerTypeInfo(list, (Dependency, ExternalLibrary)),
listify=True,
default=[]),
)
- def test(self, state: 'ModuleState', args: T.Tuple[str, BuildTargetHolder], kwargs: 'FuncTest') -> ModuleReturnValue:
+ def test(self, state: 'ModuleState', args: T.Tuple[str, BuildTarget], kwargs: 'FuncTest') -> ModuleReturnValue:
"""Generate a rust test target from a given rust target.
Rust puts it's unitests inside it's main source files, unlike most
@@ -109,7 +105,7 @@ class RustModule(ExtensionModule):
```
"""
name = args[0]
- base_target: BuildTarget = unholder(args[1])
+ base_target: BuildTarget = args[1]
if not base_target.uses_rust():
raise InterpreterException('Second positional argument to rustmod.test() must be a rust based target')
extra_args = kwargs['args']
@@ -129,7 +125,7 @@ class RustModule(ExtensionModule):
del extra_args[i]
break
- dependencies = [d.held_object for d in kwargs['dependencies']]
+ dependencies = [d for d in kwargs['dependencies']]
# We need to cast here, as currently these don't have protocol in them, but test itself does.
tkwargs = T.cast('_kwargs.FuncTest', kwargs.copy())
@@ -151,11 +147,10 @@ class RustModule(ExtensionModule):
new_target_kwargs
)
- e = ExecutableHolder(new_target, self.interpreter)
test = self.interpreter.make_test(
- self.interpreter.current_node, (name, e), tkwargs)
+ self.interpreter.current_node, (name, new_target), tkwargs)
- return ModuleReturnValue(None, [e, test])
+ return ModuleReturnValue(None, [new_target, test])
@noPosargs
@permittedKwargs({'input', 'output', 'include_directories', 'c_args', 'args'})
@@ -168,7 +163,7 @@ class RustModule(ExtensionModule):
header: 'SourceOutputs'
_deps: T.Sequence['SourceOutputs']
try:
- header, *_deps = unholder(self.interpreter.source_strings_to_files(listify(kwargs['input'])))
+ header, *_deps = self.interpreter.source_strings_to_files(listify(kwargs['input']))
except KeyError:
raise InvalidArguments('rustmod.bindgen() `input` argument must have at least one element.')
@@ -179,12 +174,12 @@ class RustModule(ExtensionModule):
if not isinstance(output, str):
raise InvalidArguments('rustmod.bindgen() `output` argument must be a string.')
- include_dirs: T.List[IncludeDirs] = typeslistify(unholder(listify(kwargs.get('include_directories', []))), IncludeDirs)
+ include_dirs: T.List[IncludeDirs] = typeslistify(listify(kwargs.get('include_directories', [])), IncludeDirs)
c_args: T.List[str] = stringlistify(listify(kwargs.get('c_args', [])))
bind_args: T.List[str] = stringlistify(listify(kwargs.get('args', [])))
# Split File and Target dependencies to add pass to CustomTarget
- depends: T.List[T.Union[GeneratedList, BuildTarget, CustomTargetIndex]] = []
+ depends: T.List[T.Union[GeneratedList, BuildTarget, CustomTargetIndex, CustomTarget]] = []
depend_files: T.List[File] = []
for d in _deps:
if isinstance(d, File):
@@ -198,8 +193,7 @@ class RustModule(ExtensionModule):
inc_strs.extend([f'-I{x}' for x in i.to_string_list(state.environment.get_source_dir())])
if self._bindgen_bin is None:
- # there's some bugs in the interpreter typeing.
- self._bindgen_bin = state.find_program('bindgen').held_object
+ self._bindgen_bin = state.find_program('bindgen')
name: str
if isinstance(header, File):
@@ -226,7 +220,7 @@ class RustModule(ExtensionModule):
backend=state.backend,
)
- return ModuleReturnValue([target], [CustomTargetHolder(target, self.interpreter)])
+ return ModuleReturnValue([target], [target])
def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule:
diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py
index df5faa1..3339cea 100644
--- a/mesonbuild/modules/unstable_simd.py
+++ b/mesonbuild/modules/unstable_simd.py
@@ -54,11 +54,11 @@ class SimdModule(ExtensionModule):
for key, value in kwargs.items():
if key not in self.isets and key != 'compiler':
basic_kwargs[key] = value
- compiler = kwargs['compiler'].compiler
+ compiler = kwargs['compiler']
if not isinstance(compiler, compilers.compilers.Compiler):
raise mesonlib.MesonException('Compiler argument must be a compiler object.')
cdata = self.interpreter.func_configuration_data(None, [], {})
- conf = cdata.held_object
+ conf = cdata.conf_data
for iset in self.isets:
if iset not in kwargs:
continue
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 441fb9f..d322833 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -18,10 +18,9 @@ import re
from .. import mlog
from .. import mesonlib, build
-from ..mesonlib import MachineChoice, MesonException, extract_as_list, unholder
+from ..mesonlib import MachineChoice, MesonException, extract_as_list
from . import ModuleReturnValue
from . import ExtensionModule
-from ..interpreter import CustomTargetHolder
from ..interpreterbase import permittedKwargs, FeatureNewKwargs, flatten
from ..programs import ExternalProgram
@@ -86,11 +85,13 @@ class WindowsModule(ExtensionModule):
wrc_depend_files = extract_as_list(kwargs, 'depend_files', pop = True)
wrc_depends = extract_as_list(kwargs, 'depends', pop = True)
for d in wrc_depends:
- if isinstance(d, CustomTargetHolder):
- extra_args += state.get_include_args([d.outdir_include()])
+ if isinstance(d, build.CustomTarget):
+ extra_args += state.get_include_args([
+ build.IncludeDirs('', [], False, [os.path.join('@BUILD_ROOT@', self.interpreter.backend.get_target_dir(d))])
+ ])
inc_dirs = extract_as_list(kwargs, 'include_directories', pop = True)
for incd in inc_dirs:
- if not isinstance(incd.held_object, (str, build.IncludeDirs)):
+ if not isinstance(incd, (str, build.IncludeDirs)):
raise MesonException('Resource include dirs should be include_directories().')
extra_args += state.get_include_args(inc_dirs)
@@ -120,7 +121,6 @@ class WindowsModule(ExtensionModule):
for subsrc in src:
add_target(subsrc)
return
- src = unholder(src)
if isinstance(src, str):
name_formatted = src
diff --git a/mesonbuild/programs.py b/mesonbuild/programs.py
index 1d93b8a..bb14f96 100644
--- a/mesonbuild/programs.py
+++ b/mesonbuild/programs.py
@@ -19,6 +19,7 @@ import os
import shutil
import stat
import sys
+import re
import typing as T
from pathlib import Path
@@ -28,9 +29,10 @@ from .mesonlib import MachineChoice
if T.TYPE_CHECKING:
from .environment import Environment
+ from .interpreter import Interpreter
-class ExternalProgram:
+class ExternalProgram(mesonlib.HoldableObject):
"""A program that is found on the system."""
@@ -41,7 +43,8 @@ class ExternalProgram:
silent: bool = False, search_dir: T.Optional[str] = None,
extra_search_dirs: T.Optional[T.List[str]] = None):
self.name = name
- self.path = None # type: T.Optional[str]
+ self.path: T.Optional[str] = None
+ self.cached_version: T.Optional[str] = None
if command is not None:
self.command = mesonlib.listify(command)
if mesonlib.is_windows():
@@ -97,6 +100,24 @@ class ExternalProgram:
'''Human friendly description of the command'''
return ' '.join(self.command)
+ def get_version(self, interpreter: 'Interpreter') -> str:
+ if not self.cached_version:
+ raw_cmd = self.get_command() + ['--version']
+ cmd: T.List[T.Union[str, ExternalProgram]] = [self, '--version']
+ res = interpreter.run_command_impl(interpreter.current_node, cmd, {}, True)
+ if res.returncode != 0:
+ m = 'Running {!r} failed'
+ raise mesonlib.MesonException(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 mesonlib.MesonException(m.format(raw_cmd))
+ self.cached_version = match.group(1)
+ return self.cached_version
+
@classmethod
def from_bin_list(cls, env: 'Environment', for_machine: MachineChoice, name: str) -> 'ExternalProgram':
# There is a static `for_machine` for this class because the binary
diff --git a/run_mypy.py b/run_mypy.py
index 982a3ae..e780adf 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -11,7 +11,7 @@ from mesonbuild.mesonlib import version_compare
modules = [
# fully typed submodules
- 'mesonbuild/ast',
+ # 'mesonbuild/ast',
'mesonbuild/cmake',
'mesonbuild/compilers',
'mesonbuild/dependencies',
@@ -23,6 +23,7 @@ modules = [
'mesonbuild/arglist.py',
# 'mesonbuild/coredata.py',
'mesonbuild/envconfig.py',
+ 'mesonbuild/interpreter/interpreterobjects.py',
'mesonbuild/linkers.py',
'mesonbuild/mcompile.py',
'mesonbuild/mdevenv.py',
diff --git a/run_project_tests.py b/run_project_tests.py
index d6dc5e1..3522009 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -224,6 +224,7 @@ class InstalledFile:
@functools.total_ordering
class TestDef:
def __init__(self, path: Path, name: T.Optional[str], args: T.List[str], skip: bool = False):
+ self.category = path.parts[1]
self.path = path
self.name = name
self.args = args
@@ -233,6 +234,9 @@ class TestDef:
self.do_not_set_opts = [] # type: T.List[str]
self.stdout = [] # type: T.List[T.Dict[str, str]]
+ # Always print a stack trace for Meson exceptions
+ self.env['MESON_FORCE_BACKTRACE'] = '1'
+
def __repr__(self) -> str:
return '<{}: {:<48} [{}: {}] -- {}>'.format(type(self).__name__, str(self.path), self.name, self.args, self.skip)
@@ -619,7 +623,7 @@ def _run_test(test: TestDef,
gen_args.extend(['--native-file', nativefile.as_posix()])
if crossfile.exists():
gen_args.extend(['--cross-file', crossfile.as_posix()])
- (returncode, stdo, stde) = run_configure(gen_args, env=test.env)
+ (returncode, stdo, stde) = run_configure(gen_args, env=test.env, catch_exception=True)
try:
logfile = Path(test_build_dir, 'meson-logs', 'meson-log.txt')
mesonlog = logfile.open(errors='ignore', encoding='utf-8').read()
@@ -1267,9 +1271,9 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]],
continue
# Handle skipped tests
- if (result is None) or (('MESON_SKIP_TEST' in result.stdo) and (skippable(name, t.path.as_posix()))):
+ if (result is None) or (('MESON_SKIP_TEST' in result.stdo) and (skippable(t.category, t.path.as_posix()))):
f.update_log(TestStatus.SKIP)
- current_test = ET.SubElement(current_suite, 'testcase', {'name': testname, 'classname': name})
+ current_test = ET.SubElement(current_suite, 'testcase', {'name': testname, 'classname': t.category})
ET.SubElement(current_test, 'skipped', {})
skipped_tests += 1
continue
@@ -1280,6 +1284,15 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]],
safe_print(bold('During:'), result.step.name)
safe_print(bold('Reason:'), result.msg)
failing_tests += 1
+ # Append a visual seperator for the different test cases
+ cols = shutil.get_terminal_size((100, 20)).columns
+ name_str = ' '.join([str(x) for x in f.testdef.display_name()])
+ name_len = len(re.sub(r'\x1B[^m]+m', '', name_str)) # Do not count escape sequences
+ left_w = (cols // 2) - (name_len // 2) - 1
+ left_w = max(3, left_w)
+ right_w = cols - left_w - name_len - 2
+ right_w = max(3, right_w)
+ failing_logs.append(f'\n\x1b[31m{"="*left_w}\x1b[0m {name_str} \x1b[31m{"="*right_w}\x1b[0m\n')
if result.step == BuildStep.configure and result.mlog != no_meson_log_msg:
# For configure failures, instead of printing stdout,
# print the meson log if available since it's a superset
@@ -1311,7 +1324,7 @@ def _run_tests(all_tests: T.List[T.Tuple[str, T.List[TestDef], bool]],
current_test = ET.SubElement(
current_suite,
'testcase',
- {'name': testname, 'classname': name, 'time': '%.3f' % total_time}
+ {'name': testname, 'classname': t.category, 'time': '%.3f' % total_time}
)
if result.msg != '':
ET.SubElement(current_test, 'failure', {'message': result.msg})
diff --git a/run_single_test.py b/run_single_test.py
index 703f0a6..4d70507 100755
--- a/run_single_test.py
+++ b/run_single_test.py
@@ -57,7 +57,7 @@ def main() -> None:
failed = True
else:
msg = mlog.green('PASS:')
- mlog.log(msg, test.display_name())
+ mlog.log(msg, *test.display_name())
if result is not None and result.msg and 'MESON_SKIP_TEST' not in result.stdo:
mlog.log('reason:', result.msg)
if result.step is BuildStep.configure:
diff --git a/run_tests.py b/run_tests.py
index 80ea38f..10d63df 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import collections
import os
import sys
import time
@@ -22,6 +23,7 @@ import subprocess
import tempfile
import platform
import argparse
+import traceback
from io import StringIO
from enum import Enum
from glob import glob
@@ -267,13 +269,21 @@ def clear_meson_configure_class_caches() -> None:
compilers.CCompiler.find_framework_cache = {}
dependencies.PkgConfigDependency.pkgbin_cache = {}
dependencies.PkgConfigDependency.class_pkgbin = mesonlib.PerMachine(None, None)
+ mesonlib.project_meson_versions = collections.defaultdict(str)
-def run_configure_inprocess(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str, str]:
+def run_configure_inprocess(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None, catch_exception: bool = False) -> T.Tuple[int, str, str]:
stderr = StringIO()
stdout = StringIO()
+ returncode = 0
with mock.patch.dict(os.environ, env or {}), mock.patch.object(sys, 'stdout', stdout), mock.patch.object(sys, 'stderr', stderr):
try:
returncode = mesonmain.run(commandlist, get_meson_script())
+ except Exception:
+ if catch_exception:
+ returncode = 1
+ traceback.print_exc()
+ else:
+ raise
finally:
clear_meson_configure_class_caches()
return returncode, stdout.getvalue(), stderr.getvalue()
@@ -282,11 +292,11 @@ def run_configure_external(full_command: T.List[str], env: T.Optional[T.Dict[str
pc, o, e = mesonlib.Popen_safe(full_command, env=env)
return pc.returncode, o, e
-def run_configure(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str, str]:
+def run_configure(commandlist: T.List[str], env: T.Optional[T.Dict[str, str]] = None, catch_exception: bool = False) -> T.Tuple[int, str, str]:
global meson_exe
if meson_exe:
return run_configure_external(meson_exe + commandlist, env=env)
- return run_configure_inprocess(commandlist, env=env)
+ return run_configure_inprocess(commandlist, env=env, catch_exception=catch_exception)
def print_system_info():
print(mlog.bold('System information.'))
diff --git a/run_unittests.py b/run_unittests.py
index 8c20e72..b55ba96 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -727,23 +727,20 @@ class InternalTests(unittest.TestCase):
self.assertEqual([1, 2, 3], listify([1, [2, [3]]]))
self.assertEqual([1, [2, [3]]], listify([1, [2, [3]]], flatten=False))
# Test flattening and unholdering
- holder1 = ObjectHolder(1)
+ class TestHeldObj(mesonbuild.mesonlib.HoldableObject):
+ def __init__(self, val: int) -> None:
+ self._val = val
+ class MockInterpreter:
+ def __init__(self) -> None:
+ self.subproject = ''
+ self.environment = None
+ heldObj1 = TestHeldObj(1)
+ holder1 = ObjectHolder(heldObj1, MockInterpreter())
self.assertEqual([holder1], listify(holder1))
self.assertEqual([holder1], listify([holder1]))
self.assertEqual([holder1, 2], listify([holder1, 2]))
self.assertEqual([holder1, 2, 3], listify([holder1, 2, [3]]))
- def test_unholder(self):
- unholder = mesonbuild.mesonlib.unholder
-
- holder1 = ObjectHolder(1)
- holder3 = ObjectHolder(3)
- holders = [holder1, holder3]
-
- self.assertEqual(1, unholder(holder1))
- self.assertEqual([1], unholder([holder1]))
- self.assertEqual([1, 3], unholder(holders))
-
def test_extract_as_list(self):
extract = mesonbuild.mesonlib.extract_as_list
# Test sanity
@@ -753,8 +750,16 @@ class InternalTests(unittest.TestCase):
self.assertEqual([1, 2, 3], extract(kwargs, 'sources', pop=True))
self.assertEqual(kwargs, {})
+ class TestHeldObj(mesonbuild.mesonlib.HoldableObject):
+ pass
+ class MockInterpreter:
+ def __init__(self) -> None:
+ self.subproject = ''
+ self.environment = None
+ heldObj = TestHeldObj()
+
# Test unholding
- holder3 = ObjectHolder(3)
+ holder3 = ObjectHolder(heldObj, MockInterpreter())
kwargs = {'sources': [1, 2, holder3]}
self.assertEqual(kwargs, {'sources': [1, 2, holder3]})
@@ -769,7 +774,6 @@ class InternalTests(unittest.TestCase):
_mock.pcdep = mock.Mock()
_mock.pcdep.name = "some_name"
_mock.version_reqs = []
- _mock = mock.Mock(held_object=_mock)
# pkgconfig dependency as lib
deps = mesonbuild.modules.pkgconfig.DependenciesHelper(dummystate, "thislib")
@@ -6165,7 +6169,7 @@ class FailureTests(BasePlatformTests):
'''
tdir = os.path.join(self.unit_test_dir, '21 exit status')
with self.assertRaises(subprocess.CalledProcessError) as cm:
- self.init(tdir, inprocess=False, override_envvars = {'MESON_UNIT_TEST': '1'})
+ self.init(tdir, inprocess=False, override_envvars = {'MESON_UNIT_TEST': '1', 'MESON_FORCE_BACKTRACE': ''})
self.assertEqual(cm.exception.returncode, 2)
self.wipe()
diff --git a/test cases/cmake/25 assembler/main.c b/test cases/cmake/25 assembler/main.c
new file mode 100644
index 0000000..5aef967
--- /dev/null
+++ b/test cases/cmake/25 assembler/main.c
@@ -0,0 +1,18 @@
+#include <stdint.h>
+#include <stdio.h>
+
+int32_t cmTestFunc(void);
+
+int main(void)
+{
+ if (cmTestFunc() > 4200)
+ {
+ printf("Test success.\n");
+ return 0;
+ }
+ else
+ {
+ printf("Test failure.\n");
+ return 1;
+ }
+}
diff --git a/test cases/cmake/25 assembler/meson.build b/test cases/cmake/25 assembler/meson.build
new file mode 100644
index 0000000..7180356
--- /dev/null
+++ b/test cases/cmake/25 assembler/meson.build
@@ -0,0 +1,9 @@
+project('assembler test', ['c', 'cpp'])
+
+cm = import('cmake')
+
+sub_pro = cm.subproject('cmTest')
+sub_dep = sub_pro.dependency('cmTest')
+
+exe1 = executable('exe1', ['main.c'], dependencies: [sub_dep])
+test('test1', exe1)
diff --git a/test cases/cmake/25 assembler/subprojects/cmTest/CMakeLists.txt b/test cases/cmake/25 assembler/subprojects/cmTest/CMakeLists.txt
new file mode 100644
index 0000000..5fb7cd6
--- /dev/null
+++ b/test cases/cmake/25 assembler/subprojects/cmTest/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(cmTest)
+
+#Detect processor
+if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "amd64")
+ SET(TEST_PROCESSOR "x86_64")
+elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64")
+ SET(TEST_PROCESSOR "x86_64")
+elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "i386")
+ SET(TEST_PROCESSOR "x86")
+elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "i686")
+ SET(TEST_PROCESSOR "x86")
+elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm")
+ SET(TEST_PROCESSOR "arm")
+elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "aarch64")
+ SET(TEST_PROCESSOR "arm")
+else ()
+ message(FATAL_ERROR, 'MESON_SKIP_TEST: Unsupported Assembler Platform')
+endif ()
+
+#Detect ABI
+if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
+ SET(TEST_ABI "sysv")
+elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD")
+ SET(TEST_ABI "sysv")
+elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD")
+ SET(TEST_ABI "sysv")
+elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
+ SET(TEST_ABI "sysv")
+else ()
+ message(FATAL_ERROR, 'MESON_SKIP_TEST: Unsupported Assembler Platform')
+endif ()
+
+SET(TEST_PLATFORM "${TEST_PROCESSOR}-${TEST_ABI}")
+
+if ( ("${TEST_PLATFORM}" MATCHES "x86_64-sysv")
+ OR ("${TEST_PLATFORM}" MATCHES "x86-sysv")
+ OR ("${TEST_PLATFORM}" MATCHES "arm-sysv"))
+ SET(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+ enable_language(ASM)
+ SET(TEST_SOURCE "cmTestAsm.s")
+endif ()
+
+add_library(cmTest STATIC cmTest.c ${TEST_SOURCE})
diff --git a/test cases/cmake/25 assembler/subprojects/cmTest/cmTest.c b/test cases/cmake/25 assembler/subprojects/cmTest/cmTest.c
new file mode 100644
index 0000000..e32415c
--- /dev/null
+++ b/test cases/cmake/25 assembler/subprojects/cmTest/cmTest.c
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+extern const int32_t cmTestArea;
+
+int32_t cmTestFunc(void)
+{
+ return cmTestArea;
+}
diff --git a/test cases/cmake/25 assembler/subprojects/cmTest/cmTestAsm.s b/test cases/cmake/25 assembler/subprojects/cmTest/cmTestAsm.s
new file mode 100644
index 0000000..8aa83a6
--- /dev/null
+++ b/test cases/cmake/25 assembler/subprojects/cmTest/cmTestAsm.s
@@ -0,0 +1,4 @@
+.text
+.globl cmTestArea
+cmTestArea:
+ .long 4242
diff --git a/test cases/common/178 bothlibraries/meson.build b/test cases/common/178 bothlibraries/meson.build
index 0bfba76..d52158d 100644
--- a/test cases/common/178 bothlibraries/meson.build
+++ b/test cases/common/178 bothlibraries/meson.build
@@ -20,6 +20,9 @@ exe_static2 = executable('prog-static2', 'main.c',
link_with : both_libs2.get_static_lib())
exe_both2 = executable('prog-both2', 'main.c', link_with : both_libs2)
+# Ensure that calling the build target methods also works
+assert(both_libs.name() == 'mylib')
+
test('runtest-shared-2', exe_shared2)
test('runtest-static-2', exe_static2)
test('runtest-both-2', exe_both2)
diff --git a/test cases/common/182 find override/meson.build b/test cases/common/182 find override/meson.build
index b277459..8dcbac7 100644
--- a/test cases/common/182 find override/meson.build
+++ b/test cases/common/182 find override/meson.build
@@ -1,8 +1,10 @@
project('find program override', 'c')
gencodegen = find_program('gencodegen', required : false)
+six_prog = find_program('six_meson_exe', required : false)
assert(not gencodegen.found(), 'gencodegen is an internal program, should not be found')
+assert(not six_prog.found(), 'six_meson_exe is an internal program, should not be found')
# Test the check-if-found-else-override workflow
if not gencodegen.found()
@@ -13,3 +15,11 @@ subdir('otherdir')
tool = find_program('sometool')
assert(tool.found())
+assert(tool.full_path() != '')
+assert(tool.full_path() == tool.path())
+
+# six_meson_exe is an overritten project executable
+six_prog = find_program('six_meson_exe')
+assert(six_prog.found())
+assert(six_prog.full_path() != '')
+assert(six_prog.full_path() == six_prog.path())
diff --git a/test cases/common/182 find override/otherdir/meson.build b/test cases/common/182 find override/otherdir/meson.build
index 5cefc88..7deff40 100644
--- a/test cases/common/182 find override/otherdir/meson.build
+++ b/test cases/common/182 find override/otherdir/meson.build
@@ -10,6 +10,10 @@ e = executable('six', 'main.c', src)
test('six', e)
+# Override stuff with an executables
+meson.override_find_program('six_meson_exe', e)
+
+
# The same again, but this time with a program that was generated
# with configure_file.
diff --git a/test cases/failing/115 compiler argument checking/meson.build b/test cases/failing/115 compiler argument checking/meson.build
new file mode 100644
index 0000000..bb1f447
--- /dev/null
+++ b/test cases/failing/115 compiler argument checking/meson.build
@@ -0,0 +1,4 @@
+project('compiler argument checking test', 'c')
+
+cc = meson.get_compiler('c')
+add_project_arguments(cc.get_supported_arguments('-meson-goober-arg-for-testing', checked : 'require'), language : 'c')
diff --git a/test cases/failing/115 compiler argument checking/test.json b/test cases/failing/115 compiler argument checking/test.json
new file mode 100644
index 0000000..93f9476
--- /dev/null
+++ b/test cases/failing/115 compiler argument checking/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/115 compiler argument checking/meson.build:4:0: ERROR: Compiler for C does not support \"-meson-goober-arg-for-testing\""
+ }
+ ]
+}
diff --git a/test cases/failing/115 empty fallback/meson.build b/test cases/failing/115 empty fallback/meson.build
new file mode 100644
index 0000000..f4eb5fe
--- /dev/null
+++ b/test cases/failing/115 empty fallback/meson.build
@@ -0,0 +1,6 @@
+project('empty fallback')
+
+# There is a subproject named 'foo' that overrides that dependency,
+# but `fallback: []` should not allow to use it. Same behaviour than with
+# `allow_fallback: false`
+dependency('foo', fallback: [])
diff --git a/test cases/failing/115 empty fallback/subprojects/foo/meson.build b/test cases/failing/115 empty fallback/subprojects/foo/meson.build
new file mode 100644
index 0000000..c9e134b
--- /dev/null
+++ b/test cases/failing/115 empty fallback/subprojects/foo/meson.build
@@ -0,0 +1,3 @@
+project('foo')
+
+meson.override_dependency('foo', declare_dependency())
diff --git a/test cases/failing/115 empty fallback/test.json b/test cases/failing/115 empty fallback/test.json
new file mode 100644
index 0000000..dcfde45
--- /dev/null
+++ b/test cases/failing/115 empty fallback/test.json
@@ -0,0 +1,7 @@
+{
+ "stdout": [
+ {
+ "line": "test cases/failing/115 empty fallback/meson.build:6:0: ERROR: Dependency \"foo\" not found, tried pkgconfig and cmake"
+ }
+ ]
+}
diff --git a/test cases/failing/45 abspath to srcdir/test.json b/test cases/failing/45 abspath to srcdir/test.json
index b6a87fe..177bac1 100644
--- a/test cases/failing/45 abspath to srcdir/test.json
+++ b/test cases/failing/45 abspath to srcdir/test.json
@@ -1,7 +1,7 @@
{
"stdout": [
{
- "line": "test cases/failing/45 abspath to srcdir/meson.build:3:0: ERROR: Tried to form an absolute path to a source dir. You should not do that but use relative paths instead."
+ "line": "test cases/failing/45 abspath to srcdir/meson.build:3:0: ERROR: Tried to form an absolute path to a source dir."
}
]
}
diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build
index 2e85ddb..f9fb21c 100644
--- a/test cases/frameworks/4 qt/meson.build
+++ b/test cases/frameworks/4 qt/meson.build
@@ -57,6 +57,10 @@ foreach qt : ['qt4', 'qt5', 'qt6']
# XML files that need to be compiled with the uic tol.
prep += qtmodule.compile_ui(sources : 'mainWindow.ui', method: get_option('method'))
+ qtmodule.preprocess(
+ ui_files : 'mainWindow.ui',
+ method: get_option('method'))
+
# Resource file(s) for rcc compiler
extra_cpp_args = []
if meson.is_unity()
@@ -100,6 +104,12 @@ foreach qt : ['qt4', 'qt5', 'qt6']
# The build system needs to include the cpp files from
# headers but the user must manually include moc
# files from sources.
+ qtmodule.preprocess(
+ moc_extra_arguments : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments`
+ moc_sources : 'manualinclude.cpp',
+ moc_headers : 'manualinclude.h',
+ method : get_option('method'))
+
manpreprocessed = qtmodule.compile_moc(
extra_args : ['-DMOC_EXTRA_FLAG'], # This is just a random macro to test `extra_arguments`
sources : 'manualinclude.cpp',
diff --git a/test cases/native/9 override with exe/meson.build b/test cases/native/9 override with exe/meson.build
index 62d2f32..5275532 100644
--- a/test cases/native/9 override with exe/meson.build
+++ b/test cases/native/9 override with exe/meson.build
@@ -1,4 +1,4 @@
-project('myexe', 'c')
+project('myexe', 'c', version: '0.1')
sub = subproject('sub')
prog = find_program('foobar', version : '>= 2.0', required : false)