diff options
Diffstat (limited to 'mesonbuild/modules')
-rw-r--r-- | mesonbuild/modules/__init__.py | 20 | ||||
-rw-r--r-- | mesonbuild/modules/cmake.py | 5 | ||||
-rw-r--r-- | mesonbuild/modules/fs.py | 121 | ||||
-rw-r--r-- | mesonbuild/modules/gnome.py | 162 | ||||
-rw-r--r-- | mesonbuild/modules/hotdoc.py | 7 | ||||
-rw-r--r-- | mesonbuild/modules/pkgconfig.py | 32 | ||||
-rw-r--r-- | mesonbuild/modules/python.py | 28 | ||||
-rw-r--r-- | mesonbuild/modules/rust.py | 8 |
8 files changed, 262 insertions, 121 deletions
diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 67d1666..3938101 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -75,26 +75,28 @@ class ModuleState: required: bool = True, version_func: T.Optional[ProgramVersionFunc] = None, wanted: T.Union[str, T.List[str]] = '', silent: bool = False, - for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.Executable, OverrideProgram]: + for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[ExternalProgram, build.OverrideExecutable, OverrideProgram]: if not isinstance(prog, list): prog = [prog] return self._interpreter.find_program_impl(prog, required=required, version_func=version_func, wanted=wanted, silent=silent, for_machine=for_machine) def find_tool(self, name: str, depname: str, varname: str, required: bool = True, - wanted: T.Optional[str] = None) -> T.Union['build.Executable', ExternalProgram, 'OverrideProgram']: - # Look in overrides in case it's built as subproject - progobj = self._interpreter.program_from_overrides([name], []) - if progobj is not None: - return progobj + wanted: T.Optional[str] = None, for_machine: MachineChoice = MachineChoice.HOST) -> T.Union[build.OverrideExecutable, ExternalProgram, 'OverrideProgram']: + if for_machine is MachineChoice.HOST: + # Look in overrides in case it's built as subproject + progobj = self._interpreter.program_from_overrides([name], []) + if progobj is not None: + return progobj # Look in machine file - prog_list = self.environment.lookup_binary_entry(MachineChoice.HOST, name) + prog_list = self.environment.lookup_binary_entry(for_machine, name) if prog_list is not None: return ExternalProgram.from_entry(name, prog_list) # Check if pkgconfig has a variable - dep = self.dependency(depname, native=True, required=False, wanted=wanted) + dep = self.dependency(depname, native=for_machine is MachineChoice.BUILD, + required=False, wanted=wanted) if dep.found() and dep.type_name == 'pkgconfig': value = dep.get_variable(pkgconfig=varname) if value: @@ -106,7 +108,7 @@ class ModuleState: return progobj # Normal program lookup - return self.find_program(name, required=required, wanted=wanted) + return self.find_program(name, required=required, wanted=wanted, for_machine=for_machine) def dependency(self, depname: str, native: bool = False, required: bool = True, wanted: T.Optional[str] = None) -> 'Dependency': diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py index e3154b0..f12cc51 100644 --- a/mesonbuild/modules/cmake.py +++ b/mesonbuild/modules/cmake.py @@ -154,10 +154,11 @@ class CMakeSubproject(ModuleObject): @noKwargs @typed_pos_args('cmake.subproject.include_directories', str) - def include_directories(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> build.IncludeDirs: + def include_directories(self, state: ModuleState, args: T.Tuple[str], kwargs: TYPE_kwargs) -> T.List[build.IncludeDirs]: info = self._args_to_info(args[0]) inc = self.get_variable(state, [info['inc']], kwargs) - assert isinstance(inc, build.IncludeDirs), 'for mypy' + assert isinstance(inc, list), 'for mypy' + assert isinstance(inc[0], build.IncludeDirs), 'for mypy' return inc @noKwargs 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/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6764133..53919bc 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -22,7 +22,7 @@ from .. import build from .. import interpreter from .. import mesonlib from .. import mlog -from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments +from ..build import CustomTarget, CustomTargetIndex, Executable, GeneratedList, InvalidArguments, OverrideExecutable from ..dependencies import Dependency, InternalDependency from ..dependencies.pkgconfig import PkgConfigDependency, PkgConfigInterface from ..interpreter.type_checking import DEPENDS_KW, DEPEND_FILES_KW, ENV_KW, INSTALL_DIR_KW, INSTALL_KW, NoneType, DEPENDENCY_SOURCES_KW, in_set_validator @@ -33,11 +33,11 @@ from ..mesonlib import ( MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, quote_arg ) from ..options import OptionKey -from ..programs import OverrideProgram +from ..programs import ExternalProgram, OverrideProgram from ..scripts.gettext import read_linguas if T.TYPE_CHECKING: - from typing_extensions import Literal, TypedDict + from typing_extensions import Literal, TypeAlias, TypedDict from . import ModuleState from ..build import BuildTarget @@ -45,7 +45,6 @@ if T.TYPE_CHECKING: from ..interpreter import Interpreter from ..interpreterbase import TYPE_var, TYPE_kwargs from ..mesonlib import FileOrString - from ..programs import ExternalProgram class PostInstall(TypedDict): glib_compile_schemas: bool @@ -137,6 +136,8 @@ if T.TYPE_CHECKING: install_header: bool install_dir: T.Optional[str] docbook: T.Optional[str] + rst: T.Optional[str] + markdown: T.Optional[str] autocleanup: Literal['all', 'none', 'objects', 'default'] class GenMarshal(TypedDict): @@ -196,7 +197,7 @@ if T.TYPE_CHECKING: vtail: T.Optional[str] depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] - ToolType = T.Union[Executable, ExternalProgram, OverrideProgram] + ToolType: TypeAlias = T.Union[OverrideExecutable, ExternalProgram, OverrideProgram] # Differs from the CustomTarget version in that it straight defaults to True @@ -253,9 +254,8 @@ class GnomeModule(ExtensionModule): def __init__(self, interpreter: 'Interpreter') -> None: super().__init__(interpreter) - self.gir_dep: T.Optional[Dependency] = None - self.giscanner: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None - self.gicompiler: T.Optional[T.Union[ExternalProgram, Executable, OverrideProgram]] = None + self.giscanner: T.Optional[ToolType] = None + self.gicompiler: T.Optional[ToolType] = None self.install_glib_compile_schemas = False self.install_gio_querymodules: T.List[str] = [] self.install_gtk_update_icon_cache = False @@ -307,7 +307,7 @@ class GnomeModule(ExtensionModule): once=True, fatal=False) @staticmethod - def _find_tool(state: 'ModuleState', tool: str) -> 'ToolType': + def _find_tool(state: 'ModuleState', tool: str, for_machine: MachineChoice = MachineChoice.HOST) -> 'ToolType': tool_map = { 'gio-querymodules': 'gio-2.0', 'glib-compile-schemas': 'gio-2.0', @@ -320,7 +320,7 @@ class GnomeModule(ExtensionModule): } depname = tool_map[tool] varname = tool.replace('-', '_') - return state.find_tool(tool, depname, varname) + return state.find_tool(tool, depname, varname, for_machine=for_machine) @typed_kwargs( 'gnome.post_install', @@ -634,7 +634,7 @@ class GnomeModule(ExtensionModule): # https://github.com/mesonbuild/meson/issues/1911 # However, g-ir-scanner does not understand -Wl,-rpath # so we need to use -L instead - for d in state.backend.determine_rpath_dirs(lib): + for d in lib.determine_rpath_dirs(): d = os.path.join(state.environment.get_build_dir(), d) link_command.append('-L' + d) if include_rpath: @@ -773,9 +773,7 @@ class GnomeModule(ExtensionModule): STATIC_BUILD_REQUIRED_VERSION = ">=1.58.1" if isinstance(girtarget, (build.StaticLibrary)) and \ - not mesonlib.version_compare( - self._get_gir_dep(state)[0].get_version(), - STATIC_BUILD_REQUIRED_VERSION): + not self._giscanner_version_compare(state, STATIC_BUILD_REQUIRED_VERSION): raise MesonException('Static libraries can only be introspected with GObject-Introspection ' + STATIC_BUILD_REQUIRED_VERSION) return girtarget @@ -789,18 +787,26 @@ class GnomeModule(ExtensionModule): if self.devenv is not None: b.devenv.append(self.devenv) - def _get_gir_dep(self, state: 'ModuleState') -> T.Tuple[Dependency, T.Union[Executable, 'ExternalProgram', 'OverrideProgram'], - T.Union[Executable, 'ExternalProgram', 'OverrideProgram']]: - if not self.gir_dep: - self.gir_dep = state.dependency('gobject-introspection-1.0') - self.giscanner = self._find_tool(state, 'g-ir-scanner') - self.gicompiler = self._find_tool(state, 'g-ir-compiler') - return self.gir_dep, self.giscanner, self.gicompiler + def _get_gi(self, state: 'ModuleState') -> T.Tuple[ToolType, ToolType]: + if not self.giscanner: + self.giscanner = self._find_tool(state, 'g-ir-scanner', for_machine=MachineChoice.BUILD) + self.gicompiler = self._find_tool(state, 'g-ir-compiler', for_machine=MachineChoice.HOST) + return self.giscanner, self.gicompiler + + def _giscanner_version_compare(self, state: 'ModuleState', cmp: str) -> bool: + # Support for --version was introduced in g-i 1.58, but Ubuntu + # Bionic shipped 1.56.1. As all our version checks are greater + # than 1.58, we can just return False if get_version fails. + try: + giscanner, _ = self._get_gi(state) + return mesonlib.version_compare(giscanner.get_version(), cmp) + except MesonException: + return False @functools.lru_cache(maxsize=None) def _gir_has_option(self, option: str) -> bool: exe = self.giscanner - if isinstance(exe, OverrideProgram): + if isinstance(exe, (Executable, OverrideProgram)): # Handle overridden g-ir-scanner assert option in {'--extra-library', '--sources-top-dirs'} return True @@ -865,7 +871,7 @@ class GnomeModule(ExtensionModule): # https://github.com/mesonbuild/meson/issues/1911 # However, g-ir-scanner does not understand -Wl,-rpath # so we need to use -L instead - for d in state.backend.determine_rpath_dirs(girtarget): + for d in girtarget.determine_rpath_dirs(): d = os.path.join(state.environment.get_build_dir(), d) ret.append('-L' + d) @@ -957,8 +963,8 @@ class GnomeModule(ExtensionModule): return gir_filelist_filename - @staticmethod def _make_gir_target( + self, state: 'ModuleState', girfile: str, scan_command: T.Sequence[T.Union['FileOrString', Executable, ExternalProgram, OverrideProgram]], @@ -988,6 +994,11 @@ class GnomeModule(ExtensionModule): run_env.set('CFLAGS', [quote_arg(x) for x in env_flags], ' ') run_env.merge(kwargs['env']) + giscanner, _ = self._get_gi(state) + + # response file supported? + rspable = self._giscanner_version_compare(state, '>= 1.85.0') + return GirTarget( girfile, state.subdir, @@ -1002,6 +1013,7 @@ class GnomeModule(ExtensionModule): install_dir=[install_dir], install_tag=['devel'], env=run_env, + rspable=rspable, ) @staticmethod @@ -1137,7 +1149,7 @@ class GnomeModule(ExtensionModule): if len(girtargets) > 1 and any(isinstance(el, Executable) for el in girtargets): raise MesonException('generate_gir only accepts a single argument when one of the arguments is an executable') - gir_dep, giscanner, gicompiler = self._get_gir_dep(state) + giscanner, gicompiler = self._get_gi(state) ns = kwargs['namespace'] nsversion = kwargs['nsversion'] @@ -1148,14 +1160,13 @@ class GnomeModule(ExtensionModule): builddir = os.path.join(state.environment.get_build_dir(), state.subdir) depends: T.List[T.Union['FileOrString', 'build.GeneratedTypes', build.BuildTarget, build.StructuredSources]] = [] - depends.extend(gir_dep.sources) depends.extend(girtargets) langs_compilers = self._get_girtargets_langs_compilers(girtargets) cflags, internal_ldflags, external_ldflags = self._get_langs_compilers_flags(state, langs_compilers) deps = self._get_gir_targets_deps(girtargets) deps += kwargs['dependencies'] - deps += [gir_dep] + deps += [state.dependency('glib-2.0'), state.dependency('gobject-2.0'), state.dependency('gmodule-2.0'), state.dependency('gio-2.0')] typelib_includes, depends = self._gather_typelib_includes_and_update_depends(state, deps, depends) # ldflags will be misinterpreted by gir scanner (showing # spurious dependencies) but building GStreamer fails if they @@ -1168,13 +1179,13 @@ class GnomeModule(ExtensionModule): scan_cflags += list(self._get_scanner_cflags(self._get_external_args_for_langs(state, [lc[0] for lc in langs_compilers]))) scan_internal_ldflags = [] scan_external_ldflags = [] - scan_env_ldflags = [] + scan_env_ldflags = state.environment.coredata.get_external_link_args(MachineChoice.HOST, 'c') for cli_flags, env_flags in (self._get_scanner_ldflags(internal_ldflags), self._get_scanner_ldflags(dep_internal_ldflags)): scan_internal_ldflags += cli_flags - scan_env_ldflags = env_flags + scan_env_ldflags += env_flags for cli_flags, env_flags in (self._get_scanner_ldflags(external_ldflags), self._get_scanner_ldflags(dep_external_ldflags)): scan_external_ldflags += cli_flags - scan_env_ldflags = env_flags + scan_env_ldflags += env_flags girtargets_inc_dirs = self._get_gir_targets_inc_dirs(girtargets) inc_dirs = kwargs['include_directories'] @@ -1182,6 +1193,32 @@ class GnomeModule(ExtensionModule): scan_command: T.List[T.Union[str, Executable, 'ExternalProgram', 'OverrideProgram']] = [giscanner] scan_command += ['--quiet'] + + if state.environment.is_cross_build() and state.environment.need_exe_wrapper(): + if not state.environment.has_exe_wrapper(): + mlog.error('generate_gir requires exe_wrapper') + + binary_wrapper = state.environment.get_exe_wrapper().get_command() + ldd = state.environment.lookup_binary_entry(MachineChoice.HOST, 'ldd') + if ldd is None: + ldd_wrapper = ['ldd'] + else: + ldd_wrapper = ExternalProgram.from_bin_list(state.environment, MachineChoice.HOST, 'ldd').get_command() + + WRAPPER_ARGS_REQUIRED_VERSION = ">=1.85.0" + if not self._giscanner_version_compare(state, WRAPPER_ARGS_REQUIRED_VERSION): + msg = ('Use of gnome.generate_gir during cross compilation requires' + f'g-ir-scanner {WRAPPER_ARGS_REQUIRED_VERSION}') + raise MesonException(msg) + else: + scan_command += ['--use-binary-wrapper', binary_wrapper[0]] + if len(binary_wrapper) > 1: + scan_command += ['--binary-wrapper-args-begin', *binary_wrapper[1:], '--binary-wrapper-args-end'] + + scan_command += ['--use-ldd-wrapper', ldd_wrapper[0]] + if len(ldd_wrapper) > 1: + scan_command += ['--ldd-wrapper-args-begin', *ldd_wrapper[1:], '--ldd-wrapper-args-end'] + scan_command += ['--no-libtool'] scan_command += ['--namespace=' + ns, '--nsversion=' + nsversion] scan_command += ['--warn-all'] @@ -1619,6 +1656,8 @@ class GnomeModule(ExtensionModule): ), KwargInfo('install_header', bool, default=False, since='0.46.0'), KwargInfo('docbook', (str, NoneType)), + KwargInfo('rst', (str, NoneType), since='1.9.0'), + KwargInfo('markdown', (str, NoneType), since='1.9.0'), KwargInfo( 'autocleanup', str, default='default', since='0.47.0', validator=in_set_validator({'all', 'none', 'objects'})), @@ -1675,6 +1714,26 @@ class GnomeModule(ExtensionModule): cmd += ['--generate-docbook', docbook] + if kwargs['rst'] is not None: + if not mesonlib.version_compare(glib_version, '>= 2.71.1'): + mlog.error(f'Glib version ({glib_version}) is too old to ' + 'support the \'rst\' kwarg, need 2.71.1 or ' + 'newer') + + rst = kwargs['rst'] + + cmd += ['--generate-rst', rst] + + if kwargs['markdown'] is not None: + if not mesonlib.version_compare(glib_version, '>= 2.75.2'): + mlog.error(f'Glib version ({glib_version}) is too old to ' + 'support the \'markdown\' kwarg, need 2.75.2 ' + 'or newer') + + markdown = kwargs['markdown'] + + cmd += ['--generate-md', markdown] + # https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a if mesonlib.version_compare(glib_version, '>= 2.51.3'): cmd += ['--output-directory', '@OUTDIR@', '--generate-c-code', namebase, '@INPUT@'] @@ -1750,6 +1809,48 @@ class GnomeModule(ExtensionModule): ) targets.append(docbook_custom_target) + if kwargs['rst'] is not None: + rst = kwargs['rst'] + # The rst output is always ${rst}-${name_of_xml_file} + output = namebase + '-rst' + outputs = [] + for f in xml_files: + outputs.append('{}-{}'.format(rst, os.path.basename(str(f)))) + + rst_custom_target = CustomTarget( + output, + state.subdir, + state.subproject, + state.environment, + cmd + ['--output-directory', '@OUTDIR@', '--generate-rst', rst, '@INPUT@'], + xml_files, + outputs, + build_by_default=build_by_default, + description='Generating gdbus reStructuredText {}', + ) + targets.append(rst_custom_target) + + if kwargs['markdown'] is not None: + markdown = kwargs['markdown'] + # The markdown output is always ${markdown}-${name_of_xml_file} + output = namebase + '-markdown' + outputs = [] + for f in xml_files: + outputs.append('{}-{}'.format(markdown, os.path.basename(str(f)))) + + markdown_custom_target = CustomTarget( + output, + state.subdir, + state.subproject, + state.environment, + cmd + ['--output-directory', '@OUTDIR@', '--generate-md', markdown, '@INPUT@'], + xml_files, + outputs, + build_by_default=build_by_default, + description='Generating gdbus markdown {}', + ) + targets.append(markdown_custom_target) + return ModuleReturnValue(targets, targets) @typed_pos_args('gnome.mkenums', str) @@ -1991,6 +2092,7 @@ class GnomeModule(ExtensionModule): extra_depends=depends, # https://github.com/mesonbuild/meson/issues/973 absolute_paths=True, + rspable=mesonlib.is_windows() or mesonlib.is_cygwin(), description='Generating GObject enum file {}', ) diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 5099b41..b66a0bf 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -14,7 +14,7 @@ from ..build import CustomTarget, CustomTargetIndex from ..dependencies import Dependency, InternalDependency from ..interpreterbase import ( InvalidArguments, noPosargs, noKwargs, typed_kwargs, FeatureDeprecated, - ContainerTypeInfo, KwargInfo, typed_pos_args + ContainerTypeInfo, KwargInfo, typed_pos_args, InterpreterObject ) from ..interpreter.interpreterobjects import _CustomTargetHolder from ..interpreter.type_checking import NoneType @@ -383,12 +383,9 @@ class HotdocTargetBuilder: class HotdocTargetHolder(_CustomTargetHolder['HotdocTarget']): - def __init__(self, target: HotdocTarget, interp: Interpreter): - super().__init__(target, interp) - self.methods.update({'config_path': self.config_path_method}) - @noPosargs @noKwargs + @InterpreterObject.method('config_path') def config_path_method(self, *args: T.Any, **kwargs: T.Any) -> str: conf = self.held_object.hotdoc_conf.absolute_path(self.interpreter.environment.source_dir, self.interpreter.environment.build_dir) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index cc0450a..bef14e9 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -38,6 +38,7 @@ if T.TYPE_CHECKING: filebase: T.Optional[str] description: T.Optional[str] url: str + license: str subdirs: T.List[str] conflicts: T.List[str] dataonly: bool @@ -155,6 +156,14 @@ class DependenciesHelper: pass elif isinstance(obj, dependencies.ExternalDependency) and obj.name == 'threads': pass + elif isinstance(obj, dependencies.InternalDependency) and all(lib.get_id() in self.metadata for lib in obj.libraries): + # Ensure BothLibraries are resolved: + if self.pub_libs and isinstance(self.pub_libs[0], build.StaticLibrary): + obj = obj.get_as_static(recursive=True) + else: + obj = obj.get_as_shared(recursive=True) + for lib in obj.libraries: + processed_reqs.append(self.metadata[lib.get_id()].filebase) else: raise mesonlib.MesonException('requires argument not a string, ' 'library with pkgconfig-generated file ' @@ -441,6 +450,7 @@ class PkgConfigModule(NewExtensionModule): def _generate_pkgconfig_file(self, state: ModuleState, deps: DependenciesHelper, subdirs: T.List[str], name: str, description: str, url: str, version: str, + license: str, pcfile: str, conflicts: T.List[str], variables: T.List[T.Tuple[str, str]], unescaped_variables: T.List[T.Tuple[str, str]], @@ -519,18 +529,20 @@ class PkgConfigModule(NewExtensionModule): ofile.write(f'{k}={v}\n') ofile.write('\n') ofile.write(f'Name: {name}\n') - if len(description) > 0: + if description: ofile.write(f'Description: {description}\n') - if len(url) > 0: + if url: ofile.write(f'URL: {url}\n') + if license: + ofile.write(f'License: {license}\n') ofile.write(f'Version: {version}\n') reqs_str = deps.format_reqs(deps.pub_reqs) - if len(reqs_str) > 0: + if reqs_str: ofile.write(f'Requires: {reqs_str}\n') reqs_str = deps.format_reqs(deps.priv_reqs) - if len(reqs_str) > 0: + if reqs_str: ofile.write(f'Requires.private: {reqs_str}\n') - if len(conflicts) > 0: + if conflicts: ofile.write('Conflicts: {}\n'.format(' '.join(conflicts))) def generate_libs_flags(libs: T.List[LIBS]) -> T.Iterable[str]: @@ -571,9 +583,9 @@ class PkgConfigModule(NewExtensionModule): if isinstance(l, (build.CustomTarget, build.CustomTargetIndex)) or 'cs' not in l.compilers: yield f'-l{lname}' - if len(deps.pub_libs) > 0: + if deps.pub_libs: ofile.write('Libs: {}\n'.format(' '.join(generate_libs_flags(deps.pub_libs)))) - if len(deps.priv_libs) > 0: + if deps.priv_libs: ofile.write('Libs.private: {}\n'.format(' '.join(generate_libs_flags(deps.priv_libs)))) cflags: T.List[str] = [] @@ -605,6 +617,7 @@ class PkgConfigModule(NewExtensionModule): KwargInfo('name', (str, NoneType), validator=lambda x: 'must not be an empty string' if x == '' else None), KwargInfo('subdirs', ContainerTypeInfo(list, str), default=[], listify=True), KwargInfo('url', str, default=''), + KwargInfo('license', str, default='', since='1.9.0'), KwargInfo('version', (str, NoneType)), VARIABLES_KW.evolve(name="unescaped_uninstalled_variables", since='0.59.0'), VARIABLES_KW.evolve(name="unescaped_variables", since='0.59.0'), @@ -659,6 +672,7 @@ class PkgConfigModule(NewExtensionModule): filebase = kwargs['filebase'] if kwargs['filebase'] is not None else name description = kwargs['description'] if kwargs['description'] is not None else default_description url = kwargs['url'] + license = kwargs['license'] conflicts = kwargs['conflicts'] # Prepend the main library to public libraries list. This is required @@ -713,7 +727,7 @@ class PkgConfigModule(NewExtensionModule): pkgroot_name = os.path.join('{libdir}', 'pkgconfig') relocatable = state.get_option('pkgconfig.relocatable') self._generate_pkgconfig_file(state, deps, subdirs, name, description, url, - version, pcfile, conflicts, variables, + version, license, pcfile, conflicts, variables, unescaped_variables, False, dataonly, pkgroot=pkgroot if relocatable else None) res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, pkgroot_name, None, state.subproject, install_tag='devel') @@ -722,7 +736,7 @@ class PkgConfigModule(NewExtensionModule): pcfile = filebase + '-uninstalled.pc' self._generate_pkgconfig_file(state, deps, subdirs, name, description, url, - version, pcfile, conflicts, variables, + version, license, pcfile, conflicts, variables, unescaped_variables, uninstalled=True, dataonly=dataonly) # Associate the main library with this generated pc file. If the library # is used in any subsequent call to the generated, it will generate a diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py index 2a7e685..3c07960 100644 --- a/mesonbuild/modules/python.py +++ b/mesonbuild/modules/python.py @@ -16,11 +16,11 @@ from ..dependencies.detect import get_dep_identifier, find_external_dependency from ..dependencies.python import BasicPythonExternalProgram, python_factory, _PythonDependencyBase from ..interpreter import extract_required_kwarg, permitted_dependency_kwargs, primitives as P_OBJ from ..interpreter.interpreterobjects import _ExternalProgramHolder -from ..interpreter.type_checking import NoneType, PRESERVE_PATH_KW, SHARED_MOD_KWS +from ..interpreter.type_checking import NoneType, DEPENDENCY_KWS, PRESERVE_PATH_KW, SHARED_MOD_KWS from ..interpreterbase import ( noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, InvalidArguments, typed_pos_args, typed_kwargs, KwargInfo, - FeatureNew, FeatureNewKwargs, disablerIfNotFound + FeatureNew, FeatureNewKwargs, disablerIfNotFound, InterpreterObject ) from ..mesonlib import MachineChoice from ..options import OptionKey @@ -126,23 +126,11 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): self.platform = info['platform'] self.is_pypy = info['is_pypy'] self.link_libpython = info['link_libpython'] - self.methods.update({ - 'extension_module': self.extension_module_method, - 'dependency': self.dependency_method, - 'install_sources': self.install_sources_method, - 'get_install_dir': self.get_install_dir_method, - 'language_version': self.language_version_method, - 'found': self.found_method, - 'has_path': self.has_path_method, - 'get_path': self.get_path_method, - 'has_variable': self.has_variable_method, - 'get_variable': self.get_variable_method, - 'path': self.path_method, - }) @permittedKwargs(mod_kwargs) @typed_pos_args('python.extension_module', str, varargs=(str, mesonlib.File, CustomTarget, CustomTargetIndex, GeneratedList, StructuredSources, ExtractedObjects, BuildTarget)) @typed_kwargs('python.extension_module', *_MOD_KWARGS, _DEFAULTABLE_SUBDIR_KW, _LIMITED_API_KW, allow_unknown=True) + @InterpreterObject.method('extension_module') def extension_module_method(self, args: T.Tuple[str, T.List[BuildTargetSource]], kwargs: ExtensionModuleKw) -> 'SharedModule': if 'install_dir' in kwargs: if kwargs['subdir'] is not None: @@ -268,6 +256,8 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): @permittedKwargs(permitted_dependency_kwargs | {'embed'}) @FeatureNewKwargs('python_installation.dependency', '0.53.0', ['embed']) @noPosargs + @typed_kwargs('python_installation.dependency', *DEPENDENCY_KWS, allow_unknown=True) + @InterpreterObject.method('dependency') def dependency_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'Dependency': disabled, required, feature = extract_required_kwarg(kwargs, self.subproject) if disabled: @@ -287,6 +277,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): PRESERVE_PATH_KW, KwargInfo('install_tag', (str, NoneType), since='0.60.0') ) + @InterpreterObject.method('install_sources') def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]], kwargs: 'PyInstallKw') -> 'Data': self.held_object.run_bytecompile[self.version] = True @@ -301,6 +292,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): @noPosargs @typed_kwargs('python_installation.install_dir', _PURE_KW, _SUBDIR_KW) + @InterpreterObject.method('get_install_dir') def get_install_dir_method(self, args: T.List['TYPE_var'], kwargs: 'PyInstallKw') -> str: self.held_object.run_bytecompile[self.version] = True pure = kwargs['pure'] if kwargs['pure'] is not None else self.pure @@ -318,16 +310,19 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): @noPosargs @noKwargs + @InterpreterObject.method('language_version') def language_version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: return self.version @typed_pos_args('python_installation.has_path', str) @noKwargs + @InterpreterObject.method('has_path') def has_path_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool: return args[0] in self.paths @typed_pos_args('python_installation.get_path', str, optargs=[object]) @noKwargs + @InterpreterObject.method('get_path') def get_path_method(self, args: T.Tuple[str, T.Optional['TYPE_var']], kwargs: 'TYPE_kwargs') -> 'TYPE_var': path_name, fallback = args try: @@ -339,11 +334,13 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): @typed_pos_args('python_installation.has_variable', str) @noKwargs + @InterpreterObject.method('has_variable') def has_variable_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool: return args[0] in self.variables @typed_pos_args('python_installation.get_variable', str, optargs=[object]) @noKwargs + @InterpreterObject.method('get_variable') def get_variable_method(self, args: T.Tuple[str, T.Optional['TYPE_var']], kwargs: 'TYPE_kwargs') -> 'TYPE_var': var_name, fallback = args try: @@ -356,6 +353,7 @@ class PythonInstallation(_ExternalProgramHolder['PythonExternalProgram']): @noPosargs @noKwargs @FeatureNew('Python module path method', '0.50.0') + @InterpreterObject.method('path') def path_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str: return super().path_method(args, kwargs) diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index f43a0ed..d0e8091 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -242,6 +242,14 @@ class RustModule(ExtensionModule): def doctest(self, state: ModuleState, args: T.Tuple[str, T.Union[SharedLibrary, StaticLibrary]], kwargs: FuncDoctest) -> ModuleReturnValue: name, base_target = args + if not base_target.uses_rust(): + raise MesonException('doc tests are only supported for Rust targets') + if not base_target.uses_rust_abi(): + raise MesonException("doc tests are not supported for rust_abi: 'c'") + if state.environment.is_cross_build() and state.environment.need_exe_wrapper(base_target.for_machine): + mlog.notice('skipping Rust doctests due to cross compilation', once=True) + return ModuleReturnValue(None, []) + # Link the base target's crate into the tests kwargs['link_with'].append(base_target) kwargs['depends'].append(base_target) |