diff --git a/mesonbuild/mdevenv.py b/mesonbuild/mdevenv.py index 7594db2f2..c304cbb52 100644 --- a/mesonbuild/mdevenv.py +++ b/mesonbuild/mdevenv.py @@ -4,12 +4,12 @@ import tempfile from pathlib import Path from . import build -from .mesonlib import MesonException, is_windows +from .mesonlib import MesonException, RealPathAction, is_windows import typing as T def add_arguments(parser: argparse.ArgumentParser) -> None: - parser.add_argument('-C', default='.', dest='wd', + parser.add_argument('-C', dest='wd', action=RealPathAction, help='directory to cd into before running') parser.add_argument('command', nargs=argparse.REMAINDER, help='Command to run in developer environment (default: interactive shell)') @@ -37,7 +37,6 @@ def get_env(b: build.Build, build_dir: str) -> T.Dict[str, str]: return extra_env.get_env(env) def run(options: argparse.Namespace) -> int: - options.wd = os.path.abspath(options.wd) buildfile = Path(options.wd) / 'meson-private' / 'build.dat' if not buildfile.is_file(): raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py index e63ab1783..afa1b4c56 100644 --- a/mesonbuild/mdist.py +++ b/mesonbuild/mdist.py @@ -23,7 +23,8 @@ import json from glob import glob from pathlib import Path from mesonbuild.environment import detect_ninja -from mesonbuild.mesonlib import windows_proof_rmtree, MesonException, quiet_git +from mesonbuild.mesonlib import (MesonException, RealPathAction, quiet_git, + windows_proof_rmtree) from mesonbuild.wrap import wrap from mesonbuild import mlog, build from .scripts.meson_exe import run_exe @@ -35,7 +36,7 @@ archive_extension = {'gztar': '.tar.gz', 'zip': '.zip'} def add_arguments(parser): - parser.add_argument('-C', default='.', dest='wd', + parser.add_argument('-C', dest='wd', action=RealPathAction, help='directory to cd into before running') parser.add_argument('--formats', default='xztar', help='Comma separated list of archive types to create. Supports xztar (default), gztar, and zip.') @@ -270,7 +271,6 @@ def determine_archives_to_generate(options): return result def run(options): - options.wd = os.path.abspath(options.wd) buildfile = Path(options.wd) / 'meson-private' / 'build.dat' if not buildfile.is_file(): raise MesonException(f'Directory {options.wd!r} does not seem to be a Meson build directory.') diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 17f604d58..65d21eee1 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -14,6 +14,7 @@ """A library of random helper functionality.""" from pathlib import Path +import argparse import enum import sys import stat @@ -70,6 +71,7 @@ __all__ = [ 'PerThreeMachine', 'PerThreeMachineDefaultable', 'ProgressBar', + 'RealPathAction', 'TemporaryDirectoryWinProof', 'Version', 'check_direntry_issues', @@ -1843,6 +1845,17 @@ else: ProgressBar = ProgressBarTqdm +class RealPathAction(argparse.Action): + def __init__(self, option_strings: T.List[str], dest: str, default: str = '.', **kwargs: T.Any): + default = os.path.abspath(os.path.realpath(default)) + super().__init__(option_strings, dest, nargs=None, default=default, **kwargs) + + def __call__(self, parser: argparse.ArgumentParser, namespace: argparse.Namespace, + values: T.Union[str, T.Sequence[T.Any], None], option_string: str = None) -> None: + assert isinstance(values, str) + setattr(namespace, self.dest, os.path.abspath(os.path.realpath(values))) + + def get_wine_shortpath(winecmd: T.List[str], wine_paths: T.Sequence[str]) -> str: """Get A short version of @wine_paths to avoid reaching WINEPATH number of char limit. diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index efc5d2cd6..124e6c671 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -139,7 +139,8 @@ def add_arguments(parser: 'argparse.ArgumentParser') -> None: Meson project. ''' parser.add_argument("srcfiles", metavar="sourcefile", nargs="*", help="source files. default: all recognized files in current directory") - parser.add_argument('-C', default='.', dest='wd', help='directory to cd into before running') + parser.add_argument('-C', dest='wd', action=mesonlib.RealPathAction, + help='directory to cd into before running') parser.add_argument("-n", "--name", help="project name. default: name of current directory") parser.add_argument("-e", "--executable", help="executable name. default: project name") parser.add_argument("-d", "--deps", help="dependencies, comma-separated") diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index b631d4a9e..e753d94db 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -28,7 +28,7 @@ from . import environment from .backend.backends import InstallData from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version -from .mesonlib import is_windows, Popen_safe +from .mesonlib import Popen_safe, RealPathAction, is_windows from .scripts import depfixer, destdir_join from .scripts.meson_exe import run_exe try: @@ -65,7 +65,7 @@ build definitions so that it will not break when the change happens.''' selinux_updates: T.List[str] = [] def add_arguments(parser: argparse.ArgumentParser) -> None: - parser.add_argument('-C', default='.', dest='wd', + parser.add_argument('-C', dest='wd', action=RealPathAction, help='directory to cd into before running') parser.add_argument('--profile-self', action='store_true', dest='profile', help=argparse.SUPPRESS) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 826619fc5..4c1d00e37 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -43,7 +43,8 @@ from . import environment from . import mlog from .coredata import major_versions_differ, MesonVersionMismatchException from .coredata import version as coredata_version -from .mesonlib import MesonException, OrderedSet, get_wine_shortpath, split_args, join_args +from .mesonlib import (MesonException, OrderedSet, RealPathAction, + get_wine_shortpath, join_args, split_args) from .mintro import get_infodir, load_info_file from .programs import ExternalProgram from .backend.backends import TestProtocol, TestSerialisation @@ -104,7 +105,7 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: help='List available tests.') parser.add_argument('--wrapper', default=None, dest='wrapper', type=split_args, help='wrapper to run tests with (e.g. Valgrind)') - parser.add_argument('-C', default='.', dest='wd', + parser.add_argument('-C', dest='wd', action=RealPathAction, # https://github.com/python/typeshed/issues/3107 # https://github.com/python/mypy/issues/7177 type=os.path.abspath, # type: ignore @@ -1485,8 +1486,7 @@ class TestHarness: startdir = os.getcwd() try: - if self.options.wd: - os.chdir(self.options.wd) + os.chdir(self.options.wd) self.build_data = build.load(os.getcwd()) if not self.options.setup: self.options.setup = self.build_data.test_setup_default_name @@ -1656,8 +1656,7 @@ class TestHarness: self.name_max_len = max([uniwidth(self.get_pretty_suite(test)) for test in tests]) startdir = os.getcwd() try: - if self.options.wd: - os.chdir(self.options.wd) + os.chdir(self.options.wd) runners = [] # type: T.List[SingleTestRunner] for i in range(self.options.repeat): runners.extend(self.get_test_runner(test) for test in tests) diff --git a/run_unittests.py b/run_unittests.py index 29edf6ab0..200c4edc8 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -6896,6 +6896,18 @@ class LinuxlikeTests(BasePlatformTests): pcfile = f.read() self.assertFalse('blub_blob_blib' in pcfile) + def test_symlink_builddir(self): + ''' + Test using a symlink as either the builddir for "setup" or + the argument for "-C". + ''' + testdir = os.path.join(self.common_test_dir, '1 trivial') + os.symlink(self.builddir, self.builddir + '-symlink') + self.change_builddir(self.builddir + '-symlink') + self.init(testdir) + self.build() + self._run(self.mtest_command) + def test_vala_c_warnings(self): ''' Test that no warnings are emitted for C code generated by Vala. This