diff --git a/docs/markdown/snippets/permit_generator_any_target.md b/docs/markdown/snippets/permit_generator_any_target.md new file mode 100644 index 000000000000..cf3e1db10981 --- /dev/null +++ b/docs/markdown/snippets/permit_generator_any_target.md @@ -0,0 +1,3 @@ +## Allow using generator with any [[@build_tgt]] object + +Passing any build target to `generator.process()` is now allowed. diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 139e31a4c436..e531350a8dbf 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -163,7 +163,7 @@ def detect_toolset(self) -> None: def get_target_private_dir(self, target: build.AnyTargetType) -> str: return os.path.join(self.get_target_dir(target), target.get_id()) - def generate_genlist_for_target( + def generate_gensrc_for_target( self, genlist: build.GeneratedTypes, target: build.BuildTarget | build.CustomTarget, parent_node: ET.Element, @@ -171,67 +171,77 @@ def generate_genlist_for_target( custom_target_include_dirs: T.List[str], custom_target_output_files: T.List[str]) -> None: if isinstance(genlist, build.GeneratedList): - for x in genlist.depends: - self.generate_genlist_for_target(x, target, parent_node, [], [], []) + self.generate_genlist_for_target(genlist, target, parent_node, generator_output_files) + return + + down = self.target_to_build_root(target) + for i in genlist.get_outputs(): + # Path to the generated source from the current vcxproj dir via the build root + ipath = os.path.join(down, self.get_target_dir(genlist), i) + custom_target_output_files.append(ipath) + idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target)) + if idir not in custom_target_include_dirs: + custom_target_include_dirs.append(idir) + + def generate_genlist_for_target( + self, genlist: build.GeneratedList, + target: build.BuildTarget | build.CustomTarget, + parent_node: ET.Element, + generator_output_files: T.List[str]) -> None: + for x in genlist.depends: + if isinstance(x, build.GeneratedList): + self.generate_genlist_for_target(x, target, parent_node, []) + target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) down = self.target_to_build_root(target) - if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)): - for i in genlist.get_outputs(): - # Path to the generated source from the current vcxproj dir via the build root - ipath = os.path.join(down, self.get_target_dir(genlist), i) - custom_target_output_files.append(ipath) - idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target)) - if idir not in custom_target_include_dirs: - custom_target_include_dirs.append(idir) - else: - generator = genlist.get_generator() - exe = generator.get_exe() - infilelist = genlist.get_inputs() - outfilelist = genlist.get_outputs() - source_dir = os.path.join(down, self.build_to_src, genlist.subdir) - idgroup = ET.SubElement(parent_node, 'ItemGroup') - samelen = len(infilelist) == len(outfilelist) - for i, curfile in enumerate(infilelist): - if samelen: - sole_output = os.path.join(target_private_dir, outfilelist[i]) - else: - sole_output = '' - infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target))) - deps = self.get_target_depend_files(genlist, True) - deps += self.get_paths_for_dep_outputs(target, genlist.extra_depends) - base_args = generator.get_arglist(infilename) - outfiles_rel = genlist.get_outputs_for(curfile) - outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] - generator_output_files += outfiles - args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output) - for x in base_args] - args = self.replace_outputs(args, target_private_dir, outfiles_rel) - args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()) - .replace("@BUILD_DIR@", target_private_dir) - for x in args] - args = [x.replace("@CURRENT_SOURCE_DIR@", source_dir) for x in args] - args = [x.replace("@SOURCE_ROOT@", self.environment.get_source_dir()) - .replace("@BUILD_ROOT@", self.environment.get_build_dir()) - for x in args] - args = [x.replace('\\', '/') for x in args] - # Always use a wrapper because MSBuild eats random characters when - # there are many arguments. - tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) - cmd, _ = self.as_meson_exe_cmdline( - exe, - self.replace_extra_args(args, genlist), - workdir=tdir_abs, - capture=outfiles[0] if generator.capture else None, - force_serialize=True, - env=genlist.env - ) - deps = cmd[-1:] + deps - abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) - os.makedirs(abs_pdir, exist_ok=True) - cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename) - ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd)) - ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) - ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) + generator = genlist.get_generator() + exe = generator.get_exe() + infilelist = genlist.get_inputs() + outfilelist = genlist.get_outputs() + source_dir = os.path.join(down, self.build_to_src, genlist.subdir) + idgroup = ET.SubElement(parent_node, 'ItemGroup') + samelen = len(infilelist) == len(outfilelist) + for i, curfile in enumerate(infilelist): + if samelen: + sole_output = os.path.join(target_private_dir, outfilelist[i]) + else: + sole_output = '' + infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target))) + deps = self.get_target_depend_files(genlist, True) + deps += self.get_paths_for_dep_outputs(target, genlist.extra_depends) + base_args = generator.get_arglist(infilename) + outfiles_rel = genlist.get_outputs_for(curfile) + outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] + generator_output_files += outfiles + args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output) + for x in base_args] + args = self.replace_outputs(args, target_private_dir, outfiles_rel) + args = [x.replace("@SOURCE_DIR@", self.environment.get_source_dir()) + .replace("@BUILD_DIR@", target_private_dir) + for x in args] + args = [x.replace("@CURRENT_SOURCE_DIR@", source_dir) for x in args] + args = [x.replace("@SOURCE_ROOT@", self.environment.get_source_dir()) + .replace("@BUILD_ROOT@", self.environment.get_build_dir()) + for x in args] + args = [x.replace('\\', '/') for x in args] + # Always use a wrapper because MSBuild eats random characters when + # there are many arguments. + tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) + cmd, _ = self.as_meson_exe_cmdline( + exe, + self.replace_extra_args(args, genlist), + workdir=tdir_abs, + capture=outfiles[0] if generator.capture else None, + force_serialize=True, + env=genlist.env + ) + deps = cmd[-1:] + deps + abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target)) + os.makedirs(abs_pdir, exist_ok=True) + cbs = ET.SubElement(idgroup, 'CustomBuild', Include=infilename) + ET.SubElement(cbs, 'Command').text = ' '.join(self.quote_arguments(cmd)) + ET.SubElement(cbs, 'Outputs').text = ';'.join(outfiles) + ET.SubElement(cbs, 'AdditionalInputs').text = ';'.join(deps) def generate_custom_generator_commands( self, target: build.BuildTarget | build.CustomTarget, parent_node: ET.Element @@ -240,7 +250,7 @@ def generate_custom_generator_commands( custom_target_include_dirs: list[str] = [] custom_target_output_files: list[str] = [] for genlist in target.get_generated_sources(): - self.generate_genlist_for_target(genlist, target, parent_node, generator_output_files, custom_target_include_dirs, custom_target_output_files) + self.generate_gensrc_for_target(genlist, target, parent_node, generator_output_files, custom_target_include_dirs, custom_target_output_files) return generator_output_files, custom_target_output_files, custom_target_include_dirs def generate(self, diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 1d2745102767..872a21ae64d4 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2005,7 +2005,7 @@ def get_arglist(self, inname: str) -> T.List[str]: basename = os.path.splitext(plainname)[0] return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] - def process_files(self, files: T.Iterable[TargetSources], + def process_files(self, files: T.Iterable[TargetSources | BuildTarget], subdir: str = '', preserve_path_from: T.Optional[str] = None, extra_args: T.Optional[T.List[str]] = None, @@ -2020,7 +2020,7 @@ def process_files(self, files: T.Iterable[TargetSources], extra_depends=list(extra_depends) if extra_depends is not None else []) for e in files: - if isinstance(e, (CustomTarget, CustomTargetIndex)): + if isinstance(e, (BuildTarget, CustomTarget, CustomTargetIndex)): output.depends.add(e) fs = [File.from_built_file(e.get_builddir(), f) for f in e.get_outputs()] elif isinstance(e, GeneratedList): @@ -2059,7 +2059,7 @@ class GeneratedList(HoldableObject): def __post_init__(self) -> None: self.name = self.generator.exe - self.depends: T.Set[GeneratedTypes] = set() + self.depends: T.Set[BuildTarget | GeneratedTypes] = set() self.infilelist: T.List[FileMaybeInTargetPrivateDir] = [] self.outfilelist: T.List[str] = [] self.outmap: T.Dict[FileMaybeInTargetPrivateDir, T.List[str]] = {} diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index 9c7dd243582e..89f9dd65fbe1 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -3256,6 +3256,9 @@ def source_strings_to_files(self, sources: list[kwtypes.BuildTargetObjects], # def source_strings_to_files(self, sources: list[T.Union[str, build.TargetSources, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects]], # type: ignore[overload-overlap] ) -> list[T.Union[build.TargetSources, build.BuildTargetTypes, build.BothLibraries, build.ExtractedObjects]]: ... + @T.overload + def source_strings_to_files(self, sources: list[T.Union[str, build.BuildTarget, build.TargetSources]]) -> list[build.BuildTarget | build.TargetSources]: ... # type: ignore[overload-overlap] + @T.overload def source_strings_to_files(self, sources: list[T.Union[str, build.TargetSources, build.StructuredSources]], ) -> list[T.Union[build.TargetSources, build.StructuredSources]]: ... # noqa: F811 @@ -3274,6 +3277,7 @@ def source_strings_to_files(self, list[mesonlib.File | str | build.BuildTargetTypes], list[str | build.TargetSources], list[str | build.TargetSources | build.StructuredSources], + list[str | build.TargetSources | build.BuildTarget], list[kwtypes.CustomTargetInputs], list[mesonlib.File | str | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], list[kwtypes.BuildTargetObjects], @@ -3285,6 +3289,7 @@ def source_strings_to_files(self, list[mesonlib.File | build.CustomTarget | build.CustomTargetIndex], list[build.TargetSources], list[build.TargetSources | build.StructuredSources], + list[build.TargetSources | build.BuildTarget], list[mesonlib.File | build.BuildTargetTypes | build.BothLibraries | build.ExtractedObjects | build.GeneratedTypes], list[build.ObjectTypes | build.GeneratedTypes], list[CustomTargetSources], diff --git a/mesonbuild/interpreter/interpreterobjects.py b/mesonbuild/interpreter/interpreterobjects.py index 6e9a25dbf8b0..8f7975d0c306 100644 --- a/mesonbuild/interpreter/interpreterobjects.py +++ b/mesonbuild/interpreter/interpreterobjects.py @@ -1228,7 +1228,7 @@ class GeneratorHolder(ObjectHolder[build.Generator]): def __init__(self, gen: build.Generator, interpreter: 'Interpreter'): super().__init__(gen, interpreter) - @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) + @typed_pos_args('generator.process', min_varargs=1, varargs=(str, mesonlib.File, build.BuildTarget, build.CustomTarget, build.CustomTargetIndex, build.GeneratedList)) @typed_kwargs( 'generator.process', KwargInfo('preserve_path_from', (str, NoneType), since='0.45.0'), @@ -1238,7 +1238,7 @@ def __init__(self, gen: build.Generator, interpreter: 'Interpreter'): ) @InterpreterObject.method('process') def process_method(self, - args: T.Tuple[T.List[str | build.TargetSources]], + args: T.Tuple[T.List[str | build.BuildTarget | build.TargetSources]], kwargs: 'kwargs.GeneratorProcess') -> build.GeneratedList: preserve_path_from = kwargs['preserve_path_from'] if preserve_path_from is not None: @@ -1252,6 +1252,11 @@ def process_method(self, 'Calling generator.process with CustomTarget or Index of CustomTarget.', '0.57.0', self.interpreter.subproject) + if any(isinstance(a, build.BuildTarget) for a in args[0]): + FeatureNew.single_use( + 'Calling generator.process with BuildTarget.', + '1.12.0', self.interpreter.subproject) + sources = self.interpreter.source_strings_to_files(args[0]) gl = self.held_object.process_files(sources, self.interpreter.subdir, preserve_path_from, extra_args=kwargs['extra_args'], env=kwargs['env'], diff --git a/test cases/common/105 generatorcustom/embedded.c b/test cases/common/105 generatorcustom/embedded.c new file mode 100644 index 000000000000..a51103c3c68e --- /dev/null +++ b/test cases/common/105 generatorcustom/embedded.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} \ No newline at end of file diff --git a/test cases/common/105 generatorcustom/gen.py b/test cases/common/105 generatorcustom/gen.py index 8c3d6e4e0a47..524bf27d5f57 100755 --- a/test cases/common/105 generatorcustom/gen.py +++ b/test cases/common/105 generatorcustom/gen.py @@ -1,12 +1,13 @@ #!/usr/bin/env python3 +from pathlib import Path import sys ifile = sys.argv[1] ofile = sys.argv[2] -with open(ifile) as f: - resname = f.readline().strip() + +resname = Path(ifile).stem templ = 'const char %s[] = "%s";\n' with open(ofile, 'w', encoding='utf-8') as f: diff --git a/test cases/common/105 generatorcustom/main.c b/test cases/common/105 generatorcustom/main.c index 153dc12cba1b..e5a86b430c37 100644 --- a/test cases/common/105 generatorcustom/main.c +++ b/test cases/common/105 generatorcustom/main.c @@ -3,6 +3,6 @@ #include "alltogether.h" int main(void) { - printf("%s - %s - %s - %s\n", res1, res2, res3, res4); + printf("%s - %s - %s - %s - %s\n", res1, res2, res3, res4, embedded); return 0; } diff --git a/test cases/common/105 generatorcustom/meson.build b/test cases/common/105 generatorcustom/meson.build index dab55de4b4f7..40b74443845b 100644 --- a/test cases/common/105 generatorcustom/meson.build +++ b/test cases/common/105 generatorcustom/meson.build @@ -16,7 +16,9 @@ res4 = custom_target('gen-res4', output : 'res4.txt', command : [gen_resx, '@OUTPUT@', '4']) -hs = gen.process('res1.txt', 'res2.txt', res3, res4[0]) +embedded = executable('embedded', 'embedded.c') + +hs = gen.process('res1.txt', 'res2.txt', res3, res4[0], embedded) allinone = custom_target('alltogether', input : hs,