diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2020-10-13 19:44:41 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2020-10-15 01:56:59 +0300 |
commit | 7c377e5a5d1e413ea3494ceed0800985fcd804ae (patch) | |
tree | beb8d9e6dfe24740de92151eaa5906b4c52b5cda | |
parent | 30d78f39812a0585a27e24ab44ef4e9da1f12574 (diff) | |
download | meson-7c377e5a5d1e413ea3494ceed0800985fcd804ae.zip meson-7c377e5a5d1e413ea3494ceed0800985fcd804ae.tar.gz meson-7c377e5a5d1e413ea3494ceed0800985fcd804ae.tar.bz2 |
intro: Add extra_files key to intro output (fixes #7310)
-rw-r--r-- | docs/markdown/IDE-integration.md | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/intro_extra_files.md | 6 | ||||
-rw-r--r-- | mesonbuild/ast/introspection.py | 68 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 2 | ||||
-rw-r--r-- | mesonbuild/build.py | 5 | ||||
-rw-r--r-- | mesonbuild/mintro.py | 24 | ||||
-rwxr-xr-x | run_unittests.py | 49 | ||||
-rw-r--r-- | test cases/unit/57 introspection/sharedlib/meson.build | 2 | ||||
-rw-r--r-- | test cases/unit/57 introspection/staticlib/meson.build | 3 | ||||
-rw-r--r-- | test cases/unit/57 introspection/staticlib/static.h | 10 |
10 files changed, 116 insertions, 57 deletions
diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index 5188359..2f91e81 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -59,6 +59,7 @@ for one target is defined as follows: "filename": ["list", "of", "generated", "files"], "build_by_default": true / false, "target_sources": [], + "extra_files": ["/path/to/file1.hpp", "/path/to/file2.hpp"], "installed": true / false, } ``` @@ -71,6 +72,9 @@ is set to `null`. The `subproject` key specifies the name of the subproject this target was defined in, or `null` if the target was defined in the top level project. +*(New in 0.56.0)* The `extra_files` key lists all files specified via the +`extra_files` kwarg of a build target. See [`executable()`](Reference-manual.md#executable). + A target usually generates only one file. However, it is possible for custom targets to have multiple outputs. diff --git a/docs/markdown/snippets/intro_extra_files.md b/docs/markdown/snippets/intro_extra_files.md new file mode 100644 index 0000000..43475cd --- /dev/null +++ b/docs/markdown/snippets/intro_extra_files.md @@ -0,0 +1,6 @@ +## New `extra_files` key in target introspection + +The target introspection (`meson introspect --targets`, `intro-targets.json`) +now has the new `extra_files` key which lists all files specified via the +`extra_files` kwarg of a build target (see `executable()`, etc.) + diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py index 9cfdded..334ff83 100644 --- a/mesonbuild/ast/introspection.py +++ b/mesonbuild/ast/introspection.py @@ -194,42 +194,51 @@ class IntrospectionInterpreter(AstInterpreter): return None name = args[0] srcqueue = [node] + extra_queue = [] # Process the sources BEFORE flattening the kwargs, to preserve the original nodes if 'sources' in kwargs_raw: srcqueue += mesonlib.listify(kwargs_raw['sources']) + if 'extra_files' in kwargs_raw: + extra_queue += mesonlib.listify(kwargs_raw['extra_files']) + kwargs = self.flatten_kwargs(kwargs_raw, True) - source_nodes = [] # type: T.List[BaseNode] - while srcqueue: - curr = srcqueue.pop(0) - arg_node = None - assert(isinstance(curr, BaseNode)) - if isinstance(curr, FunctionNode): - arg_node = curr.args - elif isinstance(curr, ArrayNode): - arg_node = curr.args - elif isinstance(curr, IdNode): - # Try to resolve the ID and append the node to the queue - assert isinstance(curr.value, str) - var_name = curr.value - if var_name in self.assignments: - tmp_node = self.assignments[var_name] - if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)): - srcqueue += [tmp_node] - elif isinstance(curr, ArithmeticNode): - srcqueue += [curr.left, curr.right] - if arg_node is None: - continue - arg_nodes = arg_node.arguments.copy() - # Pop the first element if the function is a build target function - if isinstance(curr, FunctionNode) and curr.func_name in build_target_functions: - arg_nodes.pop(0) - elemetary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))] - srcqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))] - if elemetary_nodes: - source_nodes += [curr] + def traverse_nodes(inqueue: T.List[BaseNode]) -> T.List[BaseNode]: + res = [] # type: T.List[BaseNode] + while inqueue: + curr = inqueue.pop(0) + arg_node = None + assert(isinstance(curr, BaseNode)) + if isinstance(curr, FunctionNode): + arg_node = curr.args + elif isinstance(curr, ArrayNode): + arg_node = curr.args + elif isinstance(curr, IdNode): + # Try to resolve the ID and append the node to the queue + assert isinstance(curr.value, str) + var_name = curr.value + if var_name in self.assignments: + tmp_node = self.assignments[var_name] + if isinstance(tmp_node, (ArrayNode, IdNode, FunctionNode)): + inqueue += [tmp_node] + elif isinstance(curr, ArithmeticNode): + inqueue += [curr.left, curr.right] + if arg_node is None: + continue + arg_nodes = arg_node.arguments.copy() + # Pop the first element if the function is a build target function + if isinstance(curr, FunctionNode) and curr.func_name in build_target_functions: + arg_nodes.pop(0) + elemetary_nodes = [x for x in arg_nodes if isinstance(x, (str, StringNode))] + inqueue += [x for x in arg_nodes if isinstance(x, (FunctionNode, ArrayNode, IdNode, ArithmeticNode))] + if elemetary_nodes: + res += [curr] + return res + + source_nodes = traverse_nodes(srcqueue) + extraf_nodes = traverse_nodes(extra_queue) # Make sure nothing can crash when creating the build class kwargs_reduced = {k: v for k, v in kwargs.items() if k in targetclass.known_kwargs and k in ['install', 'build_by_default', 'build_always']} @@ -251,6 +260,7 @@ class IntrospectionInterpreter(AstInterpreter): 'installed': target.should_install(), 'outputs': target.get_outputs(), 'sources': source_nodes, + 'extra_files': extraf_nodes, 'kwargs': kwargs, 'node': node, } diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 3e883e9..23734a8 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -1390,7 +1390,7 @@ class Backend: This is a limited fallback / reference implementation. The backend should override this method. ''' if isinstance(target, (build.CustomTarget, build.BuildTarget)): - source_list_raw = target.sources + target.extra_files + source_list_raw = target.sources source_list = [] for j in source_list_raw: if isinstance(j, mesonlib.File): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index bc1f54a..26b54ff 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -370,6 +370,7 @@ a hard error in the future.'''.format(name)) self.build_always_stale = False self.option_overrides_base = {} self.option_overrides_compiler = defaultdict(dict) + self.extra_files = [] # type: T.List[File] if not hasattr(self, 'typename'): raise RuntimeError('Target type is not set for target class "{}". This is a bug'.format(type(self).__name__)) @@ -518,7 +519,6 @@ class BuildTarget(Target): self.pch = {} self.extra_args = {} self.generated = [] - self.extra_files = [] self.d_features = {} self.pic = False self.pie = False @@ -1011,7 +1011,7 @@ This will become a hard error in a future Meson release.''') if self.gnu_symbol_visibility not in permitted: raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.symbol_visibility, ', '.join(permitted))) - def validate_win_subsystem(self, value: str) -> str: + def validate_win_subsystem(self, value: str) -> str: value = value.lower() if re.fullmatch(r'(boot_application|console|efi_application|efi_boot_service_driver|efi_rom|efi_runtime_driver|native|posix|windows)(,\d+(\.\d+)?)?', value) is None: raise InvalidArguments('Invalid value for win_subsystem: {}.'.format(value)) @@ -2058,7 +2058,6 @@ class CustomTarget(Target): self.depend_files = [] # Files that this target depends on but are not on the command line. self.depfile = None self.process_kwargs(kwargs, backend) - self.extra_files = [] # Whether to use absolute paths for all files on the commandline self.absolute_paths = absolute_paths unknowns = [] diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 4c1e8ea..924a103 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -28,7 +28,7 @@ from . import mlog from .backend import backends from .mparser import BaseNode, FunctionNode, ArrayNode, ArgumentNode, StringNode from .interpreter import Interpreter -from ._pathlib import PurePath +from ._pathlib import Path, PurePath import typing as T import os import argparse @@ -119,9 +119,10 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]: def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]]: tlist = [] # type: T.List[T.Dict[str, T.Union[bool, str, T.List[T.Union[str, T.Dict[str, T.Union[str, T.List[str], bool]]]]]]] - for i in intr.targets: - sources = [] # type: T.List[str] - for n in i['sources']: + root_dir = Path(intr.source_root) + def nodes_to_paths(node_list: T.List[BaseNode]) -> T.List[Path]: + res = [] # type: T.List[Path] + for n in node_list: args = [] # type: T.List[BaseNode] if isinstance(n, FunctionNode): args = list(n.args.arguments) @@ -134,9 +135,16 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st for j in args: if isinstance(j, StringNode): assert isinstance(j.value, str) - sources += [j.value] + res += [Path(j.value)] elif isinstance(j, str): - sources += [j] + res += [Path(j)] + res = [root_dir / i['subdir'] / x for x in res] + res = [x.resolve() for x in res] + return res + + for i in intr.targets: + sources = nodes_to_paths(i['sources']) + extra_f = nodes_to_paths(i['extra_files']) tlist += [{ 'name': i['name'], @@ -149,9 +157,10 @@ def list_targets_from_source(intr: IntrospectionInterpreter) -> T.List[T.Dict[st 'language': 'unknown', 'compiler': [], 'parameters': [], - 'sources': [os.path.normpath(os.path.join(os.path.abspath(intr.source_root), i['subdir'], x)) for x in sources], + 'sources': [str(x) for x in sources], 'generated_sources': [] }], + 'extra_files': [str(x) for x in extra_f], 'subproject': None, # Subprojects are not supported 'installed': i['installed'] }] @@ -182,6 +191,7 @@ def list_targets(builddata: build.Build, installdata: backends.InstallData, back 'filename': [os.path.join(build_dir, target.subdir, x) for x in target.get_outputs()], 'build_by_default': target.build_by_default, 'target_sources': backend.get_introspection_data(idname, target), + 'extra_files': [os.path.normpath(os.path.join(src_dir, x.subdir, x.fname)) for x in target.extra_files], 'subproject': target.subproject or None } diff --git a/run_unittests.py b/run_unittests.py index f6adcee..708dcb7 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -4308,10 +4308,22 @@ recommended as it is not supported on some platforms''') infodir = os.path.join(self.builddir, 'meson-info') self.assertPathExists(infodir) - def assertKeyTypes(key_type_list, obj): + def assertKeyTypes(key_type_list, obj, strict: bool = True): for i in key_type_list: + if isinstance(i[1], (list, tuple)) and None in i[1]: + i = (i[0], tuple([x for x in i[1] if x is not None])) + if i[0] not in obj or obj[i[0]] is None: + continue self.assertIn(i[0], obj) self.assertIsInstance(obj[i[0]], i[1]) + if strict: + for k in obj.keys(): + found = False + for i in key_type_list: + if k == i[0]: + found = True + break + self.assertTrue(found, 'Key "{}" not in expected list'.format(k)) root_keylist = [ ('benchmarks', list), @@ -4333,6 +4345,8 @@ recommended as it is not supported on some platforms''') ('is_parallel', bool), ('protocol', str), ('depends', list), + ('workdir', (str, None)), + ('priority', int), ] buildoptions_keylist = [ @@ -4341,6 +4355,8 @@ recommended as it is not supported on some platforms''') ('type', str), ('description', str), ('machine', str), + ('choices', (list, None)), + ('value', (str, int, bool, list)), ] buildoptions_typelist = [ @@ -4369,6 +4385,9 @@ recommended as it is not supported on some platforms''') ('filename', list), ('build_by_default', bool), ('target_sources', list), + ('extra_files', list), + ('subproject', (str, None)), + ('install_filename', (list, None)), ('installed', bool), ] @@ -4422,7 +4441,7 @@ recommended as it is not supported on some platforms''') for j in buildoptions_typelist: if i['type'] == j[0]: self.assertIsInstance(i['value'], j[1]) - assertKeyTypes(j[2], i) + assertKeyTypes(j[2], i, strict=False) valid_type = True break @@ -9328,18 +9347,20 @@ def main(): 'LinuxlikeTests', 'LinuxCrossArmTests', 'LinuxCrossMingwTests', 'WindowsTests', 'DarwinTests'] - # Don't use pytest-xdist when running single unit tests since it wastes - # time spawning a lot of processes to distribute tests to in that case. - if not running_single_tests(sys.argv, cases): - try: - import pytest # noqa: F401 - # Need pytest-xdist for `-n` arg - import xdist # noqa: F401 - pytest_args = ['-n', 'auto', './run_unittests.py'] - pytest_args += convert_args(sys.argv[1:]) - return subprocess.run(python_command + ['-m', 'pytest'] + pytest_args).returncode - except ImportError: - print('pytest-xdist not found, using unittest instead') + try: + import pytest # noqa: F401 + # Need pytest-xdist for `-n` arg + import xdist # noqa: F401 + pytest_args = [] + # Don't use pytest-xdist when running single unit tests since it wastes + # time spawning a lot of processes to distribute tests to in that case. + if not running_single_tests(sys.argv, cases): + pytest_args += ['-n', 'auto'] + pytest_args += ['./run_unittests.py'] + pytest_args += convert_args(sys.argv[1:]) + return subprocess.run(python_command + ['-m', 'pytest'] + pytest_args).returncode + except ImportError: + print('pytest-xdist not found, using unittest instead') # Fallback to plain unittest. return unittest.main(defaultTest=cases, buffer=True) diff --git a/test cases/unit/57 introspection/sharedlib/meson.build b/test cases/unit/57 introspection/sharedlib/meson.build index 3de3493..7640bc7 100644 --- a/test cases/unit/57 introspection/sharedlib/meson.build +++ b/test cases/unit/57 introspection/sharedlib/meson.build @@ -1,2 +1,2 @@ SRC_shared = ['shared.cpp'] -sharedlib = shared_library('sharedTestLib', SRC_shared) +sharedlib = shared_library('sharedTestLib', SRC_shared, extra_files: ['shared.hpp']) diff --git a/test cases/unit/57 introspection/staticlib/meson.build b/test cases/unit/57 introspection/staticlib/meson.build index b1b9afe..1cbb020 100644 --- a/test cases/unit/57 introspection/staticlib/meson.build +++ b/test cases/unit/57 introspection/staticlib/meson.build @@ -1,2 +1,3 @@ SRC_static = ['static.c'] -staticlib = static_library('staticTestLib', SRC_static) +extra_static = files(['static.h']) +staticlib = static_library('staticTestLib', SRC_static, extra_files: extra_static) diff --git a/test cases/unit/57 introspection/staticlib/static.h b/test cases/unit/57 introspection/staticlib/static.h index 506784e..06da508 100644 --- a/test cases/unit/57 introspection/staticlib/static.h +++ b/test cases/unit/57 introspection/staticlib/static.h @@ -1,3 +1,11 @@ #pragma once -int add_numbers(int a, int b);
\ No newline at end of file +#ifdef __cplusplus +extern "C" { +#endif + +int add_numbers(int a, int b); + +#ifdef __cplusplus +} +#endif |