aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/modules/fs.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/modules/fs.py')
-rw-r--r--mesonbuild/modules/fs.py65
1 files changed, 64 insertions, 1 deletions
diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py
index 2ff256b..44986f8 100644
--- a/mesonbuild/modules/fs.py
+++ b/mesonbuild/modules/fs.py
@@ -14,18 +14,25 @@
import typing as T
import hashlib
+import os
from pathlib import Path, PurePath, PureWindowsPath
from .. import mlog
from . import ExtensionModule
from . import ModuleReturnValue
-from ..mesonlib import MesonException
+from ..mesonlib import (
+ File,
+ FileOrString,
+ MesonException,
+ path_is_in_root,
+)
from ..interpreterbase import FeatureNew
from ..interpreterbase import stringArgs, noKwargs
if T.TYPE_CHECKING:
from ..interpreter import Interpreter, ModuleState
+
class FSModule(ExtensionModule):
def __init__(self, interpreter: 'Interpreter') -> None:
@@ -193,5 +200,61 @@ class FSModule(ExtensionModule):
new = original.stem
return ModuleReturnValue(str(new), [])
+ #@permittedKwargs({'encoding'})
+ @FeatureNew('fs.read', '0.57.0')
+ def read(
+ self,
+ state: 'ModuleState',
+ args: T.Sequence['FileOrString'],
+ kwargs: T.Dict[str, T.Any]
+ ) -> ModuleReturnValue:
+ '''
+ Read a file from the source tree and return its value as a decoded
+ string. If the encoding is not specified, the file is assumed to be
+ utf-8 encoded. Paths must be relative by default (to prevent accidents)
+ and are forbidden to be read from the build directory (to prevent build
+ loops)
+ '''
+ if len(args) != 1:
+ raise MesonException('expected single positional <path> arg')
+
+ path = args[0]
+ if not isinstance(path, (str, File)):
+ raise MesonException(
+ '<path> positional argument must be string or files() object')
+
+ encoding = kwargs.get('encoding', 'utf-8')
+ if not isinstance(encoding, str):
+ raise MesonException('`encoding` parameter must be a string')
+
+ src_dir = self.interpreter.environment.source_dir
+ sub_dir = self.interpreter.subdir
+ build_dir = self.interpreter.environment.get_build_dir()
+
+ if isinstance(path, File):
+ if path.is_built:
+ raise MesonException(
+ 'fs.read_file does not accept built files() objects')
+ path = os.path.join(src_dir, path.relative_name())
+ else:
+ if sub_dir:
+ src_dir = os.path.join(src_dir, sub_dir)
+ path = os.path.join(src_dir, path)
+
+ path = os.path.abspath(path)
+ if path_is_in_root(Path(path), Path(build_dir), resolve=True):
+ raise MesonException('path must not be in the build tree')
+ try:
+ with open(path, 'r', encoding=encoding) as f:
+ data = f.read()
+ except UnicodeDecodeError:
+ raise MesonException(f'decoding failed for {path}')
+ # Reconfigure when this file changes as it can contain data used by any
+ # part of the build configuration (e.g. `project(..., version:
+ # fs.read_file('VERSION')` or `configure_file(...)`
+ self.interpreter.add_build_def_file(path)
+ return ModuleReturnValue(data, [])
+
+
def initialize(*args: T.Any, **kwargs: T.Any) -> FSModule:
return FSModule(*args, **kwargs)