diff options
38 files changed, 273 insertions, 71 deletions
diff --git a/docs/markdown/Fs-module.md b/docs/markdown/Fs-module.md index 7ba4832..91c706e 100644 --- a/docs/markdown/Fs-module.md +++ b/docs/markdown/Fs-module.md @@ -206,13 +206,26 @@ fs.name('foo/bar/baz.dll.a') # baz.dll.a *since 0.54.0* Returns the last component of the path, dropping the last part of the -suffix +suffix. ```meson fs.stem('foo/bar/baz.dll') # baz fs.stem('foo/bar/baz.dll.a') # baz.dll ``` +### suffix + +*since 1.9.0* + +Returns the last dot-separated portion of the final component of the path +including the dot, if any. + +```meson +fs.suffix('foo/bar/baz.dll') # .dll +fs.suffix('foo/bar/baz.dll.a') # .a +fs.suffix('foo/bar') # (empty) +``` + ### read - `read(path, encoding: 'utf-8')` *(since 0.57.0)*: return a [string](Syntax.md#strings) with the contents of the given `path`. diff --git a/docs/markdown/snippets/fs_suffix.md b/docs/markdown/snippets/fs_suffix.md new file mode 100644 index 0000000..7059008 --- /dev/null +++ b/docs/markdown/snippets/fs_suffix.md @@ -0,0 +1,4 @@ +## Added suffix function to the FS module + +The basename and stem were already available. For completeness, expose also the +suffix. diff --git a/docs/markdown/snippets/swift-module-name.md b/docs/markdown/snippets/swift-module-name.md new file mode 100644 index 0000000..689dd84 --- /dev/null +++ b/docs/markdown/snippets/swift-module-name.md @@ -0,0 +1,9 @@ +## Explicitly setting Swift module name is now supported + +It is now possible to set the Swift module name for a target via the +*swift_module_name* target kwarg, overriding the default inferred from the +target name. + +```meson +lib = library('foo', 'foo.swift', swift_module_name: 'Foo') +``` diff --git a/docs/markdown/snippets/swift-parse-as-library.md b/docs/markdown/snippets/swift-parse-as-library.md new file mode 100644 index 0000000..5208899 --- /dev/null +++ b/docs/markdown/snippets/swift-parse-as-library.md @@ -0,0 +1,8 @@ +## Top-level statement handling in Swift libraries + +The Swift compiler normally treats modules with a single source +file (and files named main.swift) to run top-level code at program +start. This emits a main symbol which is usually undesirable in a +library target. Meson now automatically passes the *-parse-as-library* +flag to the Swift compiler in case of single-file library targets to +disable this behavior unless the source file is called main.swift. diff --git a/docs/yaml/functions/configure_file.yaml b/docs/yaml/functions/configure_file.yaml index 943ad22..2deeff4 100644 --- a/docs/yaml/functions/configure_file.yaml +++ b/docs/yaml/functions/configure_file.yaml @@ -16,6 +16,8 @@ description: | it takes any source or configured file as the `input:` and assumes that the `output:` is produced when the specified command is run. + You can install the outputted file with the `install_dir:` kwarg, see below. + *(since 0.47.0)* When the `copy:` keyword argument is set to `true`, this function will copy the file provided in `input:` to a file in the build directory with the name `output:` in the current directory. diff --git a/docs/yaml/functions/custom_target.yaml b/docs/yaml/functions/custom_target.yaml index 4920407..094787b 100644 --- a/docs/yaml/functions/custom_target.yaml +++ b/docs/yaml/functions/custom_target.yaml @@ -12,6 +12,8 @@ description: | custom_target('foo', output: 'file.txt', ...) ``` + You can install the outputted files with the `install_dir:` kwarg, see below. + *Since 0.60.0* the name argument is optional and defaults to the basename of the first output (`file.txt` in the example above). diff --git a/docs/yaml/functions/install_data.yaml b/docs/yaml/functions/install_data.yaml index fdedf7e..b9aedca 100644 --- a/docs/yaml/functions/install_data.yaml +++ b/docs/yaml/functions/install_data.yaml @@ -2,6 +2,9 @@ name: install_data returns: void description: | Installs files from the source tree that are listed as positional arguments. + Please note that this can only install static files from the source tree. + Generated files are installed via the `install_dir:` kwarg on the respective + generators, such as `custom_target()` or `configure_file(). See [Installing](Installing.md) for more examples. diff --git a/docs/yaml/functions/install_headers.yaml b/docs/yaml/functions/install_headers.yaml index da304bc..42f6462 100644 --- a/docs/yaml/functions/install_headers.yaml +++ b/docs/yaml/functions/install_headers.yaml @@ -9,6 +9,10 @@ description: | argument. As an example if this has the value `myproj` then the headers would be installed to `/{prefix}/include/myproj`. + Please note that this can only install static files from the source tree. + Generated files are installed via the `install_dir:` kwarg on the respective + generators, such as `custom_target()` or `configure_file(). + example: | For example, this will install `common.h` and `kola.h` into `/{prefix}/include`: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index ba75ce7..eebdd05 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2232,10 +2232,7 @@ class NinjaBackend(backends.Backend): def swift_module_file_name(self, target): return os.path.join(self.get_target_private_dir(target), - self.target_swift_modulename(target) + '.swiftmodule') - - def target_swift_modulename(self, target): - return target.name + target.swift_module_name + '.swiftmodule') def determine_swift_dep_modules(self, target): result = [] @@ -2262,7 +2259,7 @@ class NinjaBackend(backends.Backend): return srcs, others def generate_swift_target(self, target) -> None: - module_name = self.target_swift_modulename(target) + module_name = target.swift_module_name swiftc = target.compilers['swift'] abssrc = [] relsrc = [] @@ -2288,6 +2285,13 @@ class NinjaBackend(backends.Backend): compile_args += swiftc.get_cxx_interoperability_args(target.compilers) compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine) compile_args += self.build.get_global_args(swiftc, target.for_machine) + if isinstance(target, (build.StaticLibrary, build.SharedLibrary)): + # swiftc treats modules with a single source file, and the main.swift file in multi-source file modules + # as top-level code. This is undesirable in library targets since it emits a main function. Add the + # -parse-as-library option as necessary to prevent emitting the main function while keeping files explicitly + # named main.swift treated as the entrypoint of the module in case this is desired. + if len(abssrc) == 1 and os.path.basename(abssrc[0]) != 'main.swift': + compile_args += swiftc.get_library_args() for i in reversed(target.get_include_dirs()): basedir = i.get_curdir() for d in i.get_incdirs(): diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 72d376d..1745b9e 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -75,6 +75,7 @@ lang_arg_kwargs |= { vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'} rust_kwargs = {'rust_crate_type', 'rust_dependency_map'} cs_kwargs = {'resources', 'cs_args'} +swift_kwargs = {'swift_module_name'} buildtarget_kwargs = { 'build_by_default', @@ -110,7 +111,8 @@ known_build_target_kwargs = ( pch_kwargs | vala_kwargs | rust_kwargs | - cs_kwargs) + cs_kwargs | + swift_kwargs) known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie', 'vs_module_defs', 'android_exe_type'} known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions', 'rust_abi'} @@ -963,7 +965,7 @@ class BuildTarget(Target): self.compilers[lang] = compiler break else: - if is_known_suffix(s): + if is_known_suffix(s) and not is_header(s): path = pathlib.Path(str(s)).as_posix() m = f'No {self.for_machine.get_lower_case_name()} machine compiler for {path!r}' raise MesonException(m) @@ -1260,6 +1262,10 @@ class BuildTarget(Target): raise InvalidArguments(f'Invalid rust_dependency_map "{rust_dependency_map}": must be a dictionary with string values.') self.rust_dependency_map = rust_dependency_map + self.swift_module_name = kwargs.get('swift_module_name') + if self.swift_module_name == '': + self.swift_module_name = self.name + def _extract_pic_pie(self, kwargs: T.Dict[str, T.Any], arg: str, option: str) -> bool: # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags all_flags = self.extra_args['c'] + self.extra_args['cpp'] diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py index 47d254b..3fff7a1 100644 --- a/mesonbuild/compilers/swift.py +++ b/mesonbuild/compilers/swift.py @@ -159,6 +159,9 @@ class SwiftCompiler(Compiler): else: return ['-cxx-interoperability-mode=off'] + def get_library_args(self) -> T.List[str]: + return ['-parse-as-library'] + def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str], build_dir: str) -> T.List[str]: for idx, i in enumerate(parameter_list): diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py index d741aab..62d855d 100644 --- a/mesonbuild/interpreter/kwargs.py +++ b/mesonbuild/interpreter/kwargs.py @@ -363,6 +363,7 @@ class _BuildTarget(_BaseBuildTarget): d_module_versions: T.List[T.Union[str, int]] d_unittest: bool rust_dependency_map: T.Dict[str, str] + swift_module_name: str sources: SourcesVarargsType c_args: T.List[str] cpp_args: T.List[str] diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index fbe3e3e..a94e26b 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -633,6 +633,7 @@ _BUILD_TARGET_KWS: T.List[KwargInfo] = [ default={}, since='1.2.0', ), + KwargInfo('swift_module_name', str, default='', since='1.9.0'), KwargInfo('build_rpath', str, default='', since='0.42.0'), KwargInfo( 'gnu_symbol_visibility', diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 57fa286..e19e528 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -125,14 +125,15 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]: res[basename] = os.path.join(installdata.prefix, s.install_path, basename) return res -def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]]: - plan: T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]] = { +def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]]: + plan: T.Dict[str, T.Dict[str, T.Dict[str, T.Union[str, T.List[str], None]]]] = { 'targets': { Path(installdata.build_dir, target.fname).as_posix(): { 'destination': target.out_name, 'tag': target.tag or None, 'subproject': target.subproject or None, - 'install_rpath': target.install_rpath or None + 'install_rpath': target.install_rpath or None, + 'build_rpaths': sorted(x.decode('utf8') for x in target.rpath_dirs_to_remove), } for target in installdata.targets }, diff --git a/mesonbuild/modules/fs.py b/mesonbuild/modules/fs.py index 1fa368e..57a6b6d 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 @@ -12,7 +14,7 @@ from .. import mlog 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 ..mesonlib import File, MesonException, has_path_sep, path_is_in_root, relpath +from ..mesonlib import File, MesonException, has_path_sep, is_windows, path_is_in_root, relpath if T.TYPE_CHECKING: from . import ModuleState @@ -42,7 +44,7 @@ class FSModule(ExtensionModule): INFO = ModuleInfo('fs', '0.53.0') - def __init__(self, interpreter: 'Interpreter') -> None: + def __init__(self, interpreter: Interpreter) -> None: super().__init__(interpreter) self.methods.update({ 'as_posix': self.as_posix, @@ -62,29 +64,30 @@ class FSModule(ExtensionModule): 'replace_suffix': self.replace_suffix, 'size': self.size, 'stem': self.stem, + 'suffix': self.suffix, }) - 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 +95,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 +104,139 @@ 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: - if isinstance(args[0], File): + def is_absolute(self, state: ModuleState, args: T.Tuple[FileOrString], kwargs: T.Dict[str, T.Any]) -> bool: + path = args[0] + if isinstance(path, File): FeatureNew('fs.is_absolute with file', '0.59.0').use(state.subproject, location=state.current_node) - return PurePath(str(args[0])).is_absolute() + path = str(path) + if is_windows(): + # os.path.isabs was broken for Windows before Python 3.13, so we implement it ourselves + path = path[:3].replace(posixsep, ntsep) + return path.startswith(ntsep * 2) or path.startswith(':' + ntsep, 1) + return path.startswith(posixsep) @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] + + @noKwargs + @typed_pos_args('fs.suffix', (str, File, CustomTarget, CustomTargetIndex, BuildTarget)) + @FeatureNew('fs.suffix', '1.9.0') + def suffix(self, state: ModuleState, args: T.Tuple[T.Union[FileOrString, BuildTargetTypes]], kwargs: T.Dict[str, T.Any]) -> str: + path = self._obj_to_pathstr('fs.suffix', args[0], state) + return os.path.splitext(path)[1] @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..383b263 100644 --- a/test cases/common/220 fs module/meson.build +++ b/test cases/common/220 fs module/meson.build @@ -52,6 +52,7 @@ unixabs = '/foo' if is_windows assert(fs.is_absolute(winabs), 'is_absolute windows not detected') assert(not fs.is_absolute(unixabs), 'is_absolute unix false positive') + assert(fs.is_absolute('//foo'), 'is_absolute failed on incomplete UNC path') else assert(fs.is_absolute(unixabs), 'is_absolute unix not detected') assert(not fs.is_absolute(winabs), 'is_absolute windows false positive') @@ -84,7 +85,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 +140,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') @@ -153,8 +150,10 @@ assert(fs.name(f[1]) == 'meson.build', 'failed to get basename') assert(fs.name('foo/bar/baz.dll.a') == 'baz.dll.a', 'failed to get basename with compound suffix') if host_machine.system() in ['cygwin', 'windows'] assert(fs.name(btgt) == 'btgt.exe', 'failed to get basename of build target') + assert(fs.suffix(btgt) == '.exe', 'failed to get build target suffix') else assert(fs.name(btgt) == 'btgt', 'failed to get basename of build target') + assert(fs.suffix(btgt) == '', 'failed to get build target suffix') endif assert(fs.name(ctgt) == 'ctgt.txt', 'failed to get basename of custom target') assert(fs.name(ctgt[0]) == 'ctgt.txt', 'failed to get basename of custom target index') @@ -163,6 +162,12 @@ assert(fs.stem('foo/bar/baz.dll.a') == 'baz.dll', 'failed to get stem with compo assert(fs.stem(btgt) == 'btgt', 'failed to get stem of build target') assert(fs.stem(ctgt) == 'ctgt', 'failed to get stem of custom target') assert(fs.stem(ctgt[0]) == 'ctgt', 'failed to get stem of custom target index') +assert(fs.suffix('foo/bar/baz') == '', 'failed to get missing suffix') +assert(fs.suffix('foo/bar/baz.') == '.', 'failed to get empty suffix') +assert(fs.suffix('foo/bar/baz.dll') == '.dll', 'failed to get plain suffix') +assert(fs.suffix('foo/bar/baz.dll.a') == '.a', 'failed to get final suffix') +assert(fs.suffix(ctgt) == '.txt', 'failed to get suffix of custom target') +assert(fs.suffix(ctgt[0]) == '.txt', 'failed to get suffix of custom target index') # relative_to if build_machine.system() == 'windows' diff --git a/test cases/swift/14 single-file library/main.swift b/test cases/swift/14 single-file library/main.swift new file mode 100644 index 0000000..ccc8fb9 --- /dev/null +++ b/test cases/swift/14 single-file library/main.swift @@ -0,0 +1,3 @@ +import SingleFile + +callMe() diff --git a/test cases/swift/14 single-file library/meson.build b/test cases/swift/14 single-file library/meson.build new file mode 100644 index 0000000..8eda1d5 --- /dev/null +++ b/test cases/swift/14 single-file library/meson.build @@ -0,0 +1,4 @@ +project('single-file library', 'swift') + +lib = static_library('SingleFile', 'singlefile.swift') +executable('program', 'main.swift', link_with: [lib]) diff --git a/test cases/swift/14 single-file library/singlefile.swift b/test cases/swift/14 single-file library/singlefile.swift new file mode 100644 index 0000000..617952f --- /dev/null +++ b/test cases/swift/14 single-file library/singlefile.swift @@ -0,0 +1 @@ +public func callMe() {} diff --git a/test cases/swift/15 main in single-file library/main.swift b/test cases/swift/15 main in single-file library/main.swift new file mode 100644 index 0000000..0d95abb --- /dev/null +++ b/test cases/swift/15 main in single-file library/main.swift @@ -0,0 +1,3 @@ +import CProgram + +precondition(callMe() == 4) diff --git a/test cases/swift/15 main in single-file library/meson.build b/test cases/swift/15 main in single-file library/meson.build new file mode 100644 index 0000000..2e1202e --- /dev/null +++ b/test cases/swift/15 main in single-file library/meson.build @@ -0,0 +1,4 @@ +project('main in single-file library', 'swift', 'c') + +lib = static_library('Library', 'main.swift', include_directories: ['.']) +executable('program', 'program.c', link_with: [lib]) diff --git a/test cases/swift/15 main in single-file library/module.modulemap b/test cases/swift/15 main in single-file library/module.modulemap new file mode 100644 index 0000000..3c1817a --- /dev/null +++ b/test cases/swift/15 main in single-file library/module.modulemap @@ -0,0 +1,3 @@ +module CProgram [extern_c] { + header "program.h" +} diff --git a/test cases/swift/15 main in single-file library/program.c b/test cases/swift/15 main in single-file library/program.c new file mode 100644 index 0000000..8959dae --- /dev/null +++ b/test cases/swift/15 main in single-file library/program.c @@ -0,0 +1,5 @@ +#include "program.h" + +int callMe() { + return 4; +} diff --git a/test cases/swift/15 main in single-file library/program.h b/test cases/swift/15 main in single-file library/program.h new file mode 100644 index 0000000..5058be3 --- /dev/null +++ b/test cases/swift/15 main in single-file library/program.h @@ -0,0 +1 @@ +int callMe(void); diff --git a/test cases/swift/16 main in multi-file library/main.swift b/test cases/swift/16 main in multi-file library/main.swift new file mode 100644 index 0000000..3682e8d --- /dev/null +++ b/test cases/swift/16 main in multi-file library/main.swift @@ -0,0 +1,4 @@ +import CProgram + +precondition(callMe() == 4) +precondition(callMe2() == 6) diff --git a/test cases/swift/16 main in multi-file library/meson.build b/test cases/swift/16 main in multi-file library/meson.build new file mode 100644 index 0000000..4d287f3 --- /dev/null +++ b/test cases/swift/16 main in multi-file library/meson.build @@ -0,0 +1,4 @@ +project('main in multi-file library', 'swift', 'c') + +lib = static_library('Library', 'main.swift', 'more.swift', include_directories: ['.']) +executable('program', 'program.c', link_with: [lib]) diff --git a/test cases/swift/16 main in multi-file library/module.modulemap b/test cases/swift/16 main in multi-file library/module.modulemap new file mode 100644 index 0000000..3c1817a --- /dev/null +++ b/test cases/swift/16 main in multi-file library/module.modulemap @@ -0,0 +1,3 @@ +module CProgram [extern_c] { + header "program.h" +} diff --git a/test cases/swift/16 main in multi-file library/more.swift b/test cases/swift/16 main in multi-file library/more.swift new file mode 100644 index 0000000..716500f --- /dev/null +++ b/test cases/swift/16 main in multi-file library/more.swift @@ -0,0 +1,3 @@ +func callMe2() -> Int { + 6 +} diff --git a/test cases/swift/16 main in multi-file library/program.c b/test cases/swift/16 main in multi-file library/program.c new file mode 100644 index 0000000..8959dae --- /dev/null +++ b/test cases/swift/16 main in multi-file library/program.c @@ -0,0 +1,5 @@ +#include "program.h" + +int callMe() { + return 4; +} diff --git a/test cases/swift/16 main in multi-file library/program.h b/test cases/swift/16 main in multi-file library/program.h new file mode 100644 index 0000000..5058be3 --- /dev/null +++ b/test cases/swift/16 main in multi-file library/program.h @@ -0,0 +1 @@ +int callMe(void); diff --git a/test cases/swift/8 extra args/lib.swift b/test cases/swift/8 extra args/lib.swift new file mode 100644 index 0000000..f8167ad --- /dev/null +++ b/test cases/swift/8 extra args/lib.swift @@ -0,0 +1,3 @@ +public func callMe() { + print("test") +} diff --git a/test cases/swift/8 extra args/main.swift b/test cases/swift/8 extra args/main.swift deleted file mode 100644 index 1ff8e07..0000000 --- a/test cases/swift/8 extra args/main.swift +++ /dev/null @@ -1 +0,0 @@ -print("test") diff --git a/test cases/swift/8 extra args/meson.build b/test cases/swift/8 extra args/meson.build index ead2ff5..d243e36 100644 --- a/test cases/swift/8 extra args/meson.build +++ b/test cases/swift/8 extra args/meson.build @@ -2,8 +2,8 @@ project('extra args', 'swift') trace_fname = 'trace.json' -lib = static_library('main', - 'main.swift', +lib = static_library('lib', + 'lib.swift', swift_args: [ '-emit-loaded-module-trace', '-emit-loaded-module-trace-path', '../' + trace_fname diff --git a/test cases/vala/32 valaless vapigen/clib.c b/test cases/vala/32 valaless vapigen/clib.c new file mode 100644 index 0000000..a55ecd4 --- /dev/null +++ b/test cases/vala/32 valaless vapigen/clib.c @@ -0,0 +1,5 @@ +#include "clib.h" + +int clib_fun(void) { + return 42; +} diff --git a/test cases/vala/32 valaless vapigen/clib.h b/test cases/vala/32 valaless vapigen/clib.h new file mode 100644 index 0000000..4d855c9 --- /dev/null +++ b/test cases/vala/32 valaless vapigen/clib.h @@ -0,0 +1,3 @@ +#pragma once + +int clib_fun(void); diff --git a/test cases/vala/32 valaless vapigen/meson.build b/test cases/vala/32 valaless vapigen/meson.build new file mode 100644 index 0000000..22a99e5 --- /dev/null +++ b/test cases/vala/32 valaless vapigen/meson.build @@ -0,0 +1,34 @@ +project('valaless-vapigen', 'c') + +if host_machine.system() == 'cygwin' + error('MESON_SKIP_TEST Does not work with the Vala currently packaged in cygwin') +endif + +gnome = import('gnome') + +clib_src = [ + 'clib.c', + 'clib.h' +] + +clib_lib = shared_library('clib', clib_src) + +clib_gir = gnome.generate_gir(clib_lib, + sources: clib_src, + namespace: 'Clib', + nsversion: '0', + header: 'clib.h', + symbol_prefix: 'clib' +) + +clib_vapi = gnome.generate_vapi('clib', sources: clib_gir[0]) + +clib_dep = declare_dependency( + include_directories: include_directories('.'), + link_with: clib_lib, + sources: clib_gir, + dependencies: clib_vapi +) + + +test('clib-test', executable('clib-test', 'test_clib.c', dependencies: clib_dep)) diff --git a/test cases/vala/32 valaless vapigen/test_clib.c b/test cases/vala/32 valaless vapigen/test_clib.c new file mode 100644 index 0000000..6fd426c --- /dev/null +++ b/test cases/vala/32 valaless vapigen/test_clib.c @@ -0,0 +1,9 @@ +#include <stdlib.h> +#include <clib.h> + +int main(void) { + if (clib_fun () == 42) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py index 905ae4d..c006961 100644 --- a/unittests/allplatformstests.py +++ b/unittests/allplatformstests.py @@ -4812,120 +4812,140 @@ class AllPlatformTests(BasePlatformTests): expected = { 'targets': { get_path(f'{self.builddir}/out1-notag.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out1-notag.txt', 'install_rpath': None, 'tag': None, 'subproject': None, }, get_path(f'{self.builddir}/out2-notag.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out2-notag.txt', 'install_rpath': None, 'tag': None, 'subproject': None, }, get_path(f'{self.builddir}/libstatic.a'): { + 'build_rpaths': [], 'destination': '{libdir_static}/libstatic.a', 'install_rpath': None, 'tag': 'devel', 'subproject': None, }, get_path(f'{self.builddir}/' + exe_name('app')): { + 'build_rpaths': [], 'destination': '{bindir}/' + exe_name('app'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/' + exe_name('app-otherdir')): { + 'build_rpaths': [], 'destination': '{prefix}/otherbin/' + exe_name('app-otherdir'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/subdir/' + exe_name('app2')): { + 'build_rpaths': [], 'destination': '{bindir}/' + exe_name('app2'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/' + shared_lib_name('shared')): { + 'build_rpaths': [], 'destination': '{libdir_shared}/' + shared_lib_name('shared'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/' + shared_lib_name('both')): { + 'build_rpaths': [], 'destination': '{libdir_shared}/' + shared_lib_name('both'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/' + static_lib_name('both')): { + 'build_rpaths': [], 'destination': '{libdir_static}/' + static_lib_name('both'), 'install_rpath': None, 'tag': 'devel', 'subproject': None, }, get_path(f'{self.builddir}/' + shared_lib_name('bothcustom')): { + 'build_rpaths': [], 'destination': '{libdir_shared}/' + shared_lib_name('bothcustom'), 'install_rpath': None, 'tag': 'custom', 'subproject': None, }, get_path(f'{self.builddir}/' + static_lib_name('bothcustom')): { + 'build_rpaths': [], 'destination': '{libdir_static}/' + static_lib_name('bothcustom'), 'install_rpath': None, 'tag': 'custom', 'subproject': None, }, get_path(f'{self.builddir}/subdir/' + shared_lib_name('both2')): { + 'build_rpaths': [], 'destination': '{libdir_shared}/' + shared_lib_name('both2'), 'install_rpath': None, 'tag': 'runtime', 'subproject': None, }, get_path(f'{self.builddir}/subdir/' + static_lib_name('both2')): { + 'build_rpaths': [], 'destination': '{libdir_static}/' + static_lib_name('both2'), 'install_rpath': None, 'tag': 'devel', 'subproject': None, }, get_path(f'{self.builddir}/out1-custom.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out1-custom.txt', 'install_rpath': None, 'tag': 'custom', 'subproject': None, }, get_path(f'{self.builddir}/out2-custom.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out2-custom.txt', 'install_rpath': None, 'tag': 'custom', 'subproject': None, }, get_path(f'{self.builddir}/out3-custom.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out3-custom.txt', 'install_rpath': None, 'tag': 'custom', 'subproject': None, }, get_path(f'{self.builddir}/subdir/out1.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out1.txt', 'install_rpath': None, 'tag': None, 'subproject': None, }, get_path(f'{self.builddir}/subdir/out2.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out2.txt', 'install_rpath': None, 'tag': None, 'subproject': None, }, get_path(f'{self.builddir}/out-devel.h'): { + 'build_rpaths': [], 'destination': '{includedir}/out-devel.h', 'install_rpath': None, 'tag': 'devel', 'subproject': None, }, get_path(f'{self.builddir}/out3-notag.txt'): { + 'build_rpaths': [], 'destination': '{datadir}/out3-notag.txt', 'install_rpath': None, 'tag': None, |