diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 9091fd5c64b8..4f08c6de1fef 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -120,18 +120,22 @@ typelib_target]` Generates a marshal file using the `glib-genmarshal` tool. The first argument is the basename of the output files. -* `extra_args`: (*Added 0.42.0*) additional command line arguments to - pass +* `depends` [](BuildTarget | CustomTarget | CustomTargetIndex): + passed directly to CustomTarget (*since 0.61.0*) +* `depend_files` [](str | File): Passed directly to CustomTarget (*since 0.61.0*) +* `extra_args`: (*Added 0.42.0*) additional command line arguments to pass +* `install_dir`: directory to install header to * `install_header`: if true, install the generated header * `install_dir`: directory to install header to -* `nostdinc`: if true, don't include the standard marshallers from - glib -* `internal`: if true, mark generated sources as internal to - `glib-genmarshal` (*Requires GLib 2.54*) +* `install_header`: if true, install the generated header +* `internal`: if true, mark generated sources as internal to `glib-genmarshal` + (*Requires GLib 2.54*) +* `nostdinc`: if true, don't include the standard marshallers from glib * `prefix`: the prefix to use for symbols * `skip_source`: if true, skip source location comments -* `stdinc`: if true, include the standard marshallers from glib +* `sources` []str *required*: List of string sources to consume * `sources`: the list of sources to use as inputs +* `stdinc`: if true, include the standard marshallers from glib * `valist_marshallers`: if true, generate va_list marshallers *Added 0.35.0* @@ -158,6 +162,7 @@ template with only minor tweaks, in which case the Note that if you `#include` the generated header in any of the sources for a build target, you must add the generated header to the build target's list of sources to codify the dependency. This is true for + all generated sources, not just `mkenums`. * `c_template`: template to use for generating the source diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 2a68aa044744..1a8d0aeef1e8 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -51,7 +51,7 @@ NullSubprojectInterpreter, ) from .type_checking import ( - COMMAND_KW, + COMMAND_KW, CT_BUILD_ALWAYS, CT_BUILD_ALWAYS_STALE, CT_BUILD_BY_DEFAULT, CT_INPUT_KW, CT_INSTALL_DIR_KW, @@ -209,11 +209,10 @@ def dump_value(self, arr, list_sep, indent): default='exitcode', validator=in_set_validator({'exitcode', 'tap', 'gtest', 'rust'}), since_values={'gtest': '0.55.0', 'rust': '0.57.0'}), - KwargInfo('depends', ContainerTypeInfo(list, (build.CustomTarget, build.BuildTarget)), - listify=True, default=[], since='0.46.0'), KwargInfo('priority', int, default=0, since='0.52.0'), # TODO: env needs reworks of the way the environment variable holder itself works probably ENV_KW, + DEPENDS_KW.evolve(since='0.46.0'), KwargInfo('suite', ContainerTypeInfo(list, str), listify=True, default=['']), # yes, a list of empty string ] @@ -1706,6 +1705,8 @@ def func_subdir_done(self, node, args, kwargs): @typed_kwargs( 'custom_target', COMMAND_KW, + CT_BUILD_ALWAYS.evolve(deprecated='0.47.0'), + CT_BUILD_ALWAYS_STALE.evolve(since='0.47.0'), CT_BUILD_BY_DEFAULT, CT_INPUT_KW, CT_INSTALL_DIR_KW, @@ -1718,8 +1719,6 @@ def func_subdir_done(self, node, args, kwargs): INSTALL_KW, INSTALL_MODE_KW.evolve(since='0.47.0'), OVERRIDE_OPTIONS_KW, - KwargInfo('build_always', (bool, type(None)), deprecated='0.47.0'), - KwargInfo('build_always_stale', (bool, type(None)), since='0.47.0'), KwargInfo('feed', bool, default=False, since='0.59.0'), KwargInfo('capture', bool, default=False), KwargInfo('console', bool, default=False, since='0.48.0'), @@ -1822,8 +1821,8 @@ def func_alias_target(self, node: mparser.BaseNode, args: T.Tuple[str, T.List[bu KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), KwargInfo('output', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), DEPFILE_KW, + DEPENDS_KW, KwargInfo('capture', bool, default=False, since='0.43.0'), - KwargInfo('depends', ContainerTypeInfo(list, (build.BuildTarget, build.CustomTarget)), default=[], listify=True), ) def func_generator(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[build.Executable, ExternalProgram]], diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py index 5391e9f6c513..9ba61f942497 100644 --- a/mesonbuild/interpreter/type_checking.py +++ b/mesonbuild/interpreter/type_checking.py @@ -193,6 +193,7 @@ def _env_convertor(value: T.Union[EnvironmentVariables, T.List[str], T.List[T.Li validator=lambda x: 'Depfile must be a plain filename with a subdirectory' if has_path_sep(x) else None ) +# TODO: CustomTargetIndex should be supported here as well DEPENDS_KW: KwargInfo[T.List[T.Union[BuildTarget, CustomTarget]]] = KwargInfo( 'depends', ContainerTypeInfo(list, (BuildTarget, CustomTarget)), @@ -281,6 +282,11 @@ def _output_validator(outputs: T.List[str]) -> T.Optional[str]: CT_BUILD_BY_DEFAULT: KwargInfo[T.Optional[bool]] = KwargInfo('build_by_default', (bool, type(None)), since='0.40.0') +CT_BUILD_ALWAYS: KwargInfo[T.Optional[bool]] = KwargInfo('build_always', (bool, NoneType)) + +CT_BUILD_ALWAYS_STALE: KwargInfo[T.Optional[bool]] = KwargInfo( + 'build_always_stale', (bool, NoneType)) + INCLUDE_DIRECTORIES: KwargInfo[T.List[T.Union[str, IncludeDirs]]] = KwargInfo( 'include_dirs', ContainerTypeInfo(list, (str, IncludeDirs)), diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 62e3277e20a3..219457891dc9 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -31,9 +31,9 @@ from .. import mlog from ..build import CustomTarget, CustomTargetIndex, GeneratedList, InvalidArguments from ..dependencies import Dependency, PkgConfigDependency, InternalDependency -from ..interpreter.type_checking import DEPEND_FILES_KW, INSTALL_KW, NoneType, in_set_validator -from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, FeatureNew, FeatureDeprecatedKwargs -from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo, FeatureDeprecated +from ..interpreter.type_checking import DEPENDS_KW, DEPEND_FILES_KW, INSTALL_KW, NoneType, in_set_validator +from ..interpreterbase import noPosargs, noKwargs, permittedKwargs, FeatureNew, FeatureDeprecated +from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo from ..interpreterbase.decorators import typed_pos_args from ..mesonlib import ( MachineChoice, MesonException, OrderedSet, Popen_safe, join_args, @@ -139,6 +139,33 @@ class GdbusCodegen(TypedDict): docbook: T.Optional[str] autocleanup: Literal['all', 'none', 'objects', 'default'] + class GenMarshal(TypedDict): + + build_always: T.Optional[str] + build_always_stale: T.Optional[bool] + build_by_default: T.Optional[bool] + depend_files: T.List[mesonlib.File] + extra_args: T.List[str] + install_dir: T.List[T.Union[str, bool]] + install_header: bool + internal: T.Optional[str] + nostdinc: T.Optional[str] + prefix: T.Optional[str] + skip_source: T.Optional[str] + sources: T.List[str] + stdinc: T.Optional[str] + valist_marshallers: T.Optional[str] + + class GenerateVapi(TypedDict): + + sources: T.List[T.Union[str, GirTarget]] + install_dir: T.Optional[str] + install: bool + vapi_dirs: T.List[str] + metadata_dirs: T.List[str] + gir_dirs: T.List[str] + packages: T.List[T.Union[str, InternalDependency]] + # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( 'build_by_default', bool, default=True, @@ -1059,12 +1086,15 @@ def compile_schemas(self, state: 'ModuleState', args: T.List['TYPE_var'], kwargs self._devenv_prepend('GSETTINGS_SCHEMA_DIR', os.path.join(state.environment.get_build_dir(), state.subdir)) return ModuleReturnValue(target_g, [target_g]) - @FeatureDeprecatedKwargs('gnome.yelp', '0.43.0', ['languages'], - 'Use a LINGUAS file in the source directory instead') @typed_pos_args('gnome.yelp', str, varargs=str) @typed_kwargs( 'gnome.yelp', - KwargInfo('languages', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo( + 'languages', ContainerTypeInfo(list, str), + listify=True, default=[], + deprecated='0.43.0', + deprecated_message='Use a LINGUAS file in the source directory instead', + ), KwargInfo('media', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('sources', ContainerTypeInfo(list, str), listify=True, default=[]), KwargInfo('symlink_media', bool, default=True), @@ -1690,50 +1720,51 @@ def _make_mkenum_custom_target( # https://github.com/mesonbuild/meson/issues/973 absolute_paths=True) - @permittedKwargs({'sources', 'prefix', 'install_header', 'install_dir', 'stdinc', - 'nostdinc', 'internal', 'skip_source', 'valist_marshallers', - 'extra_args'}) @typed_pos_args('gnome.genmarshal', str) - def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: + @typed_kwargs( + 'gnome.genmarshal', + DEPEND_FILES_KW.evolve(since='0.61.0'), + DEPENDS_KW.evolve(since='0.61.0'), + INSTALL_KW.evolve(name='install_header'), + KwargInfo('extra_args', ContainerTypeInfo(list, str), listify=True), + KwargInfo('install_dir', (str, NoneType)), + KwargInfo('internal', (str, NoneType)), + KwargInfo('nostdinc', (str, NoneType)), + KwargInfo('prefix', (str, NoneType)), + KwargInfo('skip_source', (str, NoneType)), + KwargInfo('sources', ContainerTypeInfo(list, str, allow_empty=False), listify=True, required=True), + KwargInfo('stdinc', (str, NoneType)), + KwargInfo('valist_marshallers', (str, NoneType)), + ) + def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenMarshal') -> ModuleReturnValue: output = args[0] - - if 'sources' not in kwargs: - raise MesonException('Missing keyword argument "sources".') - sources = kwargs.pop('sources') - if isinstance(sources, str): - sources = [sources] - elif not isinstance(sources, list): - raise MesonException( - 'Sources keyword argument must be a string or array.') + sources = kwargs['sources'] new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') - cmd = [state.find_program('glib-genmarshal')] - known_kwargs = ['internal', 'nostdinc', 'skip_source', 'stdinc', - 'valist_marshallers', 'extra_args'] - known_custom_target_kwargs = ['build_always', 'depends', - 'depend_files', 'install_dir', - 'install_header'] - for arg, value in kwargs.items(): - if arg == 'prefix': - cmd += ['--prefix', value] - elif arg == 'extra_args': - if new_genmarshal: - cmd += mesonlib.stringlistify(value) - else: - mlog.warning('The current version of GLib does not support extra arguments \n' - 'for glib-genmarshal. You need at least GLib 2.53.3. See ', - mlog.bold('https://github.com/mesonbuild/meson/pull/2049')) - elif arg in known_kwargs and value: - cmd += ['--' + arg.replace('_', '-')] - elif arg not in known_custom_target_kwargs: - raise MesonException(f'Genmarshal does not take a {arg} keyword argument.') + cmd: T.List[T.Union['ExternalProgram', str]] = [state.find_program('glib-genmarshal')] + if kwargs['prefix']: + cmd.extend(['--prefix', kwargs['prefix']]) + if kwargs['extra_args']: + if new_genmarshal: + cmd.extend(kwargs['extra_args']) + else: + mlog.warning('The current version of GLib does not support extra arguments \n' + 'for glib-genmarshal. You need at least GLib 2.53.3. See ', + mlog.bold('https://github.com/mesonbuild/meson/pull/2049')) + for k in ['internal', 'nostdinc', 'skip_source', 'stdinc', 'valist_marshallers']: + # Mypy can't figure out that this is correct + if kwargs[k]: # type: ignore + cmd.extend([f'--{k.replace("_", "-")}', kwargs[k]]) # type: ignore - install_header = kwargs.pop('install_header', False) - install_dir = kwargs.pop('install_dir', []) + install_header = kwargs['install_header'] + install_dir = kwargs['install_dir'] - custom_kwargs = { + + custom_kwargs: T.Dict[str, T.Any] = { 'input': sources, + 'depend_files': kwargs['depend_files'], + 'install_dir': kwargs['install_dir'], } # https://github.com/GNOME/glib/commit/0fbc98097fac4d3e647684f344e508abae109fdf @@ -1742,10 +1773,6 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> Module else: custom_kwargs['capture'] = True - for arg in known_custom_target_kwargs: - if arg in kwargs: - custom_kwargs[arg] = kwargs[arg] - header_file = output + '.h' custom_kwargs['command'] = cmd + ['--body', '@INPUT@'] if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.4'): @@ -1765,19 +1792,8 @@ def genmarshal(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> Module rv = [body, header] return ModuleReturnValue(rv, rv) - @staticmethod - def _vapi_args_to_command(prefix: str, variable: str, kwargs: T.Dict[str, T.Any], accept_vapi: bool = False) -> T.List[str]: - arg_list = mesonlib.extract_as_list(kwargs, variable) - ret: T.List[str] = [] - for arg in arg_list: - if not isinstance(arg, str): - types = 'strings' + ' or InternalDependencys' if accept_vapi else '' - raise MesonException(f'All {variable} must be {types}') - ret.append(prefix + arg) - return ret - - def _extract_vapi_packages(self, state: 'ModuleState', kwargs: T.Dict[str, T.Any] - ) -> T.Tuple[T.List[str], T.List[build.Target], T.List[str], T.List[str]]: + def _extract_vapi_packages(self, state: 'ModuleState', packages: T.List[T.Union[InternalDependency, str]], + ) -> T.Tuple[T.List[str], T.List[build.Target], T.List[str], T.List[str], T.List[str]]: ''' Packages are special because we need to: - Get a list of packages for the .deps file @@ -1785,16 +1801,14 @@ def _extract_vapi_packages(self, state: 'ModuleState', kwargs: T.Dict[str, T.Any - Get package name from VapiTargets - Add include dirs for any VapiTargets ''' - arg_list = kwargs.get('packages') - if not arg_list: - return [], [], [], [] - arg_list = mesonlib.listify(arg_list) + if not packages: + return [], [], [], [], [] vapi_depends: T.List[build.Target] = [] vapi_packages: T.List[str] = [] vapi_includes: T.List[str] = [] - ret: T.List[str] = [] + vapi_args: T.List[str] = [] remaining_args = [] - for arg in arg_list: + for arg in packages: if isinstance(arg, InternalDependency): targets = [t for t in arg.sources if isinstance(t, VapiTarget)] for target in targets: @@ -1803,20 +1817,20 @@ def _extract_vapi_packages(self, state: 'ModuleState', kwargs: T.Dict[str, T.Any outdir = os.path.join(state.environment.get_build_dir(), target.get_subdir()) outfile = target.get_outputs()[0][:-5] # Strip .vapi - ret.append('--vapidir=' + outdir) - ret.append('--girdir=' + outdir) - ret.append('--pkg=' + outfile) + vapi_args.append('--vapidir=' + outdir) + vapi_args.append('--girdir=' + outdir) + vapi_args.append('--pkg=' + outfile) vapi_depends.append(target) vapi_packages.append(outfile) vapi_includes.append(srcdir) else: assert isinstance(arg, str), 'for mypy' + vapi_args.append(f'--pkg={arg}') vapi_packages.append(arg) remaining_args.append(arg) - kwargs['packages'] = remaining_args - vapi_args = ret + self._vapi_args_to_command('--pkg=', 'packages', kwargs, accept_vapi=True) - return vapi_args, vapi_depends, vapi_packages, vapi_includes + # TODO: this is supposed to take IncludeDirs, but it never worked + return vapi_args, vapi_depends, vapi_packages, vapi_includes, remaining_args def _generate_deps(self, state: 'ModuleState', library: str, packages: T.List[str], install_dir: str) -> build.Data: outdir = state.environment.scratch_dir @@ -1835,28 +1849,37 @@ def _get_vapi_link_with(self, target: build.CustomTarget) -> T.List[T.Union[buil link_with += self._get_vapi_link_with(dep) return link_with - @permittedKwargs({'sources', 'packages', 'metadata_dirs', 'gir_dirs', - 'vapi_dirs', 'install', 'install_dir'}) @typed_pos_args('gnome.generate_vapi', str) - def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: - created_values = [] + @typed_kwargs( + 'gnome.generate_vapi', + INSTALL_KW, + KwargInfo( + 'sources', + ContainerTypeInfo(list, (str, GirTarget), allow_empty=False), + listify=True, + required=True, + ), + KwargInfo('install_dir', (str, NoneType)), + KwargInfo('vapi_dirs', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('metadata_dirs', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('gir_dirs', ContainerTypeInfo(list, str), listify=True, default=[]), + KwargInfo('packages', ContainerTypeInfo(list, (str, InternalDependency)), listify=True, default=[]), + ) + def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'GenerateVapi') -> ModuleReturnValue: + created_values: T.List[Dependency] = [] library = args[0] build_dir = os.path.join(state.environment.get_build_dir(), state.subdir) source_dir = os.path.join(state.environment.get_source_dir(), state.subdir) - pkg_cmd, vapi_depends, vapi_packages, vapi_includes = self._extract_vapi_packages(state, kwargs) + pkg_cmd, vapi_depends, vapi_packages, vapi_includes, packages = self._extract_vapi_packages(state, kwargs['packages']) cmd: T.List[T.Union[str, 'ExternalProgram']] - cmd = [state.find_program('vapigen')] - cmd += ['--quiet', '--library=' + library, '--directory=' + build_dir] - cmd += self._vapi_args_to_command('--vapidir=', 'vapi_dirs', kwargs) - cmd += self._vapi_args_to_command('--metadatadir=', 'metadata_dirs', kwargs) - cmd += self._vapi_args_to_command('--girdir=', 'gir_dirs', kwargs) + cmd = [state.find_program('vapigen'), '--quiet', f'--library={library}', f'--directory={build_dir}'] + cmd.extend([f'--vapidir={d}' for d in kwargs['vapi_dirs']]) + cmd.extend([f'--metadatdir={d}' for d in kwargs['metadata_dirs']]) + cmd.extend([f'--girdir={d}' for d in kwargs['gir_dirs']]) cmd += pkg_cmd cmd += ['--metadatadir=' + source_dir] - if 'sources' not in kwargs: - raise MesonException('sources are required to generate the vapi file') - - inputs = mesonlib.extract_as_list(kwargs, 'sources') + inputs = kwargs['sources'] link_with = [] for i in inputs: @@ -1868,8 +1891,6 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> Mod i.get_subdir()) gir_file = os.path.join(subdir, i.get_outputs()[0]) cmd.append(gir_file) - else: - raise MesonException('Input must be a str or GirTarget') vapi_output = library + '.vapi' custom_kwargs = { @@ -1878,13 +1899,14 @@ def generate_vapi(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> Mod 'output': vapi_output, 'depends': vapi_depends, } - install_dir = kwargs.get('install_dir', - os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('datadir')), - 'vala', 'vapi')) - if kwargs.get('install'): - custom_kwargs['install'] = kwargs['install'] - custom_kwargs['install_dir'] = install_dir + datadir = state.environment.coredata.get_option(mesonlib.OptionKey('datadir')) + assert isinstance(datadir, str), 'for mypy' + install_dir = kwargs['install_dir'] or os.path.join(datadir, 'vala', 'vapi') + custom_kwargs['install'] = kwargs['install'] + custom_kwargs['install_dir'] = install_dir + custom_kwargs['packages'] = packages + if kwargs['install']: # We shouldn't need this locally but we install it deps_target = self._generate_deps(state, library, vapi_packages, install_dir) created_values.append(deps_target)