diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 326f56b9a0df..1f053f5abd2a 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -29,10 +29,10 @@ from .. import interpreter from .. import mesonlib from .. import mlog -from ..build import CustomTarget, CustomTargetIndex, GeneratedList, InvalidArguments +from ..build import BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList, InvalidArguments from ..dependencies import Dependency, PkgConfigDependency, InternalDependency 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 noPosargs, noKwargs, FeatureNew, FeatureDeprecated from ..interpreterbase import typed_kwargs, KwargInfo, ContainerTypeInfo from ..interpreterbase.decorators import typed_pos_args from ..mesonlib import ( @@ -166,6 +166,36 @@ class GenerateVapi(TypedDict): gir_dirs: T.List[str] packages: T.List[T.Union[str, InternalDependency]] + class _MkEnumsCommon(TypedDict): + + sources: T.List[T.Union[FileOrString, build.GeneratedTypes]] + install_header: bool + install_dir: T.Optional[str] + identifier_prefix: T.Optional[str] + symbol_prefix: T.Optional[str] + + class MkEnumsSimple(_MkEnumsCommon): + + header_prefix: str + decorator: str + function_prefix: str + body_prefix: str + + class MkEnums(_MkEnumsCommon): + + c_template: T.Optional[FileOrString] + h_template: T.Optional[FileOrString] + comments: T.Optional[str] + eprod: T.Optional[str] + fhead: T.Optional[str] + fprod: T.Optional[str] + ftail: T.Optional[str] + vhead: T.Optional[str] + vprod: T.Optional[str] + vtail: T.Optional[str] + depends: T.List[T.Union[BuildTarget, CustomTarget, CustomTargetIndex]] + + # Differs from the CustomTarget version in that it straight defaults to True _BUILD_BY_DEFAULT: KwargInfo[bool] = KwargInfo( 'build_by_default', bool, default=True, @@ -178,6 +208,19 @@ class GenerateVapi(TypedDict): listify=True, ) +_MK_ENUMS_COMMON_KWS: T.List[KwargInfo] = [ + INSTALL_KW.evolve(name='install_header'), + KwargInfo( + 'sources', + ContainerTypeInfo(list, (str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)), + listify=True, + required=True, + ), + KwargInfo('install_dir', (str, NoneType)), + KwargInfo('identifier_prefix', (str, NoneType)), + KwargInfo('symbol_prefix', (str, NoneType)), +] + # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it # @@ -711,7 +754,7 @@ def _scan_langs(self, state: 'ModuleState', langs: T.Iterable[str]) -> T.List[st return ret - def _scan_gir_targets(self, state: 'ModuleState', girtargets: T.List[build.BuildTarget]) -> T.List[T.Union[str, build.Executable]]: + def _scan_gir_targets(self, state: 'ModuleState', girtargets: T.Sequence[build.BuildTarget]) -> T.List[T.Union[str, build.Executable]]: ret: T.List[T.Union[str, build.Executable]] = [] for girtarget in girtargets: @@ -763,7 +806,7 @@ def _get_gir_targets_deps(self, girtargets: T.Sequence[build.BuildTarget] ret += girtarget.get_external_deps() return ret - def _get_gir_targets_inc_dirs(self, girtargets: T.List[build.BuildTarget]) -> T.List[build.IncludeDirs]: + def _get_gir_targets_inc_dirs(self, girtargets: T.Sequence[build.BuildTarget]) -> T.List[build.IncludeDirs]: ret: T.List[build.IncludeDirs] = [] for girtarget in girtargets: ret += girtarget.get_include_dirs() @@ -798,7 +841,7 @@ def _get_langs_compilers_flags(self, state: 'ModuleState', langs_compilers: T.Li return cflags, internal_ldflags, external_ldflags def _make_gir_filelist(self, state: 'ModuleState', srcdir: str, ns: str, - nsversion: str, girtargets: T.List[build.BuildTarget], + nsversion: str, girtargets: T.Sequence[build.BuildTarget], libsources: T.Sequence[T.Union[ str, mesonlib.File, build.GeneratedList, build.CustomTarget, build.CustomTargetIndex]] @@ -1475,80 +1518,53 @@ def gdbus_codegen(self, state: 'ModuleState', args: T.Tuple[str, T.Optional[str] return ModuleReturnValue(targets, targets) - @permittedKwargs({'sources', 'c_template', 'h_template', 'install_header', 'install_dir', - 'comments', 'identifier_prefix', 'symbol_prefix', 'eprod', 'vprod', - 'fhead', 'fprod', 'ftail', 'vhead', 'vtail', 'depends'}) @typed_pos_args('gnome.mkenums', str) - def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: + @typed_kwargs( + 'gnome.mkenums', + *_MK_ENUMS_COMMON_KWS, + DEPENDS_KW, + KwargInfo('c_template', (str, mesonlib.File, NoneType)), + KwargInfo('h_template', (str, mesonlib.File, NoneType)), + KwargInfo('comments', (str, NoneType)), + KwargInfo('eprod', (str, NoneType)), + KwargInfo('fhead', (str, NoneType)), + KwargInfo('fprod', (str, NoneType)), + KwargInfo('ftail', (str, NoneType)), + KwargInfo('vhead', (str, NoneType)), + KwargInfo('vprod', (str, NoneType)), + KwargInfo('vtail', (str, NoneType)), + ) + def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnums') -> ModuleReturnValue: basename = 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.') + c_template = kwargs['c_template'] + if isinstance(c_template, mesonlib.File): + c_template = c_template.absolute_path(state.environment.source_dir, state.environment.build_dir) + h_template = kwargs['h_template'] + if isinstance(h_template, mesonlib.File): + h_template = h_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - cmd = [] + cmd: T.List[str] = [] known_kwargs = ['comments', 'eprod', 'fhead', 'fprod', 'ftail', - 'identifier_prefix', 'symbol_prefix', 'template', + 'identifier_prefix', 'symbol_prefix', 'vhead', 'vprod', 'vtail'] - known_custom_target_kwargs = ['install_dir', 'build_always', - 'depends', 'depend_files'] - c_template = h_template = None - install_header = False - for arg, value in kwargs.items(): - if arg == 'sources': - raise AssertionError("sources should've already been handled") - elif arg == 'c_template': - c_template = value - if isinstance(c_template, mesonlib.File): - c_template = c_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - if 'template' in kwargs: - raise MesonException('Mkenums does not accept both ' - 'c_template and template keyword ' - 'arguments at the same time.') - elif arg == 'h_template': - h_template = value - if isinstance(h_template, mesonlib.File): - h_template = h_template.absolute_path(state.environment.source_dir, state.environment.build_dir) - if 'template' in kwargs: - raise MesonException('Mkenums does not accept both ' - 'h_template and template keyword ' - 'arguments at the same time.') - elif arg == 'install_header': - install_header = value - elif arg in known_kwargs: - cmd += ['--' + arg.replace('_', '-'), value] - elif arg not in known_custom_target_kwargs: - raise MesonException( - f'Mkenums does not take a {arg} keyword argument.') - cmd = [state.find_program(['glib-mkenums', 'mkenums'])] + cmd - custom_kwargs = {} - for arg in known_custom_target_kwargs: - if arg in kwargs: - custom_kwargs[arg] = kwargs[arg] + for arg in known_kwargs: + # mypy can't figure this out + if kwargs[arg]: # type: ignore + cmd += ['--' + arg.replace('_', '-'), kwargs[arg]] # type: ignore - targets = [] + targets: T.List[CustomTarget] = [] + h_target: T.Optional[CustomTarget] = None if h_template is not None: h_output = os.path.basename(os.path.splitext(h_template)[0]) # We always set template as the first element in the source array # so --template consumes it. h_cmd = cmd + ['--template', '@INPUT@'] - h_sources = [h_template] + sources - - # Copy so we don't mutate the arguments for the c_template - h_kwargs = custom_kwargs.copy() - h_kwargs['install'] = install_header - if 'install_dir' not in h_kwargs: - h_kwargs['install_dir'] = \ - state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) - h_target = self._make_mkenum_custom_target(state, h_sources, - h_output, h_cmd, - h_kwargs) + h_sources = [h_template] + kwargs['sources'] + h_target = self._make_mkenum_impl( + state, h_sources, h_output, h_cmd, install=kwargs['install_header'], + install_dir=kwargs['install_dir']) targets.append(h_target) if c_template is not None: @@ -1556,110 +1572,85 @@ def mkenums(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleRet # We always set template as the first element in the source array # so --template consumes it. c_cmd = cmd + ['--template', '@INPUT@'] - c_sources = [c_template] + sources - - c_kwargs = custom_kwargs.copy() - # Never install the C file. Complain on bug tracker if you need it. - c_kwargs['install'] = False - c_kwargs['install_dir'] = [] - if h_template is not None: - if 'depends' in custom_kwargs: - c_kwargs['depends'] += [h_target] - else: - c_kwargs['depends'] = h_target - c_target = self._make_mkenum_custom_target(state, c_sources, - c_output, c_cmd, - c_kwargs) + c_sources = [c_template] + kwargs['sources'] + + depends = kwargs['depends'].copy() + if h_target is not None: + depends.append(h_target) + c_target = self._make_mkenum_impl( + state, c_sources, c_output, c_cmd, depends=depends) targets.insert(0, c_target) if c_template is None and h_template is None: generic_cmd = cmd + ['@INPUT@'] - custom_kwargs['install'] = install_header - if 'install_dir' not in custom_kwargs: - custom_kwargs['install_dir'] = \ - state.environment.coredata.get_option(mesonlib.OptionKey('includedir')) - target = self._make_mkenum_custom_target(state, sources, basename, - generic_cmd, custom_kwargs) + target = self._make_mkenum_impl( + state, kwargs['sources'], basename, generic_cmd, + install=kwargs['install_header'], + install_dir=kwargs['install_dir']) return ModuleReturnValue(target, [target]) - elif len(targets) == 1: - return ModuleReturnValue(targets[0], [targets[0]]) else: return ModuleReturnValue(targets, targets) @FeatureNew('gnome.mkenums_simple', '0.42.0') @typed_pos_args('gnome.mkenums_simple', str) - def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> ModuleReturnValue: + @typed_kwargs( + 'gnome.mkenums_simple', + *_MK_ENUMS_COMMON_KWS, + KwargInfo('header_prefix', str, default=''), + KwargInfo('function_prefix', str, default=''), + KwargInfo('body_prefix', str, default=''), + KwargInfo('decorator', str, default=''), + ) + def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs: 'MkEnumsSimple') -> ModuleReturnValue: hdr_filename = f'{args[0]}.h' body_filename = f'{args[0]}.c' - # not really needed, just for sanity checking - forbidden_kwargs = ['c_template', 'h_template', 'eprod', 'fhead', - 'fprod', 'ftail', 'vhead', 'vtail', 'comments'] - for arg in forbidden_kwargs: - if arg in kwargs: - raise MesonException(f'mkenums_simple() does not take a {arg} keyword argument') - - # kwargs to pass as-is from mkenums_simple() to mkenums() - shared_kwargs = ['sources', 'install_header', 'install_dir', - 'identifier_prefix', 'symbol_prefix'] - mkenums_kwargs = {} - for arg in shared_kwargs: - if arg in kwargs: - mkenums_kwargs[arg] = kwargs[arg] - - # .c file generation - c_file_kwargs = copy.deepcopy(mkenums_kwargs) - if 'sources' not in kwargs: - raise MesonException('Missing keyword argument "sources".') - sources = kwargs['sources'] - if isinstance(sources, str): - sources = [sources] - elif not isinstance(sources, list): - raise MesonException( - 'Sources keyword argument must be a string or array.') - - # The `install_header` argument will be used by mkenums() when - # not using template files, so we need to forcibly unset it - # when generating the C source file, otherwise we will end up - # installing it - c_file_kwargs['install_header'] = False - - header_prefix = kwargs.get('header_prefix', '') - decl_decorator = kwargs.get('decorator', '') - func_prefix = kwargs.get('function_prefix', '') - body_prefix = kwargs.get('body_prefix', '') + header_prefix = kwargs['header_prefix'] + decl_decorator = kwargs['decorator'] + func_prefix = kwargs['function_prefix'] + body_prefix = kwargs['body_prefix'] + + cmd: T.List[str] = [] + if kwargs['identifier_prefix']: + cmd.extend(['--identifier-prefix', kwargs['identifier_prefix']]) + if kwargs['symbol_prefix']: + cmd.extend(['--symbol-prefix', kwargs['symbol_prefix']]) + c_cmd = cmd.copy() # Maybe we should write our own template files into the build dir # instead, but that seems like much more work, nice as it would be. fhead = '' if body_prefix != '': fhead += '%s\n' % body_prefix fhead += '#include "%s"\n' % hdr_filename - for hdr in sources: + for hdr in kwargs['sources']: fhead += '#include "{}"\n'.format(os.path.basename(str(hdr))) fhead += textwrap.dedent( ''' #define C_ENUM(v) ((gint) v) #define C_FLAGS(v) ((guint) v) ''') - c_file_kwargs['fhead'] = fhead + c_cmd.extend(['--fhead', fhead]) - c_file_kwargs['fprod'] = textwrap.dedent( + c_cmd.append('--fprod') + c_cmd.append(textwrap.dedent( ''' /* enumerations from "@basename@" */ - ''') + ''')) - c_file_kwargs['vhead'] = textwrap.dedent( + c_cmd.append('--vhead') + c_cmd.append(textwrap.dedent( f''' GType {func_prefix}@enum_name@_get_type (void) {{ static gsize gtype_id = 0; - static const G@Type@Value values[] = {{''') + static const G@Type@Value values[] = {{''')) - c_file_kwargs['vprod'] = ' { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },' + c_cmd.extend(['--vprod', ' { C_@TYPE@(@VALUENAME@), "@VALUENAME@", "@valuenick@" },']) - c_file_kwargs['vtail'] = textwrap.dedent( + c_cmd.append('--vtail') + c_cmd.append(textwrap.dedent( ''' { 0, NULL, NULL } }; if (g_once_init_enter (>ype_id)) { @@ -1667,55 +1658,71 @@ def mkenums_simple(self, state: 'ModuleState', args: T.Tuple[str], kwargs) -> Mo g_once_init_leave (>ype_id, new_type); } return (GType) gtype_id; - }''') + }''')) + c_cmd.append('@INPUT@') - rv = self.mkenums(state, [body_filename], c_file_kwargs) - c_file = rv.return_value + c_file = self._make_mkenum_impl(state, kwargs['sources'], body_filename, c_cmd) # .h file generation - h_file_kwargs = copy.deepcopy(mkenums_kwargs) + h_cmd = cmd.copy() - h_file_kwargs['fhead'] = textwrap.dedent( + h_cmd.append('--fhead') + h_cmd.append(textwrap.dedent( f'''#pragma once #include {header_prefix} G_BEGIN_DECLS - ''') + ''')) - h_file_kwargs['fprod'] = textwrap.dedent( + h_cmd.append('--fprod') + h_cmd.append(textwrap.dedent( ''' /* enumerations from "@basename@" */ - ''') + ''')) - h_file_kwargs['vhead'] = textwrap.dedent( + h_cmd.append('--vhead') + h_cmd.append(textwrap.dedent( f''' {decl_decorator} GType {func_prefix}@enum_name@_get_type (void); - #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ ({func_prefix}@enum_name@_get_type())''') + #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ ({func_prefix}@enum_name@_get_type())''')) - h_file_kwargs['ftail'] = textwrap.dedent( + h_cmd.append('--ftail') + h_cmd.append(textwrap.dedent( ''' - G_END_DECLS''') + G_END_DECLS''')) + h_cmd.append('@INPUT@') - rv = self.mkenums(state, [hdr_filename], h_file_kwargs) - h_file = rv.return_value + h_file = self._make_mkenum_impl( + state, kwargs['sources'], hdr_filename, h_cmd, + install=kwargs['install_header'], + install_dir=kwargs['install_dir']) return ModuleReturnValue([c_file, h_file], [c_file, h_file]) @staticmethod - def _make_mkenum_custom_target( + def _make_mkenum_impl( state: 'ModuleState', sources: T.Sequence[T.Union[str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList]], - output: str, cmd: T.List[str], kwargs: T.Dict[str, T.Any]) -> build.CustomTarget: + output: str, + cmd: T.List[str], + *, + install: bool = False, + install_dir: T.Optional[T.Sequence[T.Union[str, bool]]] = None, + depends: T.Optional[T.List[CustomTarget]] = None) -> build.CustomTarget: + real_cmd: T.List[T.Union[str, ExternalProgram]] = [state.find_program(['glib-mkenums', 'mkenums'])] + real_cmd.extend(cmd) custom_kwargs = { 'input': sources, 'output': [output], 'capture': True, - 'command': cmd + 'command': real_cmd, + 'install': install, + 'install_dir': install_dir or state.environment.coredata.get_option(mesonlib.OptionKey('includedir')), + 'depends': depends or [], } - custom_kwargs.update(kwargs) return build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs, # https://github.com/mesonbuild/meson/issues/973 absolute_paths=True)