diff options
-rw-r--r-- | mesonbuild/coredata.py | 55 | ||||
-rw-r--r-- | mesonbuild/mesonmain.py | 9 | ||||
-rwxr-xr-x | run_unittests.py | 26 | ||||
-rw-r--r-- | test cases/failing/39 libdir must be inside prefix/meson.build | 2 | ||||
-rw-r--r-- | test cases/failing/40 prefix absolute/meson.build | 2 |
5 files changed, 78 insertions, 16 deletions
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index a7be91f..60bb10b 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -131,10 +131,54 @@ class CoreData: # Only to print a warning if it changes between Meson invocations. self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') + def sanitize_prefix(self, prefix): + if not os.path.isabs(prefix): + raise MesonException('prefix value {!r} must be an absolute path' + ''.format(prefix)) + if prefix.endswith('/') or prefix.endswith('\\'): + # On Windows we need to preserve the trailing slash if the + # string is of type 'C:\' because 'C:' is not an absolute path. + if len(prefix) == 3 and prefix[1] == ':': + pass + else: + prefix = prefix[:-1] + return prefix + + def sanitize_dir_option_value(self, prefix, option, value): + ''' + If the option is an installation directory option and the value is an + absolute path, check that it resides within prefix and return the value + as a path relative to the prefix. + + This way everyone can do f.ex, get_option('libdir') and be sure to get + the library directory relative to prefix. + ''' + if option.endswith('dir') and os.path.isabs(value) and \ + option not in builtin_dir_noprefix_options: + # Value must be a subdir of the prefix + if os.path.commonpath([value, prefix]) != prefix: + m = 'The value of the {!r} option is {!r} which must be a ' \ + 'subdir of the prefix {!r}.\nNote that if you pass a ' \ + 'relative path, it is assumed to be a subdir of prefix.' + raise MesonException(m.format(option, value, prefix)) + # Convert path to be relative to prefix + skip = len(prefix) + 1 + value = value[skip:] + return value + def init_builtins(self, options): self.builtins = {} + # Sanitize prefix + options.prefix = self.sanitize_prefix(options.prefix) + # Initialize other builtin options for key in get_builtin_options(): - args = [key] + builtin_options[key][1:-1] + [getattr(options, key, get_builtin_option_default(key))] + if hasattr(options, key): + value = getattr(options, key) + value = self.sanitize_dir_option_value(options.prefix, key, value) + setattr(options, key, value) + else: + value = get_builtin_option_default(key) + args = [key] + builtin_options[key][1:-1] + [value] self.builtins[key] = builtin_options[key][0](*args) def get_builtin_option(self, optname): @@ -143,7 +187,11 @@ class CoreData: raise RuntimeError('Tried to get unknown builtin option %s.' % optname) def set_builtin_option(self, optname, value): - if optname in self.builtins: + if optname == 'prefix': + value = self.sanitize_prefix(value) + elif optname in self.builtins: + prefix = self.builtins['prefix'].value + value = self.sanitize_dir_option_value(prefix, optname, value) self.builtins[optname].set_value(value) else: raise RuntimeError('Tried to set unknown builtin option %s.' % optname) @@ -235,6 +283,9 @@ builtin_options = { 'errorlogs': [UserBooleanOption, "Whether to print the logs from failing tests.", True], } +# Installation directories that can reside in a path outside of the prefix +builtin_dir_noprefix_options = {'sysconfdir', 'localstatedir', 'sharedstatedir'} + forbidden_target_names = {'clean': None, 'clean-ctlist': None, 'clean-gcno': None, diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 37b8055..031486c 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -73,15 +73,6 @@ class MesonApp: def __init__(self, dir1, dir2, script_launcher, handshake, options, original_cmd_line_args): (self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake) - if not os.path.isabs(options.prefix): - raise RuntimeError('--prefix value must be an absolute path: {!r}'.format(options.prefix)) - if options.prefix.endswith('/') or options.prefix.endswith('\\'): - # On Windows we need to preserve the trailing slash if the - # string is of type 'C:\' because 'C:' is not an absolute path. - if len(options.prefix) == 3 and options.prefix[1] == ':': - pass - else: - options.prefix = options.prefix[:-1] self.meson_script_launcher = script_launcher self.options = options self.original_cmd_line_args = original_cmd_line_args diff --git a/run_unittests.py b/run_unittests.py index 123bca8..6aa5b2b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -33,10 +33,11 @@ def get_soname(fname): return m.group(1) raise RuntimeError('Could not determine soname:\n\n' + raw_out) -def get_fake_options(): +def get_fake_options(prefix): import argparse opts = argparse.Namespace() opts.cross_file = None + opts.prefix = prefix return opts class FakeEnvironment(object): @@ -85,7 +86,8 @@ class LinuxlikeTests(unittest.TestCase): super().tearDown() def _run(self, command): - self.output += subprocess.check_output(command, env=os.environ.copy()) + self.output += subprocess.check_output(command, stderr=subprocess.STDOUT, + env=os.environ.copy()) def init(self, srcdir, extra_args=None): if extra_args is None: @@ -510,7 +512,7 @@ class LinuxlikeTests(unittest.TestCase): ''' testdir = os.path.join(self.common_test_dir, '1 trivial') env = Environment(testdir, self.builddir, self.meson_command, - get_fake_options(), []) + get_fake_options(self.prefix), []) cc = env.detect_c_compiler(False) self._test_stds_impl(testdir, cc, 'c') @@ -521,11 +523,10 @@ class LinuxlikeTests(unittest.TestCase): ''' testdir = os.path.join(self.common_test_dir, '2 cpp') env = Environment(testdir, self.builddir, self.meson_command, - get_fake_options(), []) + get_fake_options(self.prefix), []) cpp = env.detect_cpp_compiler(False) self._test_stds_impl(testdir, cpp, 'cpp') - def test_build_by_default(self): testdir = os.path.join(self.unit_test_dir, '5 build by default') self.init(testdir) @@ -537,6 +538,21 @@ class LinuxlikeTests(unittest.TestCase): self._run(self.ninja_command + ['fooprog']) self.assertTrue(os.path.exists(exe)) + def test_libdir_must_be_inside_prefix(self): + testdir = os.path.join(self.common_test_dir, '1 trivial') + # libdir being inside prefix is ok + args = ['--prefix', '/opt', '--libdir', '/opt/lib32'] + self.init(testdir, args) + self.wipe() + # libdir not being inside prefix is not ok + args = ['--prefix', '/usr', '--libdir', '/opt/lib32'] + self.assertRaises(subprocess.CalledProcessError, self.init, testdir, args) + self.wipe() + # libdir must be inside prefix even when set via mesonconf + self.init(testdir) + self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt') + + class RewriterTests(unittest.TestCase): def setUp(self): diff --git a/test cases/failing/39 libdir must be inside prefix/meson.build b/test cases/failing/39 libdir must be inside prefix/meson.build new file mode 100644 index 0000000..66272ea --- /dev/null +++ b/test cases/failing/39 libdir must be inside prefix/meson.build @@ -0,0 +1,2 @@ +project('libdir prefix', 'c', + default_options : ['libdir=/opt/lib']) diff --git a/test cases/failing/40 prefix absolute/meson.build b/test cases/failing/40 prefix absolute/meson.build new file mode 100644 index 0000000..e2863e7 --- /dev/null +++ b/test cases/failing/40 prefix absolute/meson.build @@ -0,0 +1,2 @@ +project('prefix-abs', 'c', + default_options : ['prefix=some/path/notabs']) |