aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJouke Witteveen <j.witteveen@gmail.com>2025-07-22 23:25:33 +0200
committerJussi Pakkanen <jussi.pakkanen@mailbox.org>2025-07-24 16:04:48 +0300
commit8b81e8c89660d224212d9771dc5b45a29713e16b (patch)
treedf39e66cea91b6d32ded7c940fbc8666d445cc3c
parentf34d2c3aa3047562f7ecb4174c5d513b9d6ebe74 (diff)
downloadmeson-8b81e8c89660d224212d9771dc5b45a29713e16b.zip
meson-8b81e8c89660d224212d9771dc5b45a29713e16b.tar.gz
meson-8b81e8c89660d224212d9771dc5b45a29713e16b.tar.bz2
Restructure fs module in terms of os.path
-rw-r--r--mesonbuild/modules/fs.py101
-rw-r--r--test cases/common/220 fs module/meson.build8
2 files changed, 55 insertions, 54 deletions
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index 1fa368e..3d3159d 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -2,7 +2,9 @@
# Copyright 2019 The Meson development team
from __future__ import annotations
-from pathlib import Path, PurePath, PureWindowsPath
+from ntpath import sep as ntsep
+from pathlib import Path
+from posixpath import sep as posixsep
import hashlib
import os
import typing as T
@@ -64,27 +66,27 @@ class FSModule(ExtensionModule):
'stem': self.stem,
})
- def _absolute_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
+ def _absolute_dir(self, state: ModuleState, arg: FileOrString) -> str:
"""
make an absolute path from a relative path, WITHOUT resolving symlinks
"""
if isinstance(arg, File):
- return Path(arg.absolute_path(state.source_root, state.environment.get_build_dir()))
- return Path(state.source_root) / Path(state.subdir) / Path(arg).expanduser()
+ return arg.absolute_path(state.source_root, state.environment.get_build_dir())
+ return os.path.join(state.source_root, state.subdir, os.path.expanduser(arg))
@staticmethod
- def _obj_to_path(feature_new_prefix: str, obj: T.Union[FileOrString, BuildTargetTypes], state: ModuleState) -> PurePath:
+ def _obj_to_pathstr(feature_new_prefix: str, obj: T.Union[FileOrString, BuildTargetTypes], state: ModuleState) -> str:
if isinstance(obj, str):
- return PurePath(obj)
+ return obj
if isinstance(obj, File):
FeatureNew(f'{feature_new_prefix} with file', '0.59.0').use(state.subproject, location=state.current_node)
- return PurePath(str(obj))
+ return str(obj)
FeatureNew(f'{feature_new_prefix} with build_tgt, custom_tgt, and custom_idx', '1.4.0').use(state.subproject, location=state.current_node)
- return PurePath(state.backend.get_target_filename(obj))
+ return state.backend.get_target_filename(obj)
- def _resolve_dir(self, state: 'ModuleState', arg: 'FileOrString') -> Path:
+ def _resolve_dir(self, state: ModuleState, arg: FileOrString) -> str:
"""
resolves symlinks and makes absolute a directory relative to calling meson.build,
if not already absolute
@@ -92,7 +94,7 @@ class FSModule(ExtensionModule):
path = self._absolute_dir(state, arg)
try:
# accommodate unresolvable paths e.g. symlink loops
- path = path.resolve()
+ path = os.path.realpath(path)
except Exception:
# return the best we could do
pass
@@ -101,123 +103,126 @@ class FSModule(ExtensionModule):
@noKwargs
@FeatureNew('fs.expanduser', '0.54.0')
@typed_pos_args('fs.expanduser', str)
- def expanduser(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
- return str(Path(args[0]).expanduser())
+ def expanduser(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
+ return os.path.expanduser(args[0])
@noKwargs
@FeatureNew('fs.is_absolute', '0.54.0')
@typed_pos_args('fs.is_absolute', (str, File))
- def is_absolute(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
+ def is_absolute(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
if isinstance(args[0], File):
FeatureNew('fs.is_absolute with file', '0.59.0').use(state.subproject, location=state.current_node)
- return PurePath(str(args[0])).is_absolute()
+ return os.path.isabs(str(args[0]))
@noKwargs
@FeatureNew('fs.as_posix', '0.54.0')
@typed_pos_args('fs.as_posix', str)
- def as_posix(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
+ def as_posix(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> str:
r"""
this function assumes you are passing a Windows path, even if on a Unix-like system
and so ALL '\' are turned to '/', even if you meant to escape a character
"""
- return PureWindowsPath(args[0]).as_posix()
+ return args[0].replace(ntsep, posixsep)
@noKwargs
@typed_pos_args('fs.exists', str)
- def exists(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).exists()
+ def exists(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.exists(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_symlink', (str, File))
- def is_symlink(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
+ def is_symlink(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
if isinstance(args[0], File):
FeatureNew('fs.is_symlink with file', '0.59.0').use(state.subproject, location=state.current_node)
- return self._absolute_dir(state, args[0]).is_symlink()
+ return os.path.islink(self._absolute_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_file', str)
- def is_file(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).is_file()
+ def is_file(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.isfile(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.is_dir', str)
- def is_dir(self, state: 'ModuleState', args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
- return self._resolve_dir(state, args[0]).is_dir()
+ def is_dir(self, state: ModuleState, args: T.Tuple[str], kwargs: T.Dict[str, T.Any]) -> bool:
+ return os.path.isdir(self._resolve_dir(state, args[0]))
@noKwargs
@typed_pos_args('fs.hash', (str, File), str)
- def hash(self, state: 'ModuleState', args: T.Tuple['FileOrString', str], kwargs: T.Dict[str, T.Any]) -> str:
+ def hash(self, state: ModuleState, args: T.Tuple[FileOrString, str], kwargs: T.Dict[str, T.Any]) -> str:
if isinstance(args[0], File):
FeatureNew('fs.hash with file', '0.59.0').use(state.subproject, location=state.current_node)
file = self._resolve_dir(state, args[0])
- if not file.is_file():
+ if not os.path.isfile(file):
raise MesonException(f'{file} is not a file and therefore cannot be hashed')
try:
h = hashlib.new(args[1])
except ValueError:
raise MesonException('hash algorithm {} is not available'.format(args[1]))
- mlog.debug('computing {} sum of {} size {} bytes'.format(args[1], file, file.stat().st_size))
- h.update(file.read_bytes())
+ mlog.debug('computing {} sum of {} size {} bytes'.format(args[1], file, os.stat(file).st_size))
+ with open(file, mode='rb', buffering=0) as f:
+ h.update(f.read())
return h.hexdigest()
@noKwargs
@typed_pos_args('fs.size', (str, File))
- def size(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: T.Dict[str, T.Any]) -> int:
+ def size(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> int:
if isinstance(args[0], File):
FeatureNew('fs.size with file', '0.59.0').use(state.subproject, location=state.current_node)
file = self._resolve_dir(state, args[0])
- if not file.is_file():
+ if not os.path.isfile(file):
raise MesonException(f'{file} is not a file and therefore cannot be sized')
try:
- return file.stat().st_size
+ return os.stat(file).st_size
except ValueError:
raise MesonException('{} size could not be determined'.format(args[0]))
@noKwargs
@typed_pos_args('fs.is_samepath', (str, File), (str, File))
- def is_samepath(self, state: 'ModuleState', args: T.Tuple['FileOrString', 'FileOrString'], kwargs: T.Dict[str, T.Any]) -> bool:
+ def is_samepath(self, state: ModuleState, args: T.Tuple[FileOrString, FileOrString], kwargs: T.Dict[str, T.Any]) -> bool:
if isinstance(args[0], File) or isinstance(args[1], File):
FeatureNew('fs.is_samepath with file', '0.59.0').use(state.subproject, location=state.current_node)
file1 = self._resolve_dir(state, args[0])
file2 = self._resolve_dir(state, args[1])
- if not file1.exists():
+ if not os.path.exists(file1):
return False
- if not file2.exists():
+ if not os.path.exists(file2):
return False
try:
- return file1.samefile(file2)
+ return os.path.samefile(file1, file2)
except OSError:
return False
@noKwargs
@typed_pos_args('fs.replace_suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget), str)
- def replace_suffix(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], str], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.replace_suffix', args[0], state)
- return str(path.with_suffix(args[1]))
+ def replace_suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes], str], kwargs: T.Dict[str, T.Any]) -> str:
+ if args[1] and not args[1].startswith('.'):
+ raise ValueError(f"Invalid suffix {args[1]!r}")
+ path = self._obj_to_pathstr('fs.replace_suffix', args[0], state)
+ return os.path.splitext(path)[0] + args[1]
@noKwargs
@typed_pos_args('fs.parent', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
- def parent(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.parent', args[0], state)
- return str(path.parent)
+ def parent(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.parent', args[0], state)
+ return os.path.split(path)[0] or '.'
@noKwargs
@typed_pos_args('fs.name', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
- def name(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.name', args[0], state)
- return str(path.name)
+ def name(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.name', args[0], state)
+ return os.path.basename(path)
@noKwargs
@typed_pos_args('fs.stem', (str, File, CustomTarget, CustomTargetIndex, BuildTarget))
@FeatureNew('fs.stem', '0.54.0')
- def stem(self, state: 'ModuleState', args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
- path = self._obj_to_path('fs.stem', args[0], state)
- return str(path.stem)
+ def stem(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str:
+ path = self._obj_to_pathstr('fs.name', args[0], state)
+ return os.path.splitext(os.path.basename(path))[0]
@FeatureNew('fs.read', '0.57.0')
@typed_pos_args('fs.read', (str, File))
@typed_kwargs('fs.read', KwargInfo('encoding', str, default='utf-8'))
- def read(self, state: 'ModuleState', args: T.Tuple['FileOrString'], kwargs: 'ReadKwArgs') -> str:
+ def read(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: ReadKwArgs) -> str:
"""Read a file from the source tree and return its value as a decoded
string.
diff --git a/test cases/common/220 fs module/meson.build b/test cases/common/220 fs module/meson.build
index e5397ee..34eaab7 100644
--- a/test cases/common/220 fs module/meson.build
+++ b/test cases/common/220 fs module/meson.build
@@ -84,7 +84,7 @@ assert(new == 'foo', 'replace_suffix did not only delete last suffix')
# `/` on windows is interpreted like `.drive` which in general may not be `c:/`
# the files need not exist for fs.replace_suffix()
-original = is_windows ? 'j:/foo/bar.txt' : '/foo/bar.txt'
+original = is_windows ? 'j:\\foo\\bar.txt' : '/foo/bar.txt'
new_check = is_windows ? 'j:\\foo\\bar.ini' : '/foo/bar.ini'
new = fs.replace_suffix(original, '.ini')
@@ -139,11 +139,7 @@ endif
# parts of path
assert(fs.parent('foo/bar') == 'foo', 'failed to get dirname')
-if not is_windows
-assert(fs.parent(f[1]) == 'subdir/..', 'failed to get dirname')
-else
-assert(fs.parent(f[1]) == 'subdir\..', 'failed to get dirname')
-endif
+assert(fs.parent(f[1]) == 'subdir/..', 'failed to get dirname for file')
assert(fs.parent(btgt) == '.', 'failed to get dirname for build target')
assert(fs.parent(ctgt) == '.', 'failed to get dirname for custom target')
assert(fs.parent(ctgt[0]) == '.', 'failed to get dirname for custom target index')