Add `build/gen.py --gen ~/chromium/src=out/Default` This allows for: * `ninja -C out gen` to rerun `gn gen` if gn has changed. * `ninja -C out gen.json` to calculate the build graph generated by `gn gen` Change-Id: I9805e438abbd332d3acd9b3b54f8c4c66a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25602 Commit-Queue: Matt Stark <msta@google.com> Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/build/gen.py b/build/gen.py index 5cb1015..80b1c0d 100755 --- a/build/gen.py +++ b/build/gen.py
@@ -7,6 +7,7 @@ import argparse import os +import pathlib import platform import re import shlex @@ -216,6 +217,12 @@ '`ninja -t compdb`.')) args_list.add('--starlark', action='store_true', default=False, help='Enable (experimental) starlark integration') + args_list.add('--gen', default=None, + metavar='SRC_DIR=OUT_DIR', dest='gen_target', + help=('Generate ninja targets that invoke `gn gen` on an ' + + 'external repository.\n' + + 'Format: <src_dir>=<out_dir> ' + + '(e.g. ~/chromium/src=out/Default)')) args_list.add_to_parser(parser) options = parser.parse_args(argv) @@ -459,6 +466,16 @@ ], ) + if options.gen_target: + if '=' not in options.gen_target: + raise ValueError(f'Invalid --gen format: {repr(options.gen_target)}. Expected <src_dir>=<out_dir>') + gen_src_dir, gen_out_dir = options.gen_target.split('=', 1) + ninja.AddExternalGenTarget( + 'gen', + pathlib.Path(os.path.expanduser(gen_src_dir)).resolve(), + pathlib.Path(gen_out_dir) + ) + with open(path, 'w') as f: f.write('\n'.join(ninja_header_lines)) f.write(ninja_template)
diff --git a/build/ninja_file.py b/build/ninja_file.py index 3f53289..d5b6a3d 100644 --- a/build/ninja_file.py +++ b/build/ninja_file.py
@@ -21,7 +21,6 @@ return f'"{path}"' - @dataclasses.dataclass class Action: rule: 'DummyRule' @@ -91,13 +90,6 @@ self.actions = [] self._gn_exe = pathlib.Path('gn' + self.platform.exe_suffix) - build_prefix = '' if self.platform.is_windows() else './' - - def python(path, args): - return ( - f'{escape_path_command(sys.executable)}' - f' {escape_path_command(self.source_file(path))} {args}' - ) # Define standard/dummy rules (no rule block generated in build.ninja) self.Phony = DummyRule('phony', self) @@ -108,7 +100,8 @@ name='run_binary', ninja_file=self, command=self.chain( - f'$env {build_prefix}$in $args', python('tools/touch.py', '$out') + f'$env {self.build_prefix}$in $args', + self.python('tools/touch.py', '$out'), ), description='RUN BINARY $in', ) @@ -120,10 +113,10 @@ command=self.chain( # For golden tests it's very important that if a ninja file is no # longer generated, it is actually deleted. - python('tools/clean.py', '$path/out'), - f'{build_prefix}{self._gn_exe} gen $path/out --quiet' + self.python('tools/clean.py', '$path/out'), + f'{self.build_prefix}{self._gn_exe} gen $path/out --quiet' ' --root=$path', - python('tools/touch.py', '$out'), + self.python('tools/touch.py', '$out'), ), description='RUN GN $out', inputs=[self._gn_exe], @@ -134,8 +127,8 @@ name='compare_goldens', ninja_file=self, command=self.chain( - python('tools/compare_goldens.py', '$path $goldens'), - python('tools/touch.py', '$out'), + self.python('tools/compare_goldens.py', '$path $goldens'), + self.python('tools/touch.py', '$out'), ), description='COMPARE $out', inputs=[compare_script], @@ -146,11 +139,12 @@ self.Cargo = Rule( name='cargo', ninja_file=self, - command=python( + command=self.python( run_cargo_rel_path, '$target_type $out $cargo_out_dir $cxx "$cxxflags"' ' cargo build --color=always' - ' --manifest-path=$manifest_path $cargo_target_dir $cargo_flags' + ('' if self.debug else ' --release'), + ' --manifest-path=$manifest_path $cargo_target_dir $cargo_flags' + + ('' if self.debug else ' --release'), ), description='CARGO build $out', inputs=[run_cargo_script], @@ -173,7 +167,9 @@ def rust_profile(self): return 'debug' if self.debug else 'release' - def CargoLibTarget(self, name, *, crate_dir, target_dir, cargo_flags='', **kwargs): + def CargoLibTarget( + self, name, *, crate_dir, target_dir, cargo_flags='', **kwargs + ): return self.Cargo( name, inputs=self.directory(crate_dir, ['target', 'testdata']), @@ -186,7 +182,9 @@ **kwargs, ) - def CargoTestTarget(self, name, *, crate_dir, target_dir, cargo_flags='', **kwargs): + def CargoTestTarget( + self, name, *, crate_dir, target_dir, cargo_flags='', **kwargs + ): return self.Cargo( name, inputs=self.directory(crate_dir, ['target']), @@ -229,6 +227,53 @@ goldens=golden_path, ) + @property + def build_prefix(self): + return '' if self.platform.is_windows() else './' + + def python(self, path, args): + return ( + f'{escape_path_command(sys.executable)}' + f' {escape_path_command(self.source_file(path))} {args}' + ) + + def AddExternalGenTarget( + self, name: str, src_dir: pathlib.Path, out_dir: pathlib.Path + ): + build = ( + f'{self.build_prefix}{self._gn_exe} gen' + f' --root={escape_path_command(src_dir)}' + f' {escape_path_command(src_dir / out_dir)}' + ) + run_gn_external = Rule( + name='run_gn_external', + ninja_file=self, + command=self.chain( + build, + self.python('tools/touch.py', '$out'), + ), + description=f'GEN {src_dir} -> {out_dir}', + inputs=[self._gn_exe], + ) + gen_action = run_gn_external(name) + + run_hyperfine = Rule( + name='hyperfine', + ninja_file=self, + command=self.chain( + ( + f"hyperfine $${{HYPERFINE_OPTS:-}} '{build}" + f" --tracelog=${{out}}_$${{HYPERFINE_ITERATION}}.trace'" + ), + self.python('tools/touch.py', '$out'), + ), + description=f'BENCH {src_dir} -> {out_dir}', + inputs=[self._gn_exe], + # Allow hyperfine's spinning progress bars. + pool='console', + ) + hyperfine_action = run_hyperfine(f'{name}_bench') + def write_ninja(self): out = [] for rule in self.rules: