diff options
-rw-r--r-- | docs/markdown/Builtin-options.md | 4 | ||||
-rw-r--r-- | docs/markdown/snippets/dir_options_outside_prefix.md | 12 | ||||
-rw-r--r-- | docs/yaml/functions/_build_target_base.yaml | 8 | ||||
-rw-r--r-- | docs/yaml/functions/get_option.yaml | 18 | ||||
-rw-r--r-- | mesonbuild/coredata.py | 36 | ||||
-rw-r--r-- | test cases/failing/38 libdir must be inside prefix/meson.build | 6 | ||||
-rw-r--r-- | test cases/failing/38 libdir must be inside prefix/test.json | 10 | ||||
-rw-r--r-- | unittests/allplatformstests.py | 14 |
8 files changed, 51 insertions, 57 deletions
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 613b8b8..db3c3e8 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -23,13 +23,15 @@ For legacy reasons `--warnlevel` is the cli argument for the They can also be edited after setup using `meson configure -Doption=value`. -Installation options are all relative to the prefix, except: +Installation options are usually relative to the prefix but it should +not be relied on, since they can be absolute paths in the following cases: * When the prefix is `/usr`: `sysconfdir` defaults to `/etc`, `localstatedir` defaults to `/var`, and `sharedstatedir` defaults to `/var/lib` * When the prefix is `/usr/local`: `localstatedir` defaults to `/var/local`, and `sharedstatedir` defaults to `/var/local/lib` +* When an absolute path outside of prefix is provided by the user/distributor. ### Directories diff --git a/docs/markdown/snippets/dir_options_outside_prefix.md b/docs/markdown/snippets/dir_options_outside_prefix.md new file mode 100644 index 0000000..ceb5e50 --- /dev/null +++ b/docs/markdown/snippets/dir_options_outside_prefix.md @@ -0,0 +1,12 @@ +## All directory options now support paths outside of prefix + +Previously, Meson only allowed most directory options to be relative to prefix. +This restriction has been now lifted, bringing us in line with Autotools and +CMake. It is also useful for platforms like Nix, which install projects into +multiple independent prefixes. + +As a consequence, `get_option` might return absolute paths for any +directory option, if a directory outside of prefix is passed. This +is technically a backwards incompatible change but its effect +should be minimal, thanks to widespread use of `join_paths`/ +`/` operator and pkg-config generator module. diff --git a/docs/yaml/functions/_build_target_base.yaml b/docs/yaml/functions/_build_target_base.yaml index 62424b6..68df5d8 100644 --- a/docs/yaml/functions/_build_target_base.yaml +++ b/docs/yaml/functions/_build_target_base.yaml @@ -161,10 +161,10 @@ kwargs: install_dir: type: str description: | - override install directory for this file. The value is - relative to the `prefix` specified. F.ex, if you want to install - plugins into a subdir, you'd use something like this: `install_dir : - get_option('libdir') / 'projectname-1.0'`. + override install directory for this file. If the value is a relative path, + it will be considered relative the `prefix` option. + For example, if you want to install plugins into a subdir, you'd use + something like this: `install_dir : get_option('libdir') / 'projectname-1.0'`. install_mode: type: list[str | int] diff --git a/docs/yaml/functions/get_option.yaml b/docs/yaml/functions/get_option.yaml index 0bf0042..7f7982e 100644 --- a/docs/yaml/functions/get_option.yaml +++ b/docs/yaml/functions/get_option.yaml @@ -5,16 +5,14 @@ description: | specified in the positional argument. Note that the value returned for built-in options that end in `dir` - such as `bindir` and `libdir` is always a path relative to (and - inside) the `prefix`. - - The only exceptions are: `sysconfdir`, `localstatedir`, and - `sharedstatedir` which will return the value passed during - configuration as-is, which may be absolute, or relative to `prefix`. - [`install_dir` arguments](Installing.md) handles that as expected, but - if you need the absolute path to one of these e.g. to use in a define - etc., you should use `get_option('prefix') / - get_option('localstatedir')` + such as `bindir` and `libdir` is usually a path relative to (and + inside) the `prefix` but you should not rely on that, as it can also + be an absolute path [in some cases](Builtin-options.md#Universal options). + [`install_dir` arguments](Installing.md) handle that as expected + but if you need an absolute path, e.g. to use in a define etc., + you should use the path concatenation operator like this: + `get_option('prefix') / get_option('localstatedir')`. + Never manually join paths as if they were strings. For options of type `feature` a [[@feature]] option object diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 2bb89a2..4a84467 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -549,14 +549,13 @@ class CoreData: def sanitize_dir_option_value(self, prefix: str, option: OptionKey, value: T.Any) -> T.Any: ''' - 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. + If the option is an installation directory option, the value is an + absolute path and resides within prefix, return the value + as a path relative to the prefix. Otherwise, return it as is. - This way everyone can do f.ex, get_option('libdir') and be sure to get - the library directory relative to prefix. - - .as_posix() keeps the posix-like file separators Meson uses. + This way everyone can do f.ex, get_option('libdir') and usually get + the library directory relative to prefix, even though it really + should not be relied upon. ''' try: value = PurePath(value) @@ -564,21 +563,20 @@ class CoreData: return value if option.name.endswith('dir') and value.is_absolute() and \ option not in BULITIN_DIR_NOPREFIX_OPTIONS: - # Value must be a subdir of the prefix - # commonpath will always return a path in the native format, so we - # must use pathlib.PurePath to do the same conversion before - # comparing. - msg = ('The value of the \'{!s}\' option is \'{!s}\' 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.') - # os.path.commonpath doesn't understand case-insensitive filesystems, - # but PurePath().relative_to() does. try: + # Try to relativize the path. value = value.relative_to(prefix) except ValueError: - raise MesonException(msg.format(option, value, prefix)) - if '..' in str(value): - raise MesonException(msg.format(option, value, prefix)) + # Path is not relative, let’s keep it as is. + pass + if '..' in value.parts: + raise MesonException( + f'The value of the \'{option}\' option is \'{value}\' but ' + 'directory options are not allowed to contain \'..\'.\n' + f'If you need a path outside of the {prefix!r} prefix, ' + 'please use an absolute path.' + ) + # .as_posix() keeps the posix-like file separators Meson uses. return value.as_posix() def init_builtins(self, subproject: str) -> None: diff --git a/test cases/failing/38 libdir must be inside prefix/meson.build b/test cases/failing/38 libdir must be inside prefix/meson.build deleted file mode 100644 index 4cce7f8..0000000 --- a/test cases/failing/38 libdir must be inside prefix/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -project('libdir prefix', 'c', - default_options : ['libdir=/opt/lib']) - -if host_machine.system() == 'windows' - error('MESON_SKIP_TEST: this test does not work on Windows since /foo is not absolute') -endif
\ No newline at end of file diff --git a/test cases/failing/38 libdir must be inside prefix/test.json b/test cases/failing/38 libdir must be inside prefix/test.json deleted file mode 100644 index d9256d1..0000000 --- a/test cases/failing/38 libdir must be inside prefix/test.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "do_not_set_opts": [ - "libdir" - ], - "stdout": [ - { - "line": "test cases/failing/38 libdir must be inside prefix/meson.build:1:0: ERROR: The value of the 'libdir' option is '/opt/lib' which must be a subdir of the prefix '/usr'." - } - ] -} diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 60ff123..3eea9a3 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -213,9 +213,9 @@ class AllPlatformTests(BasePlatformTests): elif opt['name'] == 'libdir': self.assertEqual(libdir, opt['value']) - def test_libdir_must_be_inside_prefix(self): + def test_libdir_can_be_outside_prefix(self): ''' - Tests that libdir is forced to be inside prefix no matter how it is set. + Tests that libdir is allowed to be outside prefix. Must be a unit test for obvious reasons. ''' testdir = os.path.join(self.common_test_dir, '1 trivial') @@ -226,19 +226,19 @@ class AllPlatformTests(BasePlatformTests): args = ['--prefix', '/opt', '--libdir', '/opt/lib32'] self.init(testdir, extra_args=args) self.wipe() - # libdir not being inside prefix is not ok + # libdir not being inside prefix is ok too if is_windows(): args = ['--prefix', 'x:/usr', '--libdir', 'x:/opt/lib32'] else: args = ['--prefix', '/usr', '--libdir', '/opt/lib32'] - self.assertRaises(subprocess.CalledProcessError, self.init, testdir, extra_args=args) + self.init(testdir, extra_args=args) self.wipe() - # libdir must be inside prefix even when set via mesonconf + # libdir can be outside prefix even when set via mesonconf self.init(testdir) if is_windows(): - self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=x:/opt', False) + self.setconf('-Dlibdir=x:/opt', will_build=False) else: - self.assertRaises(subprocess.CalledProcessError, self.setconf, '-Dlibdir=/opt', False) + self.setconf('-Dlibdir=/opt', will_build=False) def test_prefix_dependent_defaults(self): ''' |