From 23f3cec9d029b5d44fa7f6a109ab9cda948b12ba Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Mon, 16 Jan 2017 16:08:00 +0530 Subject: Force installation dir options to be inside prefix With the exception of things like sysconfdir (/etc), every other installation directory option must be inside the prefix. Also move the prefix checks to coredata.py since prefix can also be set from inside project() with default_options and via mesonconf. Earlier you could set prefix to a relative path that way. This also allows us to return consistent values for get_option('xxxdir') regardless of whether relative paths are passed or absolute paths are passed while setting options on the command-line, via mesonconf, or via default_options in project(). Now the returned path will *always* be relative to the prefix. Includes a unit test for this, and a failing test. Closes #1299 --- mesonbuild/coredata.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) (limited to 'mesonbuild/coredata.py') 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, -- cgit v1.1