aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Fs-module.md14
-rw-r--r--docs/markdown/snippets/fs_relative_to.md17
-rw-r--r--mesonbuild/modules/fs.py23
-rw-r--r--test cases/common/220 fs module/meson.build25
-rw-r--r--test cases/common/220 fs module/subdir/btgt.c5
-rw-r--r--test cases/common/220 fs module/subdir/meson.build50
6 files changed, 132 insertions, 2 deletions
diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md
index cc67355..bf9ddba 100644
--- a/docs/markdown/Fs-module.md
+++ b/docs/markdown/Fs-module.md
@@ -224,6 +224,20 @@ fs.stem('foo/bar/baz.dll.a') # baz.dll
project. If the file specified by `path` is a `files()` object it
cannot refer to a built file.
+### relative_to
+
+*Since 1.3.0*
+
+Return a relative filepath. In event a relative path could not be found, the
+absolute path of `to` is returned. Relative path arguments will be assumed to be
+relative to `meson.current_source_dir()`.
+
+Has the following positional arguments:
+ - to `str | file | custom_tgt | custom_idx | tgt`: end path
+ - from `str | file | custom_tgt | custom_idx | tgt`: start path
+
+returns:
+ - a string
### copyfile
diff --git a/docs/markdown/snippets/fs_relative_to.md b/docs/markdown/snippets/fs_relative_to.md
new file mode 100644
index 0000000..82e6a42
--- /dev/null
+++ b/docs/markdown/snippets/fs_relative_to.md
@@ -0,0 +1,17 @@
+## `fs.relative_to()`
+
+The `fs` module now has a `relative_to` method. The method will return the
+relative path from argument one to argument two, if one exists. Otherwise, the
+absolute path to argument one is returned.
+
+```meson
+assert(fs.relative_to('c:\\prefix\\lib', 'c:\\prefix\\bin') == '..\\lib')
+assert(fs.relative_to('c:\\proj1\\foo', 'd:\\proj1\\bar') == 'c:\\proj1\\foo')
+assert(fs.relative_to('prefix\\lib\\foo', 'prefix') == 'lib\\foo')
+
+assert(fs.relative_to('/prefix/lib', '/prefix/bin') == '../lib')
+assert(fs.relative_to('prefix/lib/foo', 'prefix') == 'lib/foo')
+```
+
+In addition to strings, it can handle files, custom targets, custom target
+indices, and build targets.
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index 53174d2..a2dbdf2 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -20,18 +20,21 @@ import typing as T
from . import ExtensionModule, ModuleReturnValue, ModuleInfo
from .. import mlog
-from ..build import CustomTarget, InvalidArguments
+from ..build import BuildTarget, CustomTarget, CustomTargetIndex, InvalidArguments
from ..interpreter.type_checking import INSTALL_KW, INSTALL_MODE_KW, INSTALL_TAG_KW, NoneType
from ..interpreterbase import FeatureNew, KwargInfo, typed_kwargs, typed_pos_args, noKwargs
+from ..interpreterbase.baseobjects import TYPE_kwargs
from ..mesonlib import (
File,
MesonException,
has_path_sep,
path_is_in_root,
)
+from ..utils.universal import relpath
if T.TYPE_CHECKING:
from . import ModuleState
+ from ..build import BuildTargetTypes
from ..interpreter import Interpreter
from ..mesonlib import FileOrString, FileMode
@@ -75,6 +78,7 @@ class FSModule(ExtensionModule):
'stem': self.stem,
'read': self.read,
'copyfile': self.copyfile,
+ 'relative_to': self.relative_to,
})
def _absolute_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
@@ -312,6 +316,23 @@ class FSModule(ExtensionModule):
return ModuleReturnValue(ct, [ct])
+ @FeatureNew('fs.relative_to', '1.3.0')
+ @typed_pos_args('fs.relative_to', (str, File, CustomTarget, CustomTargetIndex, BuildTarget), (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
+ @noKwargs
+ def relative_to(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], T.Union[FileOrString, BuildTargetTypes]], kwargs: TYPE_kwargs) -> str:
+ def to_path(arg: T.Union[FileOrString, CustomTarget, CustomTargetIndex, BuildTarget]) -> str:
+ if isinstance(arg, File):
+ return arg.absolute_path(state.environment.source_dir, state.environment.build_dir)
+ elif isinstance(arg, (CustomTarget, CustomTargetIndex, BuildTarget)):
+ return state.backend.get_target_filename_abs(arg)
+ else:
+ return os.path.join(state.environment.source_dir, state.subdir, arg)
+
+ t = to_path(args[0])
+ f = to_path(args[1])
+
+ return relpath(t, f)
+
def initialize(*args: T.Any, **kwargs: T.Any) -> FSModule:
return FSModule(*args, **kwargs)
diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build
index b860fc8..7f113d6 100644
--- a/test cases/common/220 fs module/meson.build
+++ b/test cases/common/220 fs module/meson.build
@@ -1,4 +1,4 @@
-project('fs module test')
+project('fs module test', 'c')
is_windows = build_machine.system() == 'windows'
@@ -139,6 +139,29 @@ assert(fs.name('foo/bar/baz.dll.a') == 'baz.dll.a', 'failed to get basename with
assert(fs.stem('foo/bar/baz.dll') == 'baz', 'failed to get stem with suffix')
assert(fs.stem('foo/bar/baz.dll.a') == 'baz.dll', 'failed to get stem with compound suffix')
+# relative_to
+if build_machine.system() == 'windows'
+ # strings
+ assert(fs.relative_to('c:\\prefix\\lib\\foo', 'c:\\prefix') == 'lib\\foo')
+ assert(fs.relative_to('c:\\prefix\\lib', 'c:\\prefix\\bin') == '..\\lib')
+ assert(fs.relative_to('c:\\proj1\\foo', 'd:\\proj1\\bar') == 'c:\\proj1\\foo')
+ assert(fs.relative_to('prefix\\lib\\foo', 'prefix') == 'lib\\foo')
+ assert(fs.relative_to('prefix\\lib', 'prefix\\bin') == '..\\lib')
+ assert(fs.relative_to('proj1\\foo', 'proj1\\bar') == '..\\foo')
+ assert(fs.relative_to('subdir/subdirfile.txt', meson.current_source_dir()) == 'subdir\\subdirfile.txt')
+ assert(fs.relative_to(files('meson.build'), files('subdir/meson.build')) == '..\\..\\meson.build')
+ assert(fs.relative_to(files('meson.build'), 'subdir/meson.build') == '..\\..\\meson.build')
+else
+ # strings
+ assert(fs.relative_to('/prefix/lib/foo', '/prefix') == 'lib/foo')
+ assert(fs.relative_to('/prefix/lib', '/prefix/bin') == '../lib')
+ assert(fs.relative_to('prefix/lib/foo', 'prefix') == 'lib/foo')
+ assert(fs.relative_to('prefix/lib', 'prefix/bin') == '../lib')
+ assert(fs.relative_to('subdir/subdirfile.txt', meson.current_source_dir()) == 'subdir/subdirfile.txt')
+ assert(fs.relative_to(files('meson.build'), files('subdir/meson.build')) == '../../meson.build')
+ assert(fs.relative_to(files('meson.build'), 'subdir/meson.build') == '../../meson.build')
+endif
+
subdir('subdir')
subproject('subbie')
diff --git a/test cases/common/220 fs module/subdir/btgt.c b/test cases/common/220 fs module/subdir/btgt.c
new file mode 100644
index 0000000..8479e67
--- /dev/null
+++ b/test cases/common/220 fs module/subdir/btgt.c
@@ -0,0 +1,5 @@
+int
+main(void)
+{
+ return 0;
+}
diff --git a/test cases/common/220 fs module/subdir/meson.build b/test cases/common/220 fs module/subdir/meson.build
index 0cd2475..3ea902c 100644
--- a/test cases/common/220 fs module/subdir/meson.build
+++ b/test cases/common/220 fs module/subdir/meson.build
@@ -4,3 +4,53 @@ assert(fs.is_samepath(meson.project_source_root(), '..'), 'is_samepath not detec
assert(fs.is_samepath(meson.project_build_root(), meson.current_build_dir() / '..'), 'is_samepath not detecting same directory')
assert(fs.is_samepath(subdirfiles[0], 'subdirfile.txt'), 'is_samepath not detecting same directory when using File and str')
+
+# More relative_to to test subdir/builddir components
+
+python3 = find_program('python3')
+build_to_src = fs.relative_to(meson.current_source_dir(), meson.current_build_dir())
+src_to_build = fs.relative_to(meson.current_build_dir(), meson.current_source_dir())
+
+btgt = executable('btgt', 'btgt.c')
+ctgt = custom_target(
+ 'copied-files',
+ command: [
+ python3,
+ '-c',
+ 'import shutil; shutil.copyfile("@INPUT0@", "@OUTPUT0@"); shutil.copyfile("@INPUT1@", "@OUTPUT1@")'
+ ],
+ input: [
+ 'subdirfile.txt',
+ 'meson.build',
+ ],
+ output: [
+ 'subdirfile.txt',
+ 'meson.build',
+ ],
+)
+
+if build_machine.system() == 'windows'
+ # Test that CustomTarget works
+ assert(fs.relative_to('subdirfile.txt', ctgt) == '..\\@0@\\subdirfile.txt'.format(build_to_src))
+ assert(fs.relative_to(ctgt, 'subdirfile.txt') == '..\\@0@\\subdirfile.txt'.format(src_to_build))
+ # Test that CustomTargetIndex works
+ assert(fs.relative_to('meson.build', ctgt[1]) == '..\\@0@\\meson.build'.format(build_to_src))
+ assert(fs.relative_to(ctgt[1], 'meson.build') == '..\\@0@\\meson.build'.format(src_to_build))
+ # Test that BuildTarget works
+ assert(fs.relative_to('subdirfile.txt', btgt) == '..\\@0@\\subdirfile.txt'.format(build_to_src))
+ assert(fs.relative_to(btgt, 'subdirfile.txt') == '..\\@0@\\btgt.exe'.format(src_to_build))
+else
+ # Test that CustomTarget works
+ assert(fs.relative_to('subdirfile.txt', ctgt) == '../@0@/subdirfile.txt'.format(build_to_src))
+ assert(fs.relative_to(ctgt, 'subdirfile.txt') == '../@0@/subdirfile.txt'.format(src_to_build))
+ # Test that CustomTargetIndex works
+ assert(fs.relative_to('meson.build', ctgt[1]) == '../@0@/meson.build'.format(build_to_src))
+ assert(fs.relative_to(ctgt[1], 'meson.build') == '../@0@/meson.build'.format(src_to_build))
+ # Test that BuildTarget works
+ assert(fs.relative_to('subdirfile.txt', btgt) == '../@0@/subdirfile.txt'.format(build_to_src))
+ if host_machine.system() == 'windows'
+ assert(fs.relative_to(btgt, 'subdirfile.txt') == '../@0@/btgt.exe'.format(src_to_build))
+ else
+ assert(fs.relative_to(btgt, 'subdirfile.txt') == '../@0@/btgt'.format(src_to_build))
+ endif
+endif