From 12a2dc86ca736249b2ea4d47ae36b165225a3fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20Wei=C3=9Fmann?= Date: Sun, 21 May 2023 18:29:36 +0200 Subject: Allow generator.process(generator.process(...)) Fixes #1141 --- mesonbuild/backend/ninjabackend.py | 5 +- mesonbuild/backend/vs2010backend.py | 119 +++++++++++++++++++----------------- mesonbuild/build.py | 49 ++++++++++++--- 3 files changed, 108 insertions(+), 65 deletions(-) (limited to 'mesonbuild') diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cd4cfb1..4cb680b 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -2513,6 +2513,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return args def generate_genlist_for_target(self, genlist: build.GeneratedList, target: build.BuildTarget) -> None: + for x in genlist.depends: + if isinstance(x, build.GeneratedList): + self.generate_genlist_for_target(x, target) generator = genlist.get_generator() subdir = genlist.subdir exe = generator.get_exe() @@ -2524,7 +2527,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i]) else: sole_output = f'{curfile}' - infilename = curfile.rel_to_builddir(self.build_to_src) + infilename = curfile.rel_to_builddir(self.build_to_src, self.get_target_private_dir(target)) base_args = generator.get_arglist(infilename) outfiles = genlist.get_outputs_for(curfile) outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles] diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ab14a2d..310b1bc 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -118,67 +118,74 @@ class Vs2010Backend(backends.Backend): def get_target_private_dir(self, target): return os.path.join(self.get_target_dir(target), target.get_id()) + def generate_genlist_for_target(self, genlist: T.Union[build.GeneratedList, build.CustomTarget, build.CustomTargetIndex], target: build.BuildTarget, parent_node: ET.Element, generator_output_files: T.List[str], 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, [], [], []) + 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, target_private_dir)) + deps = self.get_custom_target_depend_files(genlist, True) + 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 + ) + 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, parent_node): generator_output_files = [] custom_target_include_dirs = [] custom_target_output_files = [] - target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)) - down = self.target_to_build_root(target) for genlist in target.get_generated_sources(): - 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') - for i, curfile in enumerate(infilelist): - if len(infilelist) == len(outfilelist): - 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)) - deps = self.get_custom_target_depend_files(genlist, True) - 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 - ) - 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) + self.generate_genlist_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 5743236..7b23a1d 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1668,6 +1668,34 @@ You probably should put it in link_with instead.''') 'use shared_library() with `override_options: [\'b_lundef=false\']` instead.') link_target.force_soname = True +class FileInTargetPrivateDir: + """Represents a file with the path '/path/to/build/target_private_dir/fname'. + target_private_dir is the return value of get_target_private_dir which is e.g. 'subdir/target.p'. + """ + + def __init__(self, fname: str): + self.fname = fname + +class FileMaybeInTargetPrivateDir: + """Union between 'File' and 'FileInTargetPrivateDir'""" + + def __init__(self, inner: T.Union[File, FileInTargetPrivateDir]): + self.inner = inner + + @property + def fname(self) -> str: + return self.inner.fname + + def rel_to_builddir(self, build_to_src: str, target_private_dir: str) -> str: + if isinstance(self.inner, FileInTargetPrivateDir): + return os.path.join(target_private_dir, self.inner.fname) + return self.inner.rel_to_builddir(build_to_src) + + def absolute_path(self, srcdir: str, builddir: str) -> str: + if isinstance(self.inner, FileInTargetPrivateDir): + raise RuntimeError('Unreachable code') + return self.inner.absolute_path(srcdir, builddir) + class Generator(HoldableObject): def __init__(self, exe: T.Union['Executable', programs.ExternalProgram], arguments: T.List[str], @@ -1727,10 +1755,14 @@ class Generator(HoldableObject): output.depends.add(e) if isinstance(e, CustomTargetIndex): output.depends.add(e.target) - - if isinstance(e, (CustomTarget, CustomTargetIndex, GeneratedList)): + if isinstance(e, (CustomTarget, CustomTargetIndex)): output.depends.add(e) fs = [File.from_built_file(state.subdir, f) for f in e.get_outputs()] + elif isinstance(e, GeneratedList): + if preserve_path_from: + raise InvalidArguments("generator.process: 'preserve_path_from' is not allowed if one input is a 'generated_list'.") + output.depends.add(e) + fs = [FileInTargetPrivateDir(f) for f in e.get_outputs()] elif isinstance(e, str): fs = [File.from_source_file(state.environment.source_dir, state.subdir, e)] else: @@ -1741,6 +1773,7 @@ class Generator(HoldableObject): abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir) if not self.is_parent_path(preserve_path_from, abs_f): raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.') + f = FileMaybeInTargetPrivateDir(f) output.add_file(f, state) return output @@ -1758,9 +1791,9 @@ class GeneratedList(HoldableObject): def __post_init__(self) -> None: self.name = self.generator.exe self.depends: T.Set[GeneratedTypes] = set() - self.infilelist: T.List['File'] = [] + self.infilelist: T.List[FileMaybeInTargetPrivateDir] = [] self.outfilelist: T.List[str] = [] - self.outmap: T.Dict[File, T.List[str]] = {} + self.outmap: T.Dict[FileMaybeInTargetPrivateDir, T.List[str]] = {} self.extra_depends = [] # XXX: Doesn't seem to be used? self.depend_files: T.List[File] = [] @@ -1776,7 +1809,7 @@ class GeneratedList(HoldableObject): # know the absolute path of self.depend_files.append(File.from_absolute_file(path)) - def add_preserved_path_segment(self, infile: File, outfiles: T.List[str], state: T.Union['Interpreter', 'ModuleState']) -> T.List[str]: + def add_preserved_path_segment(self, infile: FileMaybeInTargetPrivateDir, outfiles: T.List[str], state: T.Union['Interpreter', 'ModuleState']) -> T.List[str]: result: T.List[str] = [] in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir) assert os.path.isabs(self.preserve_path_from) @@ -1786,7 +1819,7 @@ class GeneratedList(HoldableObject): result.append(os.path.join(path_segment, of)) return result - def add_file(self, newfile: File, state: T.Union['Interpreter', 'ModuleState']) -> None: + def add_file(self, newfile: FileMaybeInTargetPrivateDir, state: T.Union['Interpreter', 'ModuleState']) -> None: self.infilelist.append(newfile) outfiles = self.generator.get_base_outnames(newfile.fname) if self.preserve_path_from: @@ -1794,13 +1827,13 @@ class GeneratedList(HoldableObject): self.outfilelist += outfiles self.outmap[newfile] = outfiles - def get_inputs(self) -> T.List['File']: + def get_inputs(self) -> T.List[FileMaybeInTargetPrivateDir]: return self.infilelist def get_outputs(self) -> T.List[str]: return self.outfilelist - def get_outputs_for(self, filename: 'File') -> T.List[str]: + def get_outputs_for(self, filename: FileMaybeInTargetPrivateDir) -> T.List[str]: return self.outmap[filename] def get_generator(self) -> 'Generator': -- cgit v1.1