diff options
| author | Eli Schwartz <eschwartz@archlinux.org> | 2022-05-19 19:17:01 -0400 |
|---|---|---|
| committer | Eli Schwartz <eschwartz93@gmail.com> | 2025-01-07 20:16:05 -0500 |
| commit | eb1e52afa142fc0f38260a9cb3413f2bd63b1675 (patch) | |
| tree | 26c54730d3d586c6305da7127ed5716da78407da | |
| parent | dfe5cbb3e432bb632731f853df05e2023ab233d6 (diff) | |
| download | meson-eb1e52afa142fc0f38260a9cb3413f2bd63b1675.tar.gz | |
mtest: fix rebuilding all before running tests
Inconsistency in the original implementation of commit
79e2c52a15e896e46ff3cfa3ec16fbf3f132ee01.
If an explicit list of targets is passed on the CLI, then that is passed
to rebuild_deps. If not, we pass every loaded test to rebuild_deps
instead. This means we cannot distinguish between "trying to run all
tests" and "trying to run specific tests". We then load all the deps for
all tests, and try to build them all as explicit arguments to the
underlying ninja.
There are two situations where this falls flat:
- given underspecified deps
- given all (selected?) tests legitimately happen to have no
dependencies
In both cases, we calculate that there are no deps to rebuild, we run
ninja without any targets, and this invokes the default "all" rule and
maybe builds a few thousand targets that this specific test run does not
need.
Additionally, in large projects which define many tests with many
dependencies, we could end up overflowing ARG_MAX when processing *all*
tests.
Instead, pass no tests to rebuild_deps. We then specially handle this by
directly running the relevant ninja target for "all test deps", which is
overall more elegant than specifying many many dependencies by name.
Given a subset of tests to guarantee the freshness of, we instead skip
running ninja at all if there are indeed no test dependencies.
| -rw-r--r-- | mesonbuild/mtest.py | 48 | ||||
| -rw-r--r-- | test cases/unit/106 underspecified mtest/main.c | 1 | ||||
| -rw-r--r-- | test cases/unit/106 underspecified mtest/meson.build | 8 | ||||
| -rwxr-xr-x | test cases/unit/106 underspecified mtest/runner.py | 5 | ||||
| -rw-r--r-- | unittests/platformagnostictests.py | 15 |
5 files changed, 59 insertions, 18 deletions
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index d0added78..39970e530 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -1825,9 +1825,10 @@ class TestHarness: raise RuntimeError('Test harness object can only be used once.') self.is_run = True tests = self.get_tests() + rebuild_only_tests = tests if self.options.args else [] if not tests: return 0 - if not self.options.no_rebuild and not rebuild_deps(self.ninja, self.options.wd, tests): + if not self.options.no_rebuild and not rebuild_deps(self.ninja, self.options.wd, rebuild_only_tests, self.options.benchmark): # We return 125 here in case the build failed. # The reason is that exit code 125 tells `git bisect run` that the current # commit should be skipped. Thus users can directly use `meson test` to @@ -2140,7 +2141,7 @@ def list_tests(th: TestHarness) -> bool: print(th.get_pretty_suite(t)) return not tests -def rebuild_deps(ninja: T.List[str], wd: str, tests: T.List[TestSerialisation]) -> bool: +def rebuild_deps(ninja: T.List[str], wd: str, tests: T.List[TestSerialisation], benchmark: bool) -> bool: def convert_path_to_target(path: str) -> str: path = os.path.relpath(path, wd) if os.sep != '/': @@ -2149,23 +2150,34 @@ def rebuild_deps(ninja: T.List[str], wd: str, tests: T.List[TestSerialisation]) assert len(ninja) > 0 - targets_file = os.path.join(wd, 'meson-info/intro-targets.json') - with open(targets_file, encoding='utf-8') as fp: - targets_info = json.load(fp) - - depends: T.Set[str] = set() targets: T.Set[str] = set() - intro_targets: T.Dict[str, T.List[str]] = {} - for target in targets_info: - intro_targets[target['id']] = [ - convert_path_to_target(f) - for f in target['filename']] - for t in tests: - for d in t.depends: - if d in depends: - continue - depends.update(d) - targets.update(intro_targets[d]) + if tests: + targets_file = os.path.join(wd, 'meson-info/intro-targets.json') + with open(targets_file, encoding='utf-8') as fp: + targets_info = json.load(fp) + + depends: T.Set[str] = set() + intro_targets: T.Dict[str, T.List[str]] = {} + for target in targets_info: + intro_targets[target['id']] = [ + convert_path_to_target(f) + for f in target['filename']] + for t in tests: + for d in t.depends: + if d in depends: + continue + depends.update(d) + targets.update(intro_targets[d]) + else: + if benchmark: + targets.add('meson-benchmark-prereq') + else: + targets.add('meson-test-prereq') + + if not targets: + # We want to build minimal deps, but if the subset of targets have no + # deps then ninja falls back to 'all'. + return True ret = subprocess.run(ninja + ['-C', wd] + sorted(targets)).returncode if ret != 0: diff --git a/test cases/unit/106 underspecified mtest/main.c b/test cases/unit/106 underspecified mtest/main.c new file mode 100644 index 000000000..8842fc122 --- /dev/null +++ b/test cases/unit/106 underspecified mtest/main.c @@ -0,0 +1 @@ +int main(void) { return 0 ; } diff --git a/test cases/unit/106 underspecified mtest/meson.build b/test cases/unit/106 underspecified mtest/meson.build new file mode 100644 index 000000000..c0a88d677 --- /dev/null +++ b/test cases/unit/106 underspecified mtest/meson.build @@ -0,0 +1,8 @@ +project('underspecified deps', 'c') + +runner = find_program('runner.py') +exe1 = executable('main1', 'main.c') +exe2 = executable('main2', 'main.c') + +test('runner-with-exedep', runner, args: exe1) +test('runner-without-dep', runner, args: exe2.full_path()) diff --git a/test cases/unit/106 underspecified mtest/runner.py b/test cases/unit/106 underspecified mtest/runner.py new file mode 100755 index 000000000..9fb9ac40b --- /dev/null +++ b/test cases/unit/106 underspecified mtest/runner.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +import sys, subprocess + +subprocess.run(sys.argv[1:], check=True) diff --git a/unittests/platformagnostictests.py b/unittests/platformagnostictests.py index 2fb75f284..ef277dee2 100644 --- a/unittests/platformagnostictests.py +++ b/unittests/platformagnostictests.py @@ -6,6 +6,7 @@ from __future__ import annotations import json import os import pickle +import subprocess import tempfile import subprocess import textwrap @@ -508,3 +509,17 @@ class PlatformAgnosticTests(BasePlatformTests): f.write("option('new_option', type : 'boolean', value : false)") self.setconf('-Dsubproject:new_option=true') self.assertEqual(self.getconf('subproject:new_option'), True) + + def test_mtest_rebuild_deps(self): + testdir = os.path.join(self.unit_test_dir, '106 underspecified mtest') + self.init(testdir) + + with self.assertRaises(subprocess.CalledProcessError): + self._run(self.mtest_command) + self.clean() + + with self.assertRaises(subprocess.CalledProcessError): + self._run(self.mtest_command + ['runner-without-dep']) + self.clean() + + self._run(self.mtest_command + ['runner-with-exedep']) |
