diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2020-12-15 21:21:50 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-15 21:21:50 +0000 |
commit | d32d0d6b53dc84e35f6b26fbcfaf23a37ab6b002 (patch) | |
tree | 437d20b8b08459dcb14289bf185ef2a2c003ecc1 | |
parent | c9685ac5612eb040961efe5dc60a91fc5aa686b4 (diff) | |
parent | 79e2c52a15e896e46ff3cfa3ec16fbf3f132ee01 (diff) | |
download | meson-d32d0d6b53dc84e35f6b26fbcfaf23a37ab6b002.zip meson-d32d0d6b53dc84e35f6b26fbcfaf23a37ab6b002.tar.gz meson-d32d0d6b53dc84e35f6b26fbcfaf23a37ab6b002.tar.bz2 |
Merge pull request #7902 from bonzini/mtest-build-depends-only
mtest: only build what is needed for the tests
-rw-r--r-- | docs/markdown/snippets/meson_test_depends.md | 17 | ||||
-rw-r--r-- | mesonbuild/minstall.py | 18 | ||||
-rw-r--r-- | mesonbuild/mintro.py | 47 | ||||
-rw-r--r-- | mesonbuild/mtest.py | 38 |
4 files changed, 91 insertions, 29 deletions
diff --git a/docs/markdown/snippets/meson_test_depends.md b/docs/markdown/snippets/meson_test_depends.md new file mode 100644 index 0000000..09d78f1 --- /dev/null +++ b/docs/markdown/snippets/meson_test_depends.md @@ -0,0 +1,17 @@ +## `meson test` only rebuilds test dependencies + +Until now, `meson test` rebuilt the whole project independent of the +requested tests and their dependencies. With this release, `meson test` +will only rebuild what is needed for the tests or suites that will be run. +This feature can be used, for example, to speed up bisecting regressions +using commands like the following: + + git bisect start <broken commit> <working commit> + git bisect run meson test <failing test name> + +This would find the broken commit automatically while at each step +rebuilding only those pieces of code needed to run the test. + +However, this change could cause failures if dependencies are not +specified correctly in `meson.build`. + diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 47bb88b..a8ec8f3 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -19,7 +19,6 @@ from glob import glob from .scripts import depfixer from .scripts import destdir_join from .mesonlib import is_windows, Popen_safe -from .mtest import rebuild_all from .backend.backends import InstallData from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version @@ -532,6 +531,23 @@ class Installer: else: raise +def rebuild_all(wd: str) -> bool: + if not (Path(wd) / 'build.ninja').is_file(): + print('Only ninja backend is supported to rebuild the project before installation.') + return True + + ninja = environment.detect_ninja() + if not ninja: + print("Can't find ninja, can't rebuild test.") + return False + + ret = subprocess.run(ninja + ['-C', wd]).returncode + if ret != 0: + print('Could not rebuild {}'.format(wd)) + return False + + return True + def run(opts): datafilename = 'meson-private/install.dat' private_dir = os.path.dirname(datafilename) diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index d36927f..f6262c3 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -385,12 +385,25 @@ def print_results(options: argparse.Namespace, results: T.Sequence[T.Tuple[str, print(json.dumps(out, indent=indent)) return 0 +def get_infodir(builddir: T.Optional[str] = None) -> str: + infodir = 'meson-info' + if builddir is not None: + infodir = os.path.join(builddir, infodir) + return infodir + +def get_info_file(infodir: str, kind: T.Optional[str] = None) -> str: + return os.path.join(infodir, + 'meson-info.json' if not kind else 'intro-{}.json'.format(kind)) + +def load_info_file(infodir: str, kind: T.Optional[str] = None) -> T.Any: + with open(get_info_file(infodir, kind), 'r') as fp: + return json.load(fp) + def run(options: argparse.Namespace) -> int: datadir = 'meson-private' - infodir = 'meson-info' + infodir = get_infodir(options.builddir) if options.builddir is not None: datadir = os.path.join(options.builddir, datadir) - infodir = os.path.join(options.builddir, infodir) indent = 4 if options.indent else None results = [] # type: T.List[T.Tuple[str, T.Union[dict, T.List[T.Any]]]] sourcedir = '.' if options.builddir == 'meson.build' else options.builddir[:-11] @@ -411,17 +424,18 @@ def run(options: argparse.Namespace) -> int: results += [(key, val.no_bd(intr))] return print_results(options, results, indent) - infofile = get_meson_info_file(infodir) - if not os.path.isdir(datadir) or not os.path.isdir(infodir) or not os.path.isfile(infofile): - print('Current directory is not a meson build directory.\n' - 'Please specify a valid build dir or change the working directory to it.\n' - 'It is also possible that the build directory was generated with an old\n' - 'meson version. Please regenerate it in this case.') - return 1 - - with open(infofile, 'r') as fp: - raw = json.load(fp) + try: + raw = load_info_file(infodir) intro_vers = raw.get('introspection', {}).get('version', {}).get('full', '0.0.0') + except FileNotFoundError: + if not os.path.isdir(datadir) or not os.path.isdir(infodir): + print('Current directory is not a meson build directory.\n' + 'Please specify a valid build dir or change the working directory to it.') + else: + print('Introspection file {} does not exist.\n' + 'It is also possible that the build directory was generated with an old\n' + 'meson version. Please regenerate it in this case.'.format(get_info_file(infodir))) + return 1 vers_to_check = get_meson_introspection_required_version() for i in vers_to_check: @@ -437,12 +451,11 @@ def run(options: argparse.Namespace) -> int: continue if not options.all and not getattr(options, i, False): continue - curr = os.path.join(infodir, 'intro-{}.json'.format(i)) - if not os.path.isfile(curr): - print('Introspection file {} does not exist.'.format(curr)) + try: + results += [(i, load_info_file(infodir, i))] + except FileNotFoundError: + print('Introspection file {} does not exist.'.format(get_info_file(infodir, i))) return 1 - with open(curr, 'r') as fp: - results += [(i, json.load(fp))] return print_results(options, results, indent) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index a92b5cc..4f687a5 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -45,6 +45,7 @@ from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version from .dependencies import ExternalProgram from .mesonlib import MesonException, get_wine_shortpath, split_args, join_args +from .mintro import get_infodir, load_info_file from .backend.backends import TestProtocol, TestSerialisation # GNU autotools interprets a return code of 77 from tests it executes to @@ -1019,13 +1020,21 @@ class TestHarness: def total_failure_count(self) -> int: return self.fail_count + self.unexpectedpass_count + self.timeout_count - def doit(self) -> int: + def doit(self, options: argparse.Namespace) -> int: if self.is_run: raise RuntimeError('Test harness object can only be used once.') self.is_run = True tests = self.get_tests() if not tests: return 0 + if not options.no_rebuild and not rebuild_deps(options.wd, tests): + # 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 + # bisect without needing to handle the does-not-build case separately in a + # wrapper script. + sys.exit(125) + self.run_tests(tests) return self.total_failure_count() @@ -1272,7 +1281,7 @@ def list_tests(th: TestHarness) -> bool: print(th.get_pretty_suite(t)) return not tests -def rebuild_all(wd: str) -> bool: +def rebuild_deps(wd: str, tests: T.List[TestSerialisation]) -> bool: if not (Path(wd) / 'build.ninja').is_file(): print('Only ninja backend is supported to rebuild tests before running them.') return True @@ -1282,7 +1291,21 @@ def rebuild_all(wd: str) -> bool: print("Can't find ninja, can't rebuild test.") return False - ret = subprocess.run(ninja + ['-C', wd]).returncode + depends = set() # type: T.Set[str] + targets = set() # type: T.Set[str] + intro_targets = dict() # type: T.Dict[str, T.List[str]] + for target in load_info_file(get_infodir(wd), kind='targets'): + intro_targets[target['id']] = [ + os.path.relpath(f, wd) + 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]) + + ret = subprocess.run(ninja + ['-C', wd] + sorted(targets)).returncode if ret != 0: print('Could not rebuild {}'.format(wd)) return False @@ -1318,18 +1341,11 @@ def run(options: argparse.Namespace) -> int: print('Could not find requested program: {!r}'.format(check_bin)) return 1 - if not options.list and not options.no_rebuild: - if not rebuild_all(options.wd): - # 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 bisect without needing to handle the does-not-build case separately in a wrapper script. - return 125 - with TestHarness(options) as th: try: if options.list: return list_tests(th) - return th.doit() + return th.doit(options) except TestException as e: print('Meson test encountered an error:\n') if os.environ.get('MESON_FORCE_BACKTRACE'): |