diff --git a/mesonbuild/msubprojects.py b/mesonbuild/msubprojects.py index f4b4405634b2..3e61b7c3439f 100755 --- a/mesonbuild/msubprojects.py +++ b/mesonbuild/msubprojects.py @@ -720,7 +720,14 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: p.set_defaults(subprojects_func=Runner.packagefiles) def run(options: 'Arguments') -> int: - source_dir = os.path.relpath(os.path.realpath(options.sourcedir)) + real_source_dir = os.path.realpath(options.sourcedir) + try: + source_dir = os.path.relpath(real_source_dir) + except ValueError: + # On Windows, os.path.relpath raises ValueError when the source + # directory is on a different mount/UNC share than the current + # working directory. Fall back to the absolute path in that case. + source_dir = real_source_dir if not os.path.isfile(os.path.join(source_dir, 'meson.build')): mlog.error('Directory', mlog.bold(source_dir), 'does not seem to be a Meson source directory.') return 1 diff --git a/unittests/machinefiletests.py b/unittests/machinefiletests.py index b20b8172f9e4..d0e5868038a1 100644 --- a/unittests/machinefiletests.py +++ b/unittests/machinefiletests.py @@ -93,6 +93,30 @@ def test_home_variable(self): finally: os.unlink(fname) +class SubprojectsUpdateTests(TestCase): + + def test_subprojects_update_relpath_cross_mount(self): + """msubprojects.run() must not crash with an unhandled ValueError + when os.path.relpath() cannot compute a relative path (as happens + on Windows when the source directory is on a different mount/UNC + share than the current working directory).""" + import mesonbuild.msubprojects as msubprojects + + with tempfile.TemporaryDirectory() as tmpdir: + options = mock.Mock() + options.sourcedir = tmpdir + options.allow_insecure = False + + def raising_relpath(*args, **kwargs): + raise ValueError("path is on mount 'Z:', start on mount 'C:'") + + with mock.patch('os.path.relpath', side_effect=raising_relpath): + # No meson.build in tmpdir, so run() should reach the + # "does not seem to be a Meson source directory" error + # path and return 1, rather than raising ValueError. + result = msubprojects.run(options) + self.assertEqual(result, 1) + class NativeFileTests(BasePlatformTests): def setUp(self):