The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
4.1 KiB
123 lines
4.1 KiB
# SPDX-License-Identifier: Apache-2.0 |
|
# Copyright 2024 The Meson development team |
|
|
|
from __future__ import annotations |
|
|
|
import sys, os, subprocess, shutil |
|
import pathlib |
|
import typing as T |
|
|
|
if T.TYPE_CHECKING: |
|
import argparse |
|
|
|
from ..mesonlib import get_meson_command |
|
|
|
# Note: when adding arguments, please also add them to the completion |
|
# scripts in $MESONSRC/data/shell-completions/ |
|
def add_arguments(parser: 'argparse.ArgumentParser') -> None: |
|
parser.add_argument('--intermediaries', |
|
default=False, |
|
action='store_true', |
|
help='Check intermediate files.') |
|
parser.add_argument('mesonargs', nargs='*', |
|
help='Arguments to pass to "meson setup".') |
|
|
|
IGNORE_PATTERNS = ('.ninja_log', |
|
'.ninja_deps', |
|
'meson-private', |
|
'meson-logs', |
|
'meson-info', |
|
) |
|
|
|
INTERMEDIATE_EXTENSIONS = ('.gch', |
|
'.pch', |
|
'.o', |
|
'.obj', |
|
'.class', |
|
) |
|
|
|
class ReproTester: |
|
def __init__(self, options: T.Any): |
|
self.args = options.mesonargs |
|
self.meson = get_meson_command()[:] |
|
self.builddir = pathlib.Path('buildrepro') |
|
self.storagedir = pathlib.Path('buildrepro.1st') |
|
self.issues: T.List[str] = [] |
|
self.check_intermediaries = options.intermediaries |
|
|
|
def run(self) -> int: |
|
if not os.path.isfile('meson.build'): |
|
sys.exit('This command needs to be run at your project source root.') |
|
self.disable_ccache() |
|
self.cleanup() |
|
self.build() |
|
self.check_output() |
|
self.print_results() |
|
if not self.issues: |
|
self.cleanup() |
|
return len(self.issues) |
|
|
|
def disable_ccache(self) -> None: |
|
os.environ['CCACHE_DISABLE'] = '1' |
|
|
|
def cleanup(self) -> None: |
|
if self.builddir.exists(): |
|
shutil.rmtree(self.builddir) |
|
if self.storagedir.exists(): |
|
shutil.rmtree(self.storagedir) |
|
|
|
def build(self) -> None: |
|
setup_command: T.Sequence[str] = self.meson + ['setup', str(self.builddir)] + self.args |
|
build_command: T.Sequence[str] = self.meson + ['compile', '-C', str(self.builddir)] |
|
subprocess.check_call(setup_command) |
|
subprocess.check_call(build_command) |
|
self.builddir.rename(self.storagedir) |
|
subprocess.check_call(setup_command) |
|
subprocess.check_call(build_command) |
|
|
|
def ignore_file(self, fstr: str) -> bool: |
|
for p in IGNORE_PATTERNS: |
|
if p in fstr: |
|
return True |
|
if not self.check_intermediaries: |
|
if fstr.endswith(INTERMEDIATE_EXTENSIONS): |
|
return True |
|
return False |
|
|
|
def check_contents(self, fromdir: str, todir: str, check_contents: bool) -> None: |
|
import filecmp |
|
frompath = fromdir + '/' |
|
topath = todir + '/' |
|
for fromfile in pathlib.Path(fromdir).glob('**/*'): |
|
if not fromfile.is_file(): |
|
continue |
|
fstr = fromfile.as_posix() |
|
if self.ignore_file(fstr): |
|
continue |
|
assert fstr.startswith(frompath) |
|
tofile = pathlib.Path(fstr.replace(frompath, topath, 1)) |
|
if not tofile.exists(): |
|
self.issues.append(f'Missing file: {tofile}') |
|
elif check_contents: |
|
if not filecmp.cmp(fromfile, tofile, shallow=False): |
|
self.issues.append(f'File contents differ: {fromfile}') |
|
|
|
def print_results(self) -> None: |
|
if self.issues: |
|
print('Build differences detected') |
|
for i in self.issues: |
|
print(i) |
|
else: |
|
print('No differences detected.') |
|
|
|
def check_output(self) -> None: |
|
self.check_contents('buildrepro', 'buildrepro.1st', True) |
|
self.check_contents('buildrepro.1st', 'buildrepro', False) |
|
|
|
def run(options: T.Any) -> None: |
|
rt = ReproTester(options) |
|
try: |
|
sys.exit(rt.run()) |
|
except Exception as e: |
|
print(e) |
|
sys.exit(1)
|
|
|