diff --git a/mesonbuild/build.py b/mesonbuild/build.py index c401bd28b..ddce65c72 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1498,12 +1498,19 @@ You probably should put it in link_with instead.''') return class Generator: - def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], kwargs): + def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], + arguments: T.List[str], + output: T.List[str], + *, + depfile: T.Optional[str] = None, + capture: bool = False, + depends: T.Optional[T.List[T.Union[BuildTarget, 'CustomTarget']]] = None): self.exe = exe - self.depfile = None - self.capture = False - self.depends = [] - self.process_kwargs(kwargs) + self.depfile = depfile + self.capture = capture + self.depends: T.List[T.Union[BuildTarget, 'CustomTarget']] = depends or [] + self.arglist = arguments + self.outputs = output def __repr__(self): repr_str = "<{0}: {1}>" @@ -1512,53 +1519,7 @@ class Generator: def get_exe(self) -> T.Union['Executable', programs.ExternalProgram]: return self.exe - def process_kwargs(self, kwargs): - if 'arguments' not in kwargs: - raise InvalidArguments('Generator must have "arguments" keyword argument.') - args = kwargs['arguments'] - if isinstance(args, str): - args = [args] - if not isinstance(args, list): - raise InvalidArguments('"Arguments" keyword argument must be a string or a list of strings.') - for a in args: - if not isinstance(a, str): - raise InvalidArguments('A non-string object in "arguments" keyword argument.') - self.arglist = args - if 'output' not in kwargs: - raise InvalidArguments('Generator must have "output" keyword argument.') - outputs = listify(kwargs['output']) - for rule in outputs: - if not isinstance(rule, str): - raise InvalidArguments('"output" may only contain strings.') - if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule: - raise InvalidArguments('Every element of "output" must contain @BASENAME@ or @PLAINNAME@.') - if has_path_sep(rule): - raise InvalidArguments('"outputs" must not contain a directory separator.') - if len(outputs) > 1: - for o in outputs: - if '@OUTPUT@' in o: - raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.') - self.outputs = outputs - if 'depfile' in kwargs: - depfile = kwargs['depfile'] - if not isinstance(depfile, str): - raise InvalidArguments('Depfile must be a string.') - if os.path.basename(depfile) != depfile: - raise InvalidArguments('Depfile must be a plain filename without a subdirectory.') - self.depfile = depfile - if 'capture' in kwargs: - capture = kwargs['capture'] - if not isinstance(capture, bool): - raise InvalidArguments('Capture must be boolean.') - self.capture = capture - if 'depends' in kwargs: - depends = unholder(listify(kwargs['depends'])) - for d in depends: - if not (isinstance(d, (BuildTarget, CustomTarget))): - raise InvalidArguments('Depends entries must be build targets.') - self.depends.append(d) - - def get_base_outnames(self, inname) -> T.List[str]: + def get_base_outnames(self, inname): plainname = os.path.basename(inname) basename = os.path.splitext(plainname)[0] bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs] diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index f2b1bd9ba..dce0391f6 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1959,15 +1959,27 @@ This will become a hard error in the future.''' % kwargs['input'], location=self 'generator', KwargInfo('arguments', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), KwargInfo('output', ContainerTypeInfo(list, str, allow_empty=False), required=True, listify=True), - KwargInfo('depfile', str), + KwargInfo('depfile', str, validator=lambda x: 'Depfile must be a plain filename with a subdirectory' if has_path_sep(x) else None), KwargInfo('capture', bool, default=False, since='0.43.0'), KwargInfo('depends', ContainerTypeInfo(list, (BuildTargetHolder, CustomTargetHolder)), default=[], listify=True), ) def func_generator(self, node: mparser.FunctionNode, args: T.Tuple[T.Union[ExecutableHolder, ExternalProgramHolder]], kwargs: 'kwargs.FuncGenerator') -> GeneratorHolder: - gen = build.Generator(args[0].held_object, kwargs) - holder = GeneratorHolder(self, gen, self) + for rule in kwargs['output']: + if '@BASENAME@' not in rule and '@PLAINNAME@' not in rule: + raise InvalidArguments('Every element of "output" must contain @BASENAME@ or @PLAINNAME@.') + if has_path_sep(rule): + raise InvalidArguments('"output" must not contain a directory separator.') + if len(kwargs['output']) > 1: + for o in kwargs['output']: + if '@OUTPUT@' in o: + raise InvalidArguments('Tried to use @OUTPUT@ in a rule with more than one output.') + + depends = [d.held_object for d in kwargs.pop('depends')] + + gen = build.Generator(args[0].held_object, depends=depends, **kwargs) + holder = GeneratorHolder(gen, self) self.generators.append(holder) return holder diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 76cc4dbf1..f32802736 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -344,12 +344,12 @@ class QtBaseModule(ExtensionModule): "please check your qt{2} installation") raise MesonException(err_msg.format('UIC', f'uic-qt{self.qt_version}', self.qt_version)) - ui_kwargs: T.Dict[str, T.Any] = { # TODO: if Generator was properly annotated… - 'output': 'ui_@BASENAME@.h', - 'arguments': kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@']} # TODO: This generator isn't added to the generator list in the Interpreter - gen = build.Generator(self.uic, ui_kwargs) # type: ignore - out = gen.process_files(f'Qt{self.qt_version} ui', kwargs['sources'], state) + gen = build.Generator( + self.uic, + kwargs['extra_args'] + ['-o', '@OUTPUT@', '@INPUT@'], + ['ui_@BASENAME@.h']) + out = gen.process_files(f'Qt{self.qt_version} ui', kwargs['sources'], state) # type: ignore return ModuleReturnValue(out, [out]) @FeatureNew('qt.compile_moc', '0.59.0') @@ -382,15 +382,11 @@ class QtBaseModule(ExtensionModule): arguments = kwargs['extra_args'] + inc + compile_args + ['@INPUT@', '-o', '@OUTPUT@'] if kwargs['headers']: - moc_kwargs = {'output': 'moc_@BASENAME@.cpp', - 'arguments': arguments} - moc_gen = build.Generator(self.moc, moc_kwargs) # type: ignore - output.append(moc_gen.process_files(f'Qt{self.qt_version} moc header', kwargs['headers'], state)) + moc_gen = build.Generator(self.moc, arguments, ['moc_@BASENAME@.cpp']) + output.append(moc_gen.process_files(f'Qt{self.qt_version} moc header', kwargs['headers'], state)) # type: ignore if kwargs['sources']: - moc_kwargs = {'output': '@BASENAME@.moc', - 'arguments': arguments} - moc_gen = build.Generator(self.moc, moc_kwargs) # type: ignore - output.append(moc_gen.process_files(f'Qt{self.qt_version} moc source', kwargs['sources'], state)) + moc_gen = build.Generator(self.moc, arguments, ['@BASENAME@.moc']) + output.append(moc_gen.process_files(f'Qt{self.qt_version} moc source', kwargs['sources'], state)) # type: ignore return ModuleReturnValue(output, [output])