summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Klocek <michal.klocek@qt.io>2023-06-03 07:33:02 +0200
committerMichal Klocek <michal.klocek@qt.io>2023-07-06 14:33:05 +0000
commit266cfa0fb83513250bcefd8234e0916c195a4b2e (patch)
tree762d08bbe1280c7e4b6324dbb53aae04477faa5a
parentb5eefeb8ec4af885f132e9bb71e64192d719309b (diff)
Update GN to 112.0.5615.213
Change-Id: I952ea0966149fd80f9171340fde3c6fa6ad1792f Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/481441 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--gn/AUTHORS1
-rw-r--r--gn/OWNERS1
-rw-r--r--gn/README.md8
-rw-r--r--gn/build/build_win.ninja.template6
-rwxr-xr-xgn/build/full_test.py10
-rwxr-xr-xgn/build/gen.py34
-rw-r--r--gn/docs/faq.md16
-rw-r--r--gn/docs/reference.md491
-rw-r--r--gn/examples/ios/.gn4
-rw-r--r--gn/examples/ios/build/BUILD.gn2
-rw-r--r--gn/examples/ios/build/BUILDCONFIG.gn10
-rw-r--r--gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py2
-rw-r--r--gn/examples/ios/build/config/ios/scripts/merge_plist.py12
-rw-r--r--gn/examples/ios/build/config/ios/scripts/sdk_info.py64
-rw-r--r--gn/examples/ios/build/config/ios/sdk_info.gni2
-rw-r--r--gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni6
-rw-r--r--gn/examples/ios/build/toolchain/ios/BUILD.gn2
-rw-r--r--gn/infra/README.recipes.md56
-rw-r--r--gn/infra/config/generated/cr-buildbucket.cfg16
-rw-r--r--gn/infra/config/generated/luci-scheduler.cfg6
-rw-r--r--gn/infra/config/generated/project.cfg7
-rw-r--r--gn/infra/config/generated/realms.cfg4
-rwxr-xr-xgn/infra/config/main.star20
-rw-r--r--gn/infra/config/recipes.cfg5
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json4
-rwxr-xr-xgn/infra/recipes.py138
-rw-r--r--gn/infra/recipes/gn.expected/ci_linux.json340
-rw-r--r--gn/infra/recipes/gn.expected/ci_mac.json336
-rw-r--r--gn/infra/recipes/gn.expected/ci_win.json188
-rw-r--r--gn/infra/recipes/gn.expected/cipd_exists.json364
-rw-r--r--gn/infra/recipes/gn.expected/cipd_register.json376
-rw-r--r--gn/infra/recipes/gn.expected/cq_linux.json564
-rw-r--r--gn/infra/recipes/gn.expected/cq_mac.json560
-rw-r--r--gn/infra/recipes/gn.expected/cq_win.json383
-rw-r--r--gn/infra/recipes/gn.py60
-rw-r--r--gn/misc/vim/syntax/gn.vim2
-rw-r--r--gn/src/base/command_line.cc95
-rw-r--r--gn/src/base/command_line.h30
-rw-r--r--gn/src/base/compiler_specific.h4
-rw-r--r--gn/src/base/files/file.cc8
-rw-r--r--gn/src/base/files/file.h5
-rw-r--r--gn/src/base/files/file_posix.cc2
-rw-r--r--gn/src/base/files/file_util.h13
-rw-r--r--gn/src/base/files/file_util_posix.cc17
-rw-r--r--gn/src/base/files/file_util_win.cc44
-rw-r--r--gn/src/base/files/file_win.cc5
-rw-r--r--gn/src/base/logging.cc2
-rw-r--r--gn/src/base/logging.h2
-rw-r--r--gn/src/base/numerics/clamped_math.h12
-rw-r--r--gn/src/base/numerics/safe_conversions.h16
-rw-r--r--gn/src/base/third_party/icu/README.chromium10
-rw-r--r--gn/src/base/third_party/icu/icu_utf.cc129
-rw-r--r--gn/src/base/third_party/icu/icu_utf.h407
-rw-r--r--gn/src/gn/action_target_generator.cc13
-rw-r--r--gn/src/gn/action_values.h5
-rw-r--r--gn/src/gn/analyzer.cc9
-rw-r--r--gn/src/gn/analyzer_unittest.cc2
-rw-r--r--gn/src/gn/args.cc6
-rw-r--r--gn/src/gn/binary_target_generator.cc28
-rw-r--r--gn/src/gn/binary_target_generator.h1
-rw-r--r--gn/src/gn/build_settings.h10
-rw-r--r--gn/src/gn/builder.cc35
-rw-r--r--gn/src/gn/builder.h8
-rw-r--r--gn/src/gn/builtin_tool.cc47
-rw-r--r--gn/src/gn/builtin_tool.h41
-rw-r--r--gn/src/gn/bundle_data.h5
-rw-r--r--gn/src/gn/c_substitution_type.cc13
-rw-r--r--gn/src/gn/c_substitution_type.h3
-rw-r--r--gn/src/gn/command_args.cc2
-rw-r--r--gn/src/gn/command_clean.cc80
-rw-r--r--gn/src/gn/command_desc.cc2
-rw-r--r--gn/src/gn/command_format.cc46
-rw-r--r--gn/src/gn/command_format_unittest.cc1
-rw-r--r--gn/src/gn/command_gen.cc168
-rw-r--r--gn/src/gn/command_meta.cc11
-rw-r--r--gn/src/gn/commands.cc287
-rw-r--r--gn/src/gn/commands.h132
-rw-r--r--gn/src/gn/compile_commands_writer.cc151
-rw-r--r--gn/src/gn/compile_commands_writer.h57
-rw-r--r--gn/src/gn/compile_commands_writer_unittest.cc170
-rw-r--r--gn/src/gn/config_values_generator.h10
-rw-r--r--gn/src/gn/exec_process.cc2
-rw-r--r--gn/src/gn/filesystem_utils.cc25
-rw-r--r--gn/src/gn/filesystem_utils.h6
-rw-r--r--gn/src/gn/format_test_data/084.gn34
-rw-r--r--gn/src/gn/format_test_data/084.golden38
-rw-r--r--gn/src/gn/function_get_target_outputs.cc3
-rw-r--r--gn/src/gn/functions.cc63
-rw-r--r--gn/src/gn/functions_target.cc107
-rw-r--r--gn/src/gn/functions_unittest.cc186
-rw-r--r--gn/src/gn/gn_main.cc3
-rw-r--r--gn/src/gn/header_checker.cc10
-rw-r--r--gn/src/gn/header_checker.h2
-rw-r--r--gn/src/gn/header_checker_unittest.cc10
-rw-r--r--gn/src/gn/import_manager.cc8
-rw-r--r--gn/src/gn/json_project_writer.cc4
-rw-r--r--gn/src/gn/ninja_action_target_writer.cc22
-rw-r--r--gn/src/gn/ninja_action_target_writer.h4
-rw-r--r--gn/src/gn/ninja_action_target_writer_unittest.cc65
-rw-r--r--gn/src/gn/ninja_binary_target_writer.cc25
-rw-r--r--gn/src/gn/ninja_binary_target_writer.h4
-rw-r--r--gn/src/gn/ninja_binary_target_writer_unittest.cc10
-rw-r--r--gn/src/gn/ninja_build_writer.cc90
-rw-r--r--gn/src/gn/ninja_build_writer.h27
-rw-r--r--gn/src/gn/ninja_build_writer_unittest.cc101
-rw-r--r--gn/src/gn/ninja_c_binary_target_writer.cc137
-rw-r--r--gn/src/gn/ninja_c_binary_target_writer_unittest.cc1237
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer.cc163
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer.h17
-rw-r--r--gn/src/gn/ninja_rust_binary_target_writer_unittest.cc821
-rw-r--r--gn/src/gn/ninja_target_command_util.cc5
-rw-r--r--gn/src/gn/ninja_target_command_util.h3
-rw-r--r--gn/src/gn/ninja_target_writer.cc175
-rw-r--r--gn/src/gn/ninja_target_writer.h17
-rw-r--r--gn/src/gn/ninja_toolchain_writer.cc5
-rw-r--r--gn/src/gn/parse_tree.cc13
-rw-r--r--gn/src/gn/parser.cc35
-rw-r--r--gn/src/gn/pointer_set.h12
-rw-r--r--gn/src/gn/pointer_set_unittest.cc14
-rw-r--r--gn/src/gn/resolved_target_deps.h90
-rw-r--r--gn/src/gn/resolved_target_deps_unittest.cc63
-rw-r--r--gn/src/gn/runtime_deps.cc2
-rw-r--r--gn/src/gn/rust_project_writer.cc245
-rw-r--r--gn/src/gn/rust_project_writer_helpers.h51
-rw-r--r--gn/src/gn/rust_project_writer_helpers_unittest.cc154
-rw-r--r--gn/src/gn/rust_project_writer_unittest.cc185
-rw-r--r--gn/src/gn/rust_substitution_type.cc6
-rw-r--r--gn/src/gn/rust_substitution_type.h1
-rw-r--r--gn/src/gn/scope.cc53
-rw-r--r--gn/src/gn/scope.h36
-rw-r--r--gn/src/gn/settings.cc9
-rw-r--r--gn/src/gn/settings.h5
-rw-r--r--gn/src/gn/setup.cc92
-rw-r--r--gn/src/gn/setup.h8
-rw-r--r--gn/src/gn/setup_unittest.cc111
-rw-r--r--gn/src/gn/string_atom.h2
-rw-r--r--gn/src/gn/substitution_list.cc5
-rw-r--r--gn/src/gn/substitution_list.h3
-rw-r--r--gn/src/gn/substitution_type.cc4
-rw-r--r--gn/src/gn/substitution_writer_unittest.cc6
-rw-r--r--gn/src/gn/swift_values.cc2
-rw-r--r--gn/src/gn/swift_values.h3
-rw-r--r--gn/src/gn/switches.cc10
-rw-r--r--gn/src/gn/switches.h4
-rw-r--r--gn/src/gn/target.cc135
-rw-r--r--gn/src/gn/target.h36
-rw-r--r--gn/src/gn/target_public_pair.h131
-rw-r--r--gn/src/gn/target_public_pair_unittest.cc54
-rw-r--r--gn/src/gn/target_unittest.cc13
-rw-r--r--gn/src/gn/template.cc16
-rw-r--r--gn/src/gn/tool.cc24
-rw-r--r--gn/src/gn/tool.h4
-rw-r--r--gn/src/gn/toolchain.cc32
-rw-r--r--gn/src/gn/toolchain.h7
-rw-r--r--gn/src/gn/trace.cc48
-rw-r--r--gn/src/gn/trace.h7
-rw-r--r--gn/src/gn/unique_vector.h21
-rw-r--r--gn/src/gn/variables.cc25
-rw-r--r--gn/src/gn/visibility.cc9
-rw-r--r--gn/src/gn/xcode_object.cc145
-rw-r--r--gn/src/gn/xcode_object.h48
-rw-r--r--gn/src/gn/xcode_object_unittest.cc8
-rw-r--r--gn/src/gn/xcode_writer.cc250
-rw-r--r--gn/src/gn/xcode_writer.h26
-rw-r--r--gn/src/util/atomic_write.cc34
-rw-r--r--gn/src/util/atomic_write.h22
-rw-r--r--gn/src/util/atomic_write_unittest.cc36
-rw-r--r--gn/src/util/worker_pool.cc2
-rwxr-xr-xgn/tools/update_reference.sh12
169 files changed, 9750 insertions, 2292 deletions
diff --git a/gn/AUTHORS b/gn/AUTHORS
index 45cedd7e57a..5e1e5e69535 100644
--- a/gn/AUTHORS
+++ b/gn/AUTHORS
@@ -26,6 +26,7 @@ Alfredo Mazzinghi <mzz.lrd@gmail.com>
Andrew Boyarshin <andrew.boyarshin@gmail.com>
Anuj Kumar Sharma <anujk.sharma@samsung.com>
DanCraft99 <simputest@gmail.com>
+Evangelos Foutras <evangelos@foutrelis.com>
Gergely Nagy <ngg@ngg.hu>
Ilia K <ki.stfu@gmail.com>
Ivan Naydonov <samogot@gmail.com>
diff --git a/gn/OWNERS b/gn/OWNERS
index d78697cf06f..05715701270 100644
--- a/gn/OWNERS
+++ b/gn/OWNERS
@@ -1,3 +1,4 @@
brettw@chromium.org
phosek@chromium.org
scottmg@chromium.org
+sdefresne@chromium.org
diff --git a/gn/README.md b/gn/README.md
index 75821c6d720..4bb1a9ec78e 100644
--- a/gn/README.md
+++ b/gn/README.md
@@ -51,7 +51,7 @@ some disadvanages:
path. This isn't necessarily the correct trade-off for smaller projects.
* The minimal build configuration is relatively heavyweight. There are several
- files required and the exact way all compilers are linkers are run must be
+ files required and the exact way all compilers and linkers are run must be
specified in the configuration (see "Examples" below). There is no default
compiler configuration.
@@ -94,7 +94,7 @@ Alternatively, you can build GN from source with a C++17 compiler:
git clone https://gn.googlesource.com/gn
cd gn
- python build/gen.py
+ python build/gen.py # --allow-warning if you want to build with warnings.
ninja -C out
# To run tests:
out/gn_unittests
@@ -104,8 +104,8 @@ in `PATH`, so you'll want to run from a Visual Studio command prompt, or
similar.
On Linux, Mac and z/OS, the default compiler is `clang++`, a recent version is
-expected to be found in `PATH`. This can be overridden by setting `CC`, `CXX`,
-and `AR`.
+expected to be found in `PATH`. This can be overridden by setting the `CC`, `CXX`,
+and `AR` environment variables.
On z/OS, building GN requires [ZOSLIB](https://github.com/ibmruntimes/zoslib) to be
installed, as described at that URL. When building with `build/gen.py`, use the option
diff --git a/gn/build/build_win.ninja.template b/gn/build/build_win.ninja.template
index e2ca1861042..f1d17750c83 100644
--- a/gn/build/build_win.ninja.template
+++ b/gn/build/build_win.ninja.template
@@ -1,12 +1,12 @@
rule cxx
- command = ninja -t msvc -- $cxx /nologo /showIncludes /FC $includes $cflags /c $in /Fo$out
+ command = $cxx /nologo /showIncludes /FC $includes $cflags /c $in /Fo$out
description = CXX $out
deps = msvc
rule alink_thin
- command = ninja -t msvc -- $ar /nologo /ignore:4221 $libflags /OUT:$out $in
+ command = $ar /nologo /ignore:4221 $libflags /OUT:$out $in
description = LIB $out
rule link
- command = ninja -t msvc -- $ld /nologo $ldflags /OUT:$out /PDB:$out.pdb $in $solibs $libs
+ command = $ld /nologo $ldflags /OUT:$out /PDB:$out.pdb $in $solibs $libs
description = LINK $out
diff --git a/gn/build/full_test.py b/gn/build/full_test.py
index 46ba2de1634..79b4ac48365 100755
--- a/gn/build/full_test.py
+++ b/gn/build/full_test.py
@@ -33,7 +33,7 @@ def Trial(gn_path_to_use, save_out_dir=None):
def main():
if len(sys.argv) < 3 or len(sys.argv) > 4:
- print 'Usage: full_test.py /chrome/tree/at/762a25542878 rel_gn_path [clean]'
+ print('Usage: full_test.py /chrome/tree/at/762a25542878 rel_gn_path [clean]')
return 1
if len(sys.argv) == 4:
@@ -57,7 +57,7 @@ def main():
# First, do a comparison to make sure the output between the two gn binaries
# actually matches.
- print 'Confirming output matches...'
+ print('Confirming output matches...')
dir_a = os.path.join('out', 'a')
dir_b = os.path.join('out', 'b')
Trial(in_chrome_tree_gn, dir_a)
@@ -66,13 +66,13 @@ def main():
# Then, some time trials.
TRIALS = 5
- print 'Comparing performance... (takes a while)'
+ print('Comparing performance... (takes a while)')
time_a = timeit.timeit('Trial("%s")' % in_chrome_tree_gn, number=TRIALS,
setup='from __main__ import Trial')
time_b = timeit.timeit('Trial("%s")' % our_gn, number=TRIALS,
setup='from __main__ import Trial')
- print 'In-tree gn avg: %.3fs' % (time_a / TRIALS)
- print 'Our gn avg: %.3fs' % (time_b / TRIALS)
+ print('In-tree gn avg: %.3fs' % (time_a / TRIALS))
+ print('Our gn avg: %.3fs' % (time_b / TRIALS))
return 0
diff --git a/gn/build/gen.py b/gn/build/gen.py
index ae9c40de8aa..2d109ed6cf3 100755
--- a/gn/build/gen.py
+++ b/gn/build/gen.py
@@ -17,9 +17,9 @@ import sys
# GN's CI builders.
try: # py3
- from shlex import quote as shell_quote
+ from shlex import quote as shell_quote
except ImportError: # py2
- from pipes import quote as shell_quote
+ from pipes import quote as shell_quote
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.dirname(SCRIPT_DIR)
@@ -183,6 +183,9 @@ def main(argv):
'library, or \'-l<name>\' on POSIX systems. Can be ' +
'used multiple times. Useful to link custom malloc ' +
'or cpu profiling libraries.'))
+ args_list.add('--allow-warnings', action='store_true', default=False,
+ help=('Allow compiler warnings, don\'t treat them as '
+ 'errors.'))
if sys.platform == 'zos':
args_list.add('--zoslib-dir',
action='store',
@@ -215,8 +218,8 @@ def main(argv):
def GenerateLastCommitPosition(host, header):
ROOT_TAG = 'initial-commit'
describe_output = subprocess.check_output(
- ['git', 'describe', 'HEAD', '--match', ROOT_TAG], shell=host.is_windows(),
- cwd=REPO_ROOT)
+ ['git', 'describe', 'HEAD', '--abbrev=12', '--match', ROOT_TAG],
+ shell=host.is_windows(), cwd=REPO_ROOT)
mo = re.match(ROOT_TAG + '-(\d+)-g([0-9a-f]+)', describe_output.decode())
if not mo:
raise ValueError(
@@ -296,7 +299,7 @@ def WriteGenericNinja(path, static_libraries, executables,
object_ext = '.o'
def escape_path_ninja(path):
- return path.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
+ return path.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
def src_to_obj(path):
return escape_path_ninja('%s' % os.path.splitext(path)[0] + object_ext)
@@ -427,6 +430,9 @@ def WriteGNNinja(path, platform, host, options, args_list):
cflags.extend(['-flto', '-fwhole-program-vtables'])
ldflags.extend(['-flto', '-fwhole-program-vtables'])
+ if not options.allow_warnings:
+ cflags.append('-Werror')
+
cflags.extend([
'-D_FILE_OFFSET_BITS=64',
'-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS',
@@ -438,9 +444,17 @@ def WriteGNNinja(path, platform, host, options, args_list):
'-Wall',
'-Wextra',
'-Wno-unused-parameter',
+
+ '-Wextra-semi',
+ '-Wundef',
+
'-std=c++17'
])
+ # flags not supported by gcc/g++.
+ if cxx == 'clang++':
+ cflags.extend(['-Wrange-loop-analysis', '-Wextra-semi-stmt'])
+
if platform.is_linux() or platform.is_mingw() or platform.is_msys():
ldflags.append('-Wl,--as-needed')
@@ -504,6 +518,9 @@ def WriteGNNinja(path, platform, host, options, args_list):
libflags.extend(['/LTCG'])
ldflags.extend(['/LTCG'])
+ if not options.allow_warnings:
+ cflags.append('/WX')
+
cflags.extend([
'/DNOMINMAX',
'/DUNICODE',
@@ -515,7 +532,6 @@ def WriteGNNinja(path, platform, host, options, args_list):
'/D_WIN32_WINNT=0x0A00',
'/FS',
'/W4',
- '/WX',
'/Zi',
'/wd4099',
'/wd4100',
@@ -559,7 +575,6 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/base/strings/stringprintf.cc',
'src/base/strings/utf_string_conversion_utils.cc',
'src/base/strings/utf_string_conversions.cc',
- 'src/base/third_party/icu/icu_utf.cc',
'src/base/timer/elapsed_timer.cc',
'src/base/value_iterators.cc',
'src/base/values.cc',
@@ -576,6 +591,7 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/gn/bundle_data.cc',
'src/gn/bundle_data_target_generator.cc',
'src/gn/bundle_file_rule.cc',
+ 'src/gn/builtin_tool.cc',
'src/gn/c_include_iterator.cc',
'src/gn/c_substitution_type.cc',
'src/gn/c_tool.cc',
@@ -715,6 +731,7 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/gn/xcode_object.cc',
'src/gn/xcode_writer.cc',
'src/gn/xml_element_writer.cc',
+ 'src/util/atomic_write.cc',
'src/util/exe_path.cc',
'src/util/msg_loop.cc',
'src/util/semaphore.cc',
@@ -790,6 +807,7 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/gn/path_output_unittest.cc',
'src/gn/pattern_unittest.cc',
'src/gn/pointer_set_unittest.cc',
+ 'src/gn/resolved_target_deps_unittest.cc',
'src/gn/runtime_deps_unittest.cc',
'src/gn/scope_per_file_provider_unittest.cc',
'src/gn/scope_unittest.cc',
@@ -801,6 +819,7 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/gn/string_utils_unittest.cc',
'src/gn/substitution_pattern_unittest.cc',
'src/gn/substitution_writer_unittest.cc',
+ 'src/gn/target_public_pair_unittest.cc',
'src/gn/target_unittest.cc',
'src/gn/template_unittest.cc',
'src/gn/test_with_scheduler.cc',
@@ -815,6 +834,7 @@ def WriteGNNinja(path, platform, host, options, args_list):
'src/gn/visual_studio_writer_unittest.cc',
'src/gn/xcode_object_unittest.cc',
'src/gn/xml_element_writer_unittest.cc',
+ 'src/util/atomic_write_unittest.cc',
'src/util/test/gn_test.cc',
], 'libs': []},
}
diff --git a/gn/docs/faq.md b/gn/docs/faq.md
index d300c906116..41a178de23a 100644
--- a/gn/docs/faq.md
+++ b/gn/docs/faq.md
@@ -171,6 +171,22 @@ date of the output. This approach also may appear to work but is subtly wrong:
the additions of new files to the source directory will not trigger the build
step and that addition will not be reflected in an incremental build.
+## How can I reference all files in a directory (glob)?
+
+Sometimes people want to automatically refer to all files in a directory,
+typically something like `"*.cc"` for the sources. This is called a "glob."
+
+Globs are not supported in GN. In order for Ninja to know when to re-run
+GN, it would need to check the directory modification times of any
+directories being globbed. Directory modification times that reflect
+additions and removals of files are not as reliably implemented across
+platforms and filesystems as file modification times (for example, it is
+not supported on Windows FAT32 drives).
+
+Even if directory modification times work properly on your build systems,
+GN's philosophy prefers a very explicit build specification style that
+is contrary to globs.
+
## Why does "gn check" complain about conditionally included headers?
The "gn check" feature (see `gn help check`) validates that the source code's
diff --git a/gn/docs/reference.md b/gn/docs/reference.md
index 922f1d9d999..76ef2a9e503 100644
--- a/gn/docs/reference.md
+++ b/gn/docs/reference.md
@@ -34,7 +34,7 @@
* [shared_library: Declare a shared library target.](#func_shared_library)
* [source_set: Declare a source set target.](#func_source_set)
* [static_library: Declare a static library target.](#func_static_library)
- * [target: Declare an target with the given programmatic type.](#func_target)
+ * [target: Declare a target with the given programmatic type.](#func_target)
* [Buildfile functions](#functions)
* [assert: Assert an expression is true at generation time.](#func_assert)
* [config: Defines a configuration object.](#func_config)
@@ -53,6 +53,7 @@
* [not_needed: Mark variables from scope as not needed.](#func_not_needed)
* [pool: Defines a pool object.](#func_pool)
* [print: Prints to the console.](#func_print)
+ * [print_stack_trace: Prints a stack trace.](#func_print_stack_trace)
* [process_file_template: Do template expansion over a list of files.](#func_process_file_template)
* [read_file: Read a file into a variable.](#func_read_file)
* [rebase_path: Rebase a file or directory to another location.](#func_rebase_path)
@@ -124,6 +125,7 @@
* [framework_dirs: [directory list] Additional framework search directories.](#var_framework_dirs)
* [frameworks: [name list] Name of frameworks that must be linked.](#var_frameworks)
* [friend: [label pattern list] Allow targets to include private headers.](#var_friend)
+ * [gen_deps: [label list] Declares targets that should generate when this one does.](#var_gen_deps)
* [include_dirs: [directory list] Additional include directories.](#var_include_dirs)
* [inputs: [file list] Additional compile-time dependencies.](#var_inputs)
* [ldflags: [string list] Flags passed to the linker.](#var_ldflags)
@@ -138,7 +140,7 @@
* [output_prefix_override: [boolean] Don't use prefix for output name.](#var_output_prefix_override)
* [outputs: [file list] Output files for actions and copy targets.](#var_outputs)
* [partial_info_plist: [filename] Path plist from asset catalog compiler.](#var_partial_info_plist)
- * [pool: [string] Label of the pool used by the action.](#var_pool)
+ * [pool: [string] Label of the pool used by binary targets and actions.](#var_pool)
* [precompiled_header: [string] Header file to precompile.](#var_precompiled_header)
* [precompiled_header_type: [string] "gcc" or "msvc".](#var_precompiled_header_type)
* [precompiled_source: [file name] Source file to precompile.](#var_precompiled_source)
@@ -777,6 +779,7 @@
"vs2015" - Visual Studio 2015 project/solution files.
"vs2017" - Visual Studio 2017 project/solution files.
"vs2019" - Visual Studio 2019 project/solution files.
+ "vs2022" - Visual Studio 2022 project/solution files.
"xcode" - Xcode workspace/solution files.
"qtcreator" - QtCreator project files.
"json" - JSON file containing target information
@@ -825,6 +828,34 @@
"legacy" - Legacy Build system
"new" - New Build System
+ --xcode-configs=<config_name_list>
+ Configure the list of build configuration supported by the generated
+ project. If specified, must be a list of semicolon-separated strings.
+ If ommitted, a single configuration will be used in the generated
+ project derived from the build directory.
+
+ --xcode-config-build-dir=<string>
+ If present, must be a path relative to the source directory. It will
+ default to $root_out_dir if ommitted. The path is assumed to point to
+ the directory where ninja needs to be invoked. This variable can be
+ used to build for multiple configuration / platform / environment from
+ the same generated Xcode project (assuming that the user has created a
+ gn build directory with the correct args.gn for each).
+
+ One useful value is to use Xcode variables such as '${CONFIGURATION}'
+ or '${EFFECTIVE_PLATFORM}'.
+
+ --xcode-additional-files-patterns=<pattern_list>
+ If present, must be a list of semicolon-separated file patterns. It
+ will be used to add all files matching the pattern located in the
+ source tree to the project. It can be used to add, e.g. documentation
+ files to the project to allow easily edit them.
+
+ --xcode-additional-files-roots=<path_list>
+ If present, must be a list of semicolon-separated paths. It will be used
+ as roots when looking for additional files to add. If ommitted, defaults
+ to "//".
+
--ninja-executable=<string>
Can be used to specify the ninja executable to use when building.
@@ -888,21 +919,37 @@
replay of individual compilations independent of the build system.
This is an unstable format and likely to change without warning.
+ --add-export-compile-commands=<label_pattern>
+ Adds an additional label pattern (see "gn help label_pattern") of a
+ target to add to the compilation database. This pattern is appended to any
+ list values specified in the export_compile_commands variable in the
+ .gn file (see "gn help dotfile"). This allows the user to add additional
+ targets to the compilation database that the project doesn't add by default.
+
+ To add more than one value, specify this switch more than once. Each
+ invocation adds an additional label pattern.
+
+ Example:
+ --add-export-compile-commands=//tools:my_tool
+ --add-export-compile-commands="//base/*"
+
--export-compile-commands[=<target_name1,target_name2...>]
- Produces a compile_commands.json file in the root of the build directory
- containing an array of “command objects”, where each command object
- specifies one way a translation unit is compiled in the project. If a list
- of target_name is supplied, only targets that are reachable from any
- target in any build file whose name is target_name will be used for
- “command objects” generation, otherwise all available targets will be used.
- This is used for various Clang-based tooling, allowing for the replay of
- individual compilations independent of the build system.
- e.g. "foo" will match:
- - "//path/to/src:foo"
- - "//other/path:foo"
- - "//foo:foo"
+ DEPRECATED https://bugs.chromium.org/p/gn/issues/detail?id=302.
+ Please use --add-export-compile-commands for per-user configuration, and
+ the "export_compile_commands" value in the project-level .gn file (see
+ "gn help dotfile") for per-project configuration.
+
+ Overrides the value of the export_compile_commands in the .gn file (see
+ "gn help dotfile") as well as the --add-export-compile-commands switch.
+
+ Unlike the .gn setting, this switch takes a legacy format which is a list
+ of target names that are matched in any directory. For example, "foo" will
+ match:
+ - "//path/to/src:foo"
+ - "//other/path:foo"
+ - "//foo:foo"
and not match:
- - "//foo:bar"
+ - "//foo:bar"
```
### <a name="cmd_help"></a>**gn help &lt;anything&gt;**
@@ -1013,17 +1060,18 @@
A list of target labels from which to initiate the walk.
--data
- A list of keys from which to extract data. In each target walked, its metadata
- scope is checked for the presence of these keys. If present, the contents of
- those variable in the scope are appended to the results list.
+ A comma-separated list of keys from which to extract data. In each target
+ walked, its metadata scope is checked for the presence of these keys. If
+ present, the contents of those variable in the scope are appended to the
+ results list.
--walk (optional)
- A list of keys from which to control the walk. In each target walked, its
- metadata scope is checked for the presence of any of these keys. If present,
- the contents of those variables is checked to ensure that it is a label of
- a valid dependency of the target and then added to the set of targets to walk.
- If the empty string ("") is present in any of these keys, all deps and data_deps
- are added to the walk set.
+ A comma-separated list of keys from which to control the walk. In each
+ target walked, its metadata scope is checked for the presence of any of
+ these keys. If present, the contents of those variables is checked to ensure
+ that it is a label of a valid dependency of the target and then added to the
+ set of targets to walk. If the empty string ("") is present in any of these
+ keys, all deps and data_deps are added to the walk set.
--rebase (optional)
A destination directory onto which to rebase any paths found. If set, all
@@ -1038,7 +1086,7 @@
Lists collected metaresults for the `files` key in the //base/foo:foo
target and all of its dependency tree.
- gn meta out/Debug "//base/foo" --data=files --data=other
+ gn meta out/Debug "//base/foo" --data=files,other
Lists collected metaresults for the `files` and `other` keys in the
//base/foo:foo target and all of its dependency tree.
@@ -1309,6 +1357,12 @@
and stuff like other Python files required to run your script in the "inputs"
variable.
+ Actions can take the configs and public_configs lists, as well as any of the
+ configs variables (defines, include_dirs, etc.) set directly on the target.
+ These behave exactly as they would on a binary target and can be accessed
+ using substitution patterns in the script args (see "gn help args") to
+ implement custom compiler-like tools.
+
The "deps" and "public_deps" for an action will always be
completed before any part of the action is run so it can depend on
the output of previous steps. The "data_deps" will be built if the
@@ -1351,8 +1405,20 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Action variables: args, bridge_header, configs, data, depfile,
+ framework_dirs, inputs, module_deps, module_name,
+ outputs*, pool, response_file_contents, script*,
+ sources
* = required
```
@@ -1440,8 +1506,20 @@
#### **Variables**
```
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources*
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Action variables: args, bridge_header, configs, data, depfile,
+ framework_dirs, inputs, module_deps, module_name,
+ outputs*, pool, response_file_contents, script*,
+ sources
* = required
```
@@ -1493,7 +1571,13 @@
#### **Variables**
```
- sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Bundle-specific: sources*, outputs*
* = required
```
@@ -1543,6 +1627,23 @@
mapping from each source file to an output file name using source expansion
(see "gn help source_expansion"). The placeholders will look like
"{{source_name_part}}", for example.
+
+ If you want to copy the output of a previous build step, the target that
+ generates the file to copy must be reachable from the deps or public_deps of
+ the copy target.
+```
+
+#### **Variables**
+
+```
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Copy variables: sources*, outputs*
+ * = required
```
#### **Examples**
@@ -1562,6 +1663,24 @@
# names in the gen dir. This will just copy each file.
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
+
+ # Copy the output of a generated executable.
+ copy("package_melon") {
+ # This example uses get_label_info() to compute the output directory of the
+ # dependency. This allows the copy rule to work regardless of the toolchain.
+ #
+ # In some cases (particularly actions defined previously in the same file)
+ # you can use get_target_outputs() to get the input file which can eliminate
+ # the assumptions about the output file name of the dependency.
+
+ input_dir = get_label_info("//src/tools/melon", "root_out_dir");
+ sources = [ "$input_dir/melon" ]
+
+ outputs = [ "$target_gen_dir/{{source_file_part}}" ]
+
+ # Depend on the target to build the file before copying.
+ deps = [ "//src/tools/melon" ]
+ }
```
### <a name="func_create_bundle"></a>**create_bundle**: [ios/macOS] Build an iOS or macOS bundle.
@@ -1607,11 +1726,17 @@
#### **Variables**
```
- bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
- bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
- visibility, product_type, code_signing_args, code_signing_script,
- code_signing_sources, code_signing_outputs, xcode_extra_attributes,
- xcode_test_application_name, partial_info_plist, metadata
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Bundle vars: bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
+ bundle_executable_dir, bundle_deps_filter, product_type,
+ code_signing_args, code_signing_script, code_signing_sources,
+ code_signing_outputs, xcode_extra_attributes,
+ xcode_test_application_name, partial_info_plist
```
#### **Example**
@@ -1728,14 +1853,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name
```
@@ -1763,6 +1889,18 @@
dependencies. See the example for details.
```
+#### **Variables**
+
+```
+ Dependent configs: all_dependent_configs, public_configs
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+ Generated file: contents, data_keys, rebase, walk_keys, output_conversion
+```
+
#### **Example (metadata collection)**
```
@@ -1860,18 +1998,6 @@
"../base/foo.cpp", // from //base:a
]
```
-
-#### **Variables**
-
-```
- contents
- data_keys
- rebase
- walk_keys
- output_conversion
- Deps: data_deps, deps, public_deps
- Dependent configs: all_dependent_configs, public_configs
-```
### <a name="func_group"></a>**group**: Declare a named group of targets.
```
@@ -1883,8 +2009,12 @@
#### **Variables**
```
- Deps: data_deps, deps, public_deps
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
```
#### **Example**
@@ -1922,14 +2052,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name, crate_type
```
@@ -1954,14 +2085,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name
```
@@ -1989,14 +2121,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name
```
@@ -2023,14 +2156,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name, crate_type
```
@@ -2068,14 +2202,15 @@
#### **Variables**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
```
### <a name="func_static_library"></a>**static_library**: Declare a static library target.
@@ -2092,14 +2227,15 @@
```
complete_static_lib
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
- Deps: data_deps, deps, public_deps
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
- output_name, output_extension, public, sources, testonly,
+ output_extension, output_name, public, sources, testonly,
visibility
Rust variables: aliased_deps, crate_root, crate_name
@@ -2109,7 +2245,7 @@
target containing both C and C++ sources is acceptable, but a
target containing C and Rust sources is not).
```
-### <a name="func_target"></a>**target**: Declare an target with the given programmatic type.
+### <a name="func_target"></a>**target**: Declare a target with the given programmatic type.
```
target(target_type_string, target_name_string) { ... }
@@ -2128,6 +2264,20 @@
source_set("doom_melon") {
```
+#### **Common target variables**
+
+```
+ Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,
+ write_runtime_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_extension, output_name, public, sources, testonly,
+ visibility
+
+ Targets will also have variables specific to that type, see "gn help <type>"
+ for more.
+```
+
#### **Example**
```
@@ -2201,10 +2351,10 @@
#### **Variables valid in a config definition**
```
- Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
- asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source, rustflags,
- rustenv, swiftflags
+ Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,
+ cflags_objcc, defines, include_dirs, inputs, ldflags,
+ lib_dirs, libs, precompiled_header, precompiled_source,
+ rustenv, rustflags, swiftflags, testonly
Nested configs: configs
General: visibility
```
@@ -2865,6 +3015,35 @@
print(sources, deps)
```
+### <a name="func_print_stack_trace"></a>**print_stack_trace**: Prints a stack trace.
+
+```
+ Prints the current file location, and all template invocations that led up to
+ this location, to the console.
+```
+
+#### **Examples**
+
+```
+ template("foo"){
+ print_stack_trace()
+ }
+ template("bar"){
+ foo(target_name + ".foo") {
+ baz = invoker.baz
+ }
+ }
+ bar("lala") {
+ baz = 42
+ }
+
+ will print out the following:
+
+ print_stack_trace() initiated at //build.gn:2
+ bar("lala") //BUILD.gn:9
+ foo("lala.foo") //BUILD.gn:5
+ print_stack_trace() //BUILD.gn:2
+```
### <a name="func_process_file_template"></a>**process_file_template**: Do template expansion over a list of files.
```
@@ -4377,6 +4556,13 @@
- "arm"
- "arm64"
- "mipsel"
+ - "mips64el"
+ - "s390x"
+ - "ppc64"
+ - "riscv32"
+ - "riscv64"
+ - "e2k"
+ - "loong64"
```
### <a name="var_target_gen_dir"></a>**target_gen_dir**: Directory for a target's generated files.
@@ -4686,6 +4872,14 @@
to the script. Typically you would use source expansion (see "gn help
source_expansion") to insert the source file names.
+ Args can also expand the substitution patterns corresponding to config
+ variables in the same way that compiler tools (see "gn help tool") do. These
+ allow actions that run compiler or compiler-like tools to access the results
+ of propagating configs through the build graph. For example:
+
+ args = [ "{{defines}}", "{{include_dirs}}", "{{rustenv}}", "--input-file",
+ "{{source}}" ]
+
See also "gn help action" and "gn help action_foreach".
```
### <a name="var_asmflags"></a>**asmflags**: Flags passed to the assembler.
@@ -5395,10 +5589,10 @@
# Locate the depfile in the output directory named like the
# inputs but with a ".d" appended.
- depfile = "$relative_target_output_dir/{{source_name}}.d"
+ depfile = "$target_gen_dir/{{source_name_part}}.d"
# Say our script uses "-o <d file>" to indicate the depfile.
- args = [ "{{source}}", "-o", depfile ]
+ args = [ "{{source}}", "-o", rebase_path(depfile, root_build_dir)]
}
```
### <a name="var_deps"></a>**deps**: Private linked dependencies.
@@ -5583,6 +5777,18 @@
]
}
```
+### <a name="var_gen_deps"></a>**gen_deps**: Declares targets that should generate when this one does.
+
+```
+ A list of target labels.
+
+ Not all GN targets that get evaluated are actually turned into ninja targets
+ (see "gn help execution"). If this target is generated, then any targets in
+ the "gen_deps" list will also be generated, regardless of the usual critera.
+
+ Since "gen_deps" are not build time dependencies, there can be cycles between
+ "deps" and "gen_deps" or within "gen_deps" itself.
+```
### <a name="var_include_dirs"></a>**include_dirs**: Additional include directories.
```
@@ -6011,16 +6217,21 @@
The file will be generated regardless of whether the asset compiler has
been invoked or not. See "gn help create_bundle".
```
-### <a name="var_pool"></a>**pool**: Label of the pool used by the action.
+### <a name="var_pool"></a>**pool**: Label of the pool used by binary targets actions.
```
- A fully-qualified label representing the pool that will be used for the
- action. Pools are defined using the pool() {...} declaration.
+ A fully-qualified label representing the pool that will be used for binary
+ targets and actions. Pools are defined using the pool() {...} declaration.
```
#### **Example**
```
+ executable("binary") {
+ pool = "//build:custom_pool"
+ ...
+ }
+
action("action") {
pool = "//build:custom_pool"
...
@@ -6763,6 +6974,28 @@
"//build/my_config.gni",
]
+ export_compile_commands [optional]
+ A list of label patterns for which to generate a Clang compilation
+ database (see "gn help label_pattern" for the string format).
+
+ When specified, GN will generate a compile_commands.json file in the root
+ of the build directory containing information on how to compile each
+ source file reachable from any label matching any pattern in the list.
+ This is used for Clang-based tooling and some editor integration. See
+ https://clang.llvm.org/docs/JSONCompilationDatabase.html
+
+ The switch --add-export-compile-commands to "gn gen" (see "gn help gen")
+ appends to this value which provides a per-user way to customize it.
+
+ The deprecated switch --export-compile-commands to "gn gen" (see "gn help
+ gen") adds to the export target list using a different format.
+
+ Example:
+ export_compile_commands = [
+ "//base/*",
+ "//tools:doom_melon",
+ ]
+
root [optional]
Label of the root build target. The GN build will start by loading the
build file containing this target name. This defaults to "//:" which will
@@ -6773,12 +7006,14 @@
help --root-target").
script_executable [optional]
- Path to specific Python executable or other interpreter to use in
- action targets and exec_script calls. By default GN searches the
- PATH for Python to execute these scripts.
+ By default, GN runs the scripts used in action targets and exec_script
+ calls using the Python interpreter found in PATH. This value specifies the
+ Python executable or other interpreter to use instead.
+
+ If set to the empty string, the scripts will be executed directly.
- If set to the empty string, the path specified in action targets
- and exec_script calls will be executed directly.
+ The command-line switch --script-executable will override this value (see
+ "gn help --script-executable")
secondary_source [optional]
Label of an alternate directory tree to find input files. When searching
@@ -6903,6 +7138,13 @@
required (directly or transitively) to build a target in the default
toolchain.
+ Some targets might be associated but without a formal build dependency (for
+ example, related tools or optional variants). A target that is marked as
+ "generated" can propagate its generated state to an associated target using
+ "gen_deps". This will make the referenced dependency have Ninja rules
+ generated in the same cases the source target has but without a build-time
+ dependency and even in non-default toolchains.
+
See also "gn help ninja_rules".
```
@@ -7109,19 +7351,20 @@
```
All execution happens in the context of a scope which holds the current state
(like variables). With the exception of loops and conditions, '{' introduces
- a new scope that has a parent reference to the old scope.
+ a new scope.
- Variable reads recursively search all nested scopes until the variable is
- found or there are no more scopes. Variable writes always go into the current
- scope. This means that after the closing '}' (again excepting loops and
- conditions), all local variables will be restored to the previous values.
- This also means that "foo = foo" can do useful work by copying a variable
- into the current scope that was defined in a containing scope.
+ Most scopes have a parent reference to the old scope. Variable reads
+ recursively search all parent scopes until the variable is found or there are
+ no more scopes. Variable writes always go into the current scope. This means
+ that after the closing '}' (again excepting loops and conditions), all local
+ variables will be restored to the previous values. This also means that "foo
+ = foo" can do useful work by copying a variable into the current scope that
+ was defined in a containing scope.
- Scopes can also be assigned to variables. Such scopes can be created by
- functions like exec_script, when invoking a template (the template code
- refers to the variables set by the invoking code by the implicitly-created
- "invoker" scope), or explicitly like:
+ Scopes can be assigned to variables. Examples of such scopes are the
+ implicitly-created "invoker" when invoking a template (which refers to the
+ variables set by the invoking code), scopes created by functions like
+ exec_script, and scopes explicitly created like
empty_scope = {}
myvalues = {
@@ -7129,10 +7372,14 @@
bar = "something"
}
- Inside such a scope definition can be any GN code including conditionals and
- function calls. After the close of the scope, it will contain all variables
- explicitly set by the code contained inside it. After this, the values can be
- read, modified, or added to:
+ In the case of explicitly created scopes and scopes created by functions like
+ exec_script, there is no reference to the parent scope. Such scopes are fully
+ self-contained and do not "inherit" values from their defining scope.
+
+ Inside an explicit scope definition can be any GN code including conditionals
+ and function calls. After the close of the scope, it will contain all
+ variables explicitly set by the code contained inside it. After this, the
+ values can be read, modified, or added to:
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
@@ -7552,6 +7799,10 @@
short name. Use "ninja doom_melon" to compile the
"//tools/fruit:doom_melon" executable.
+ Note that for Apple platforms, create_bundle targets with a product_type
+ of "com.apple.product-type.application" are considered as executable
+ for this rule (as they define application bundles).
+
5. The short names of all targets if there is only one target with that
short name.
diff --git a/gn/examples/ios/.gn b/gn/examples/ios/.gn
index e5b6d4a3db7..fc31273e4cc 100644
--- a/gn/examples/ios/.gn
+++ b/gn/examples/ios/.gn
@@ -1,2 +1,6 @@
# The location of the build configuration file.
buildconfig = "//build/BUILDCONFIG.gn"
+
+# The build script are using `python3` which is the only version of
+# python installed by default on macOS Monterey and above.
+script_executable = "python3"
diff --git a/gn/examples/ios/build/BUILD.gn b/gn/examples/ios/build/BUILD.gn
index b6bb6de8079..e543493a550 100644
--- a/gn/examples/ios/build/BUILD.gn
+++ b/gn/examples/ios/build/BUILD.gn
@@ -77,6 +77,8 @@ if (current_os == "ios" || current_os == "mac") {
[
"--target-cpu",
current_cpu,
+ "--target-environment",
+ target_environment,
"--deployment-target",
ios_deployment_target,
],
diff --git a/gn/examples/ios/build/BUILDCONFIG.gn b/gn/examples/ios/build/BUILDCONFIG.gn
index 53ee3d9ab69..ca6c49d56a2 100644
--- a/gn/examples/ios/build/BUILDCONFIG.gn
+++ b/gn/examples/ios/build/BUILDCONFIG.gn
@@ -15,6 +15,16 @@ if (current_os == "") {
current_os = target_os
}
+declare_args() {
+ # Control which platform the build is targeting. Valid values are
+ # "simulator" or "device".
+ target_environment = "simulator"
+}
+
+assert(
+ target_environment == "simulator" || target_environment == "device",
+ "Only supported values for target_environment are 'simulator' and 'device'")
+
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [ "//build:compiler" ]
diff --git a/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py b/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
index cf38b10d198..9e1dfbc2138 100644
--- a/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
+++ b/gn/examples/ios/build/config/ios/scripts/find_app_identifier_prefix.py
@@ -21,7 +21,7 @@ class ProvisioningProfile(object):
def __init__(self, mobileprovision_path):
self._path = mobileprovision_path
- self._data = plistlib.readPlistFromString(
+ self._data = plistlib.loads(
subprocess.check_output(
['security', 'cms', '-D', '-i', mobileprovision_path]))
diff --git a/gn/examples/ios/build/config/ios/scripts/merge_plist.py b/gn/examples/ios/build/config/ios/scripts/merge_plist.py
index 452cf534f9f..f13ce007750 100644
--- a/gn/examples/ios/build/config/ios/scripts/merge_plist.py
+++ b/gn/examples/ios/build/config/ios/scripts/merge_plist.py
@@ -18,11 +18,15 @@ import sys
VARIABLE_PATTERN = re.compile(r'\$\(([^)]*)\)')
+def GetCommandOutput(command):
+ """Returns the output of `command` as a string."""
+ return subprocess.check_output(command, encoding='utf-8')
+
+
def LoadPlist(plist_path):
"""Loads Apple Property List file at |plist_path|."""
return json.loads(
- subprocess.check_output(
- ['plutil', '-convert', 'json', '-o', '-', plist_path]))
+ GetCommandOutput(['plutil', '-convert', 'json', '-o', '-', plist_path]))
def SavePlist(plist_path, content, format):
@@ -30,7 +34,7 @@ def SavePlist(plist_path, content, format):
proc = subprocess.Popen(
['plutil', '-convert', format, '-o', plist_path, '-'],
stdin=subprocess.PIPE)
- output, _ = proc.communicate(json.dumps(content))
+ output, _ = proc.communicate(json.dumps(content).encode('utf-8'))
if proc.returncode:
raise subprocess.CalledProcessError(
proc.returncode,
@@ -76,7 +80,7 @@ def PerformSubstitutions(plist, substitutions):
if isinstance(plist, list):
return [ PerformSubstitutions(item, substitutions) for item in plist ]
- if isinstance(plist, (str, unicode)):
+ if isinstance(plist, str):
result = plist
while True:
match = VARIABLE_PATTERN.search(result)
diff --git a/gn/examples/ios/build/config/ios/scripts/sdk_info.py b/gn/examples/ios/build/config/ios/scripts/sdk_info.py
index 831a3ed1e43..c8f55cb475b 100644
--- a/gn/examples/ios/build/config/ios/scripts/sdk_info.py
+++ b/gn/examples/ios/build/config/ios/scripts/sdk_info.py
@@ -16,6 +16,11 @@ XCODE_VERSION_PATTERN = re.compile(r'Xcode (\d+)\.(\d+)')
XCODE_BUILD_PATTERN = re.compile(r'Build version (.*)')
+def GetCommandOutput(command):
+ """Returns the output of `command` as a string."""
+ return subprocess.check_output(command, encoding='utf-8')
+
+
def GetAppleCpuName(target_cpu):
"""Returns the name of the |target_cpu| using Apple's convention."""
return {
@@ -25,36 +30,31 @@ def GetAppleCpuName(target_cpu):
}.get(target_cpu, target_cpu)
-def IsSimulator(target_cpu):
- """Returns whether the |target_cpu| corresponds to a simulator build."""
- return not target_cpu.startswith('arm')
-
-
-def GetPlatform(target_cpu):
- """Returns the platform for the |target_cpu|."""
- if IsSimulator(target_cpu):
- return 'iphonesimulator'
- else:
- return 'iphoneos'
+def GetPlatform(target_environment):
+ """Returns the platform for |target_environment|."""
+ return {
+ 'simulator': 'iphonesimulator',
+ 'device': 'iphoneos'
+ }[target_environment]
-def GetPlaformDisplayName(target_cpu):
- """Returns the platform display name for the |target_cpu|."""
- if IsSimulator(target_cpu):
- return 'iPhoneSimulator'
- else:
- return 'iPhoneOS'
+def GetPlaformDisplayName(target_environment):
+ """Returns the platform display name for |target_environment|."""
+ return {
+ 'simulator': 'iPhoneSimulator',
+ 'device': 'iPhoneOS'
+ }[target_environment]
def ExtractOSVersion():
"""Extract the version of macOS of the current machine."""
- return subprocess.check_output(['sw_vers', '-buildVersion']).strip()
+ return GetCommandOutput(['sw_vers', '-buildVersion']).strip()
def ExtractXcodeInfo():
"""Extract Xcode version and build version."""
version, build = None, None
- for line in subprocess.check_output(['xcodebuild', '-version']).splitlines():
+ for line in GetCommandOutput(['xcodebuild', '-version']).splitlines():
match = XCODE_VERSION_PATTERN.search(line)
if match:
major, minor = match.group(1), match.group(2)
@@ -72,23 +72,22 @@ def ExtractXcodeInfo():
def ExtractSDKInfo(info, sdk):
"""Extract information about the SDK."""
- return subprocess.check_output(
- ['xcrun', '--sdk', sdk, '--show-sdk-' + info]).strip()
+ return GetCommandOutput(['xcrun', '--sdk', sdk, '--show-sdk-' + info]).strip()
def GetDeveloperDir():
"""Returns the developer dir."""
- return subprocess.check_output(['xcode-select', '-print-path']).strip()
+ return GetCommandOutput(['xcode-select', '-print-path']).strip()
-def GetSDKInfoForCpu(target_cpu, sdk_version, deployment_target):
+def GetSDKInfoForCpu(target_cpu, environment, sdk_version, deployment_target):
"""Returns a dictionary with information about the SDK."""
- platform = GetPlatform(target_cpu)
+ platform = GetPlatform(environment)
sdk_version = sdk_version or ExtractSDKInfo('version', platform)
deployment_target = deployment_target or sdk_version
target = target_cpu + '-apple-ios' + deployment_target
- if IsSimulator(target_cpu):
+ if environment == 'simulator':
target = target + '-simulator'
xcode_version, xcode_build = ExtractXcodeInfo()
@@ -96,10 +95,10 @@ def GetSDKInfoForCpu(target_cpu, sdk_version, deployment_target):
sdk_info = {}
sdk_info['compiler'] = 'com.apple.compilers.llvm.clang.1_0'
- sdk_info['is_simulator'] = IsSimulator(target_cpu)
+ sdk_info['is_simulator'] = environment == 'simulator'
sdk_info['macos_build'] = ExtractOSVersion()
sdk_info['platform'] = platform
- sdk_info['platform_name'] = GetPlaformDisplayName(target_cpu)
+ sdk_info['platform_name'] = GetPlaformDisplayName(environment)
sdk_info['sdk'] = effective_sdk
sdk_info['sdk_build'] = ExtractSDKInfo('build-version', effective_sdk)
sdk_info['sdk_path'] = ExtractSDKInfo('path', effective_sdk)
@@ -124,6 +123,12 @@ def ParseArgs(argv):
choices=('x86', 'x64', 'arm', 'arm64'),
help='target cpu')
parser.add_argument(
+ '-e',
+ '--target-environment',
+ default='simulator',
+ choices=('simulator', 'device'),
+ help='target environment')
+ parser.add_argument(
'-s', '--sdk-version',
help='version of the sdk')
parser.add_argument(
@@ -140,9 +145,8 @@ def main(argv):
args = ParseArgs(argv)
sdk_info = GetSDKInfoForCpu(
- GetAppleCpuName(args.target_cpu),
- args.sdk_version,
- args.deployment_target)
+ GetAppleCpuName(args.target_cpu), args.target_environment,
+ args.sdk_version, args.deployment_target)
if args.output == '-':
sys.stdout.write(json.dumps(sdk_info))
diff --git a/gn/examples/ios/build/config/ios/sdk_info.gni b/gn/examples/ios/build/config/ios/sdk_info.gni
index 90b86310870..c8002533c4e 100644
--- a/gn/examples/ios/build/config/ios/sdk_info.gni
+++ b/gn/examples/ios/build/config/ios/sdk_info.gni
@@ -8,6 +8,8 @@ sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
[
"--target-cpu",
current_cpu,
+ "--target-environment",
+ target_environment,
"--deployment-target",
ios_deployment_target,
],
diff --git a/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni b/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
index 1a3d6296321..43ec35593ec 100644
--- a/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
+++ b/gn/examples/ios/build/config/ios/templates/ios_binary_bundle.gni
@@ -117,5 +117,11 @@ template("ios_binary_bundle") {
bundle_contents_dir = bundle_root_dir
bundle_executable_dir = bundle_contents_dir
bundle_resources_dir = bundle_contents_dir
+
+ xcode_extra_attributes = {
+ CODE_SIGN_IDENTITY = ""
+ CODE_SIGNING_REQUIRED = "NO"
+ CODE_SIGNING_ALLOWED = "NO"
+ }
}
}
diff --git a/gn/examples/ios/build/toolchain/ios/BUILD.gn b/gn/examples/ios/build/toolchain/ios/BUILD.gn
index 12d246b5c03..b7797200709 100644
--- a/gn/examples/ios/build/toolchain/ios/BUILD.gn
+++ b/gn/examples/ios/build/toolchain/ios/BUILD.gn
@@ -23,6 +23,8 @@ template("ios_toolchain") {
[
"--target-cpu",
current_cpu,
+ "--target-environment",
+ target_environment,
"--deployment-target",
ios_deployment_target,
],
diff --git a/gn/infra/README.recipes.md b/gn/infra/README.recipes.md
index fabdf8542eb..0196775a6f6 100644
--- a/gn/infra/README.recipes.md
+++ b/gn/infra/README.recipes.md
@@ -3,21 +3,23 @@
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
- * [macos_sdk](#recipe_modules-macos_sdk) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
- * [target](#recipe_modules-target)
- * [windows_sdk](#recipe_modules-windows_sdk)
+ * [macos_sdk](#recipe_modules-macos_sdk) (Python3 ✅) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
+ * [target](#recipe_modules-target) (Python3 ✅)
+ * [windows_sdk](#recipe_modules-windows_sdk) (Python3 ✅)
**[Recipes](#Recipes)**
- * [gn](#recipes-gn) &mdash; Recipe for building GN.
- * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full)
- * [target:examples/full](#recipes-target_examples_full)
- * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full)
+ * [gn](#recipes-gn) (Python3 ✅) &mdash; Recipe for building GN.
+ * [macos_sdk:examples/full](#recipes-macos_sdk_examples_full) (Python3 ✅)
+ * [target:examples/full](#recipes-target_examples_full) (Python3 ✅)
+ * [windows_sdk:examples/full](#recipes-windows_sdk_examples_full) (Python3 ✅)
## Recipe Modules
### *recipe_modules* / [macos\_sdk](/infra/recipe_modules/macos_sdk)
[DEPS](/infra/recipe_modules/macos_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
The `macos_sdk` module provides safe functions to access a semi-hermetic
XCode installation.
@@ -66,6 +68,8 @@ Raises:
[DEPS](/infra/recipe_modules/target/__init__.py#5): [recipe\_engine/platform][recipe_engine/recipe_modules/platform]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
#### **class [TargetApi](/infra/recipe_modules/target/api.py#82)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
&emsp; **@property**<br>&mdash; **def [host](/infra/recipe_modules/target/api.py#87)(self):**
@@ -73,6 +77,8 @@ Raises:
[DEPS](/infra/recipe_modules/windows_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
#### **class [WindowsSDKApi](/infra/recipe_modules/windows_sdk/api.py#10)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
API for using Windows SDK distributed via CIPD.
@@ -89,36 +95,44 @@ Raises:
### *recipes* / [gn](/infra/recipes/gn.py)
-[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [target](#recipe_modules-target), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [target](#recipe_modules-target), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cas][recipe_engine/recipe_modules/cas], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+PYTHON_VERSION_COMPATIBILITY: PY3
Recipe for building GN.
-&mdash; **def [RunSteps](/infra/recipes/gn.py#102)(api, repository):**
+&mdash; **def [RunSteps](/infra/recipes/gn.py#103)(api, repository):**
### *recipes* / [macos\_sdk:examples/full](/infra/recipe_modules/macos_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/macos_sdk/examples/full.py#5): [macos\_sdk](#recipe_modules-macos_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
&mdash; **def [RunSteps](/infra/recipe_modules/macos_sdk/examples/full.py#13)(api):**
### *recipes* / [target:examples/full](/infra/recipe_modules/target/examples/full.py)
[DEPS](/infra/recipe_modules/target/examples/full.py#5): [target](#recipe_modules-target), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
&mdash; **def [RunSteps](/infra/recipe_modules/target/examples/full.py#13)(api):**
### *recipes* / [windows\_sdk:examples/full](/infra/recipe_modules/windows_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/windows_sdk/examples/full.py#5): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+PYTHON_VERSION_COMPATIBILITY: PY3
+
&mdash; **def [RunSteps](/infra/recipe_modules/windows_sdk/examples/full.py#13)(api):**
-[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-buildbucket
-[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-cipd
-[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-context
-[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-file
-[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-json
-[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-path
-[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-platform
-[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-properties
-[recipe_engine/recipe_modules/python]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-python
-[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-raw_io
-[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/README.recipes.md#recipe_modules-step
-[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/d31ba13ede8c21e60116ae61e4490d53ba77fcbd/recipe_engine/recipe_api.py#856
+[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-buildbucket
+[recipe_engine/recipe_modules/cas]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-cas
+[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-cipd
+[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-file
+[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-json
+[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-path
+[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-platform
+[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-properties
+[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-raw_io
+[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/recipe_engine/recipe_api.py#886
diff --git a/gn/infra/config/generated/cr-buildbucket.cfg b/gn/infra/config/generated/cr-buildbucket.cfg
index fdfd16caf43..9815e2bfea0 100644
--- a/gn/infra/config/generated/cr-buildbucket.cfg
+++ b/gn/infra/config/generated/cr-buildbucket.cfg
@@ -14,7 +14,7 @@ buckets {
name: "linux"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
- dimensions: "os:Ubuntu-16.04"
+ dimensions: "os:Ubuntu-18.04"
dimensions: "pool:luci.flex.ci"
recipe {
name: "gn"
@@ -24,7 +24,7 @@ buckets {
execution_timeout_secs: 3600
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
@@ -46,7 +46,7 @@ buckets {
}
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
@@ -68,7 +68,7 @@ buckets {
}
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
@@ -92,7 +92,7 @@ buckets {
name: "linux"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
- dimensions: "os:Ubuntu-16.04"
+ dimensions: "os:Ubuntu-18.04"
dimensions: "pool:luci.flex.try"
recipe {
name: "gn"
@@ -102,7 +102,7 @@ buckets {
execution_timeout_secs: 3600
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
@@ -124,7 +124,7 @@ buckets {
}
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
@@ -146,7 +146,7 @@ buckets {
}
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
- key: "luci.use_realms"
+ key: "luci.recipes.use_python3"
value: 100
}
}
diff --git a/gn/infra/config/generated/luci-scheduler.cfg b/gn/infra/config/generated/luci-scheduler.cfg
index 1ba26bf74ad..a3ff24d7fce 100644
--- a/gn/infra/config/generated/luci-scheduler.cfg
+++ b/gn/infra/config/generated/luci-scheduler.cfg
@@ -10,7 +10,7 @@ job {
acl_sets: "ci"
buildbucket {
server: "cr-buildbucket.appspot.com"
- bucket: "luci.gn.ci"
+ bucket: "ci"
builder: "linux"
}
}
@@ -20,7 +20,7 @@ job {
acl_sets: "ci"
buildbucket {
server: "cr-buildbucket.appspot.com"
- bucket: "luci.gn.ci"
+ bucket: "ci"
builder: "mac"
}
}
@@ -30,7 +30,7 @@ job {
acl_sets: "ci"
buildbucket {
server: "cr-buildbucket.appspot.com"
- bucket: "luci.gn.ci"
+ bucket: "ci"
builder: "win"
}
}
diff --git a/gn/infra/config/generated/project.cfg b/gn/infra/config/generated/project.cfg
index 0ac69510b6f..764450f818e 100644
--- a/gn/infra/config/generated/project.cfg
+++ b/gn/infra/config/generated/project.cfg
@@ -6,3 +6,10 @@
name: "gn"
access: "group:all"
+lucicfg {
+ version: "1.30.11"
+ package_dir: ".."
+ config_dir: "generated"
+ entry_point: "main.star"
+ experiments: "crbug.com/1182002"
+}
diff --git a/gn/infra/config/generated/realms.cfg b/gn/infra/config/generated/realms.cfg
index ed90980b030..54791e928d7 100644
--- a/gn/infra/config/generated/realms.cfg
+++ b/gn/infra/config/generated/realms.cfg
@@ -49,4 +49,8 @@ realms {
principals: "group:project-gn-tryjob-access"
principals: "group:service-account-cq"
}
+ bindings {
+ role: "role/swarming.taskTriggerer"
+ principals: "group:flex-try-led-users"
+ }
}
diff --git a/gn/infra/config/main.star b/gn/infra/config/main.star
index 2762024af3f..135796771ba 100755
--- a/gn/infra/config/main.star
+++ b/gn/infra/config/main.star
@@ -1,10 +1,9 @@
#!/usr/bin/env lucicfg
-lucicfg.check_version("1.23.3", "Please update depot_tools")
+lucicfg.check_version("1.30.9", "Please update depot_tools")
-# Enable LUCI Realms support and launch all builds in realms-aware mode.
-lucicfg.enable_experiment("crbug.com/1085650")
-luci.builder.defaults.experiments.set({"luci.use_realms": 100})
+# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.
+lucicfg.enable_experiment("crbug.com/1182002")
lucicfg.config(
config_dir = "generated",
@@ -56,6 +55,9 @@ def builder(name, bucket, os, caches = None, triggered_by = None):
execution_timeout = 1 * time.hour,
dimensions = {"cpu": "x86-64", "os": os, "pool": "luci.flex.%s" % bucket},
triggered_by = triggered_by,
+ experiments = {
+ "luci.recipes.use_python3": 100,
+ },
)
luci.logdog(
@@ -95,7 +97,7 @@ def ci_builder(name, os, caches = None):
short_name = name,
)
-ci_builder("linux", "Ubuntu-16.04")
+ci_builder("linux", "Ubuntu-18.04")
ci_builder("mac", "Mac-10.15", caches = [swarming.cache("macos_sdk")])
ci_builder("win", "Windows-10", caches = [swarming.cache("windows_sdk")])
@@ -136,6 +138,12 @@ luci.bucket(name = "try", acls = [
),
])
+luci.binding(
+ realm = "try",
+ roles = "role/swarming.taskTriggerer",
+ groups = "flex-try-led-users",
+)
+
def try_builder(name, os, caches = None):
builder(name, "try", os, caches)
luci.cq_tryjob_verifier(
@@ -143,6 +151,6 @@ def try_builder(name, os, caches = None):
cq_group = "gn",
)
-try_builder("linux", "Ubuntu-16.04")
+try_builder("linux", "Ubuntu-18.04")
try_builder("mac", "Mac-10.15", caches = [swarming.cache("macos_sdk")])
try_builder("win", "Windows-10", caches = [swarming.cache("windows_sdk")])
diff --git a/gn/infra/config/recipes.cfg b/gn/infra/config/recipes.cfg
index 45963824ce8..da25624866f 100644
--- a/gn/infra/config/recipes.cfg
+++ b/gn/infra/config/recipes.cfg
@@ -2,11 +2,12 @@
"api_version": 2,
"deps": {
"recipe_engine": {
- "branch": "main",
- "revision": "d31ba13ede8c21e60116ae61e4490d53ba77fcbd",
+ "branch": "refs/heads/main",
+ "revision": "ff5ce51ceaaa7a62275bb766831a3ce1e88de37d",
"url": "https://chromium.googlesource.com/infra/luci/recipes-py"
}
},
"project_id": "gn",
+ "py3_only": true,
"recipes_path": "infra"
}
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
index f6c937f8812..f45cd832c0a 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json
@@ -30,9 +30,9 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
- "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "RECIPE_MODULE[recipe_engine::json]\\resources\\read.py",
"[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
diff --git a/gn/infra/recipes.py b/gn/infra/recipes.py
index 2fe00862255..7c534c224c3 100755
--- a/gn/infra/recipes.py
+++ b/gn/infra/recipes.py
@@ -1,47 +1,59 @@
-#!/usr/bin/env python
-
-# Copyright 2017 The LUCI Authors. All rights reserved.
+#!/bin/sh
+# Copyright 2019 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
+# We want to run python in unbuffered mode; however shebangs on linux grab the
+# entire rest of the shebang line as a single argument, leading to errors like:
+#
+# /usr/bin/env: 'python3 -u': No such file or directory
+#
+# This little shell hack is a triple-quoted noop in python, but in sh it
+# evaluates to re-exec'ing this script in unbuffered mode.
+# pylint: disable=pointless-string-statement
+''''exec python3 -u -- "$0" ${1+"$@"} # '''
+# vi: syntax=python
"""Bootstrap script to clone and forward to the recipe engine tool.
*******************
** DO NOT MODIFY **
*******************
-This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/master/recipes.py.
+This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/main/recipes.py.
To fix bugs, fix in the googlesource repo then run the autoroller.
"""
+# pylint: disable=wrong-import-position
import argparse
+import errno
import json
import logging
import os
-import random
import subprocess
import sys
-import time
-import urlparse
from collections import namedtuple
+from io import open # pylint: disable=redefined-builtin
-from cStringIO import StringIO
+try:
+ import urllib.parse as urlparse
+except ImportError:
+ import urlparse
# The dependency entry for the recipe_engine in the client repo's recipes.cfg
#
# url (str) - the url to the engine repo we want to use.
# revision (str) - the git revision for the engine to get.
# branch (str) - the branch to fetch for the engine as an absolute ref (e.g.
-# refs/heads/master)
-EngineDep = namedtuple('EngineDep',
- 'url revision branch')
+# refs/heads/main)
+EngineDep = namedtuple('EngineDep', 'url revision branch')
class MalformedRecipesCfg(Exception):
+
def __init__(self, msg, path):
- super(MalformedRecipesCfg, self).__init__('malformed recipes.cfg: %s: %r'
- % (msg, path))
+ full_message = 'malformed recipes.cfg: %s: %r' % (msg, path)
+ super(MalformedRecipesCfg, self).__init__(full_message)
def parse(repo_root, recipes_cfg_path):
@@ -58,9 +70,12 @@ def parse(repo_root, recipes_cfg_path):
recipes_path (str) - native path to where the recipes live inside of the
current repo (i.e. the folder containing `recipes/` and/or
`recipe_modules`)
+ py3_only (bool) - True if this repo has been marked as ONLY supporting
+ python3.
"""
- with open(recipes_cfg_path, 'rU') as fh:
+ with open(recipes_cfg_path, 'r') as fh:
pb = json.load(fh)
+ py3_only = pb.get('py3_only', False)
try:
if pb['api_version'] != 2:
@@ -73,40 +88,42 @@ def parse(repo_root, recipes_cfg_path):
if not repo_name:
repo_name = pb['project_id']
if repo_name == 'recipe_engine':
- return None, pb.get('recipes_path', '')
+ return None, pb.get('recipes_path', ''), py3_only
engine = pb['deps']['recipe_engine']
if 'url' not in engine:
raise MalformedRecipesCfg(
- 'Required field "url" in dependency "recipe_engine" not found',
- recipes_cfg_path)
+ 'Required field "url" in dependency "recipe_engine" not found',
+ recipes_cfg_path)
engine.setdefault('revision', '')
- engine.setdefault('branch', 'refs/heads/master')
+ engine.setdefault('branch', 'refs/heads/main')
recipes_path = pb.get('recipes_path', '')
# TODO(iannucci): only support absolute refs
if not engine['branch'].startswith('refs/'):
engine['branch'] = 'refs/heads/' + engine['branch']
- recipes_path = os.path.join(
- repo_root, recipes_path.replace('/', os.path.sep))
- return EngineDep(**engine), recipes_path
+ recipes_path = os.path.join(repo_root,
+ recipes_path.replace('/', os.path.sep))
+ return EngineDep(**engine), recipes_path, py3_only
except KeyError as ex:
- raise MalformedRecipesCfg(ex.message, recipes_cfg_path)
+ raise MalformedRecipesCfg(str(ex), recipes_cfg_path)
-_BAT = '.bat' if sys.platform.startswith(('win', 'cygwin')) else ''
+IS_WIN = sys.platform.startswith(('win', 'cygwin'))
+
+_BAT = '.bat' if IS_WIN else ''
GIT = 'git' + _BAT
-VPYTHON = 'vpython' + _BAT
CIPD = 'cipd' + _BAT
-REQUIRED_BINARIES = {GIT, VPYTHON, CIPD}
+REQUIRED_BINARIES = {GIT, CIPD}
def _is_executable(path):
return os.path.isfile(path) and os.access(path, os.X_OK)
+
# TODO: Use shutil.which once we switch to Python3.
def _is_on_path(basename):
for path in os.environ['PATH'].split(os.pathsep):
@@ -122,13 +139,13 @@ def _subprocess_call(argv, **kwargs):
def _git_check_call(argv, **kwargs):
- argv = [GIT]+argv
+ argv = [GIT] + argv
logging.info('Running %r', argv)
subprocess.check_call(argv, **kwargs)
def _git_output(argv, **kwargs):
- argv = [GIT]+argv
+ argv = [GIT] + argv
logging.info('Running %r', argv)
return subprocess.check_output(argv, **kwargs)
@@ -152,10 +169,14 @@ def parse_args(argv):
def checkout_engine(engine_path, repo_root, recipes_cfg_path):
- dep, recipes_path = parse(repo_root, recipes_cfg_path)
+ """Checks out the recipe_engine repo pinned in recipes.cfg.
+
+ Returns the path to the recipe engine repo and the py3_only boolean.
+ """
+ dep, recipes_path, py3_only = parse(repo_root, recipes_cfg_path)
if dep is None:
# we're running from the engine repo already!
- return os.path.join(repo_root, recipes_path)
+ return os.path.join(repo_root, recipes_path), py3_only
url = dep.url
@@ -174,22 +195,33 @@ def checkout_engine(engine_path, repo_root, recipes_cfg_path):
_git_check_call(['init', engine_path], stdout=NUL)
try:
- _git_check_call(['rev-parse', '--verify', '%s^{commit}' % revision],
- cwd=engine_path, stdout=NUL, stderr=NUL)
- except subprocess.CalledProcessError:
- _git_check_call(['fetch', url, branch], cwd=engine_path, stdout=NUL,
+ _git_check_call(['rev-parse', '--verify',
+ '%s^{commit}' % revision],
+ cwd=engine_path,
+ stdout=NUL,
stderr=NUL)
+ except subprocess.CalledProcessError:
+ _git_check_call(['fetch', '--quiet', url, branch],
+ cwd=engine_path,
+ stdout=NUL)
try:
_git_check_call(['diff', '--quiet', revision], cwd=engine_path)
except subprocess.CalledProcessError:
+ index_lock = os.path.join(engine_path, '.git', 'index.lock')
+ try:
+ os.remove(index_lock)
+ except OSError as exc:
+ if exc.errno != errno.ENOENT:
+ logging.warn('failed to remove %r, reset will fail: %s', index_lock,
+ exc)
_git_check_call(['reset', '-q', '--hard', revision], cwd=engine_path)
# If the engine has refactored/moved modules we need to clean all .pyc files
# or things will get squirrely.
_git_check_call(['clean', '-qxf'], cwd=engine_path)
- return engine_path
+ return engine_path, py3_only
def main():
@@ -206,22 +238,36 @@ def main():
if recipes_cfg_path:
# calculate repo_root from recipes_cfg_path
repo_root = os.path.dirname(
- os.path.dirname(
- os.path.dirname(recipes_cfg_path)))
+ os.path.dirname(os.path.dirname(recipes_cfg_path)))
else:
# find repo_root with git and calculate recipes_cfg_path
- repo_root = (_git_output(
- ['rev-parse', '--show-toplevel'],
- cwd=os.path.abspath(os.path.dirname(__file__))).strip())
- repo_root = os.path.abspath(repo_root)
+ repo_root = (
+ _git_output(['rev-parse', '--show-toplevel'],
+ cwd=os.path.abspath(os.path.dirname(__file__))).strip())
+ repo_root = os.path.abspath(repo_root).decode()
recipes_cfg_path = os.path.join(repo_root, 'infra', 'config', 'recipes.cfg')
args = ['--package', recipes_cfg_path] + args
-
- engine_path = checkout_engine(engine_override, repo_root, recipes_cfg_path)
-
- return _subprocess_call([
- VPYTHON, '-u',
- os.path.join(engine_path, 'recipe_engine', 'main.py')] + args)
+ engine_path, py3_only = checkout_engine(engine_override, repo_root, recipes_cfg_path)
+
+ using_py3 = py3_only or os.getenv('RECIPES_USE_PY3') == 'true'
+ vpython = ('vpython' + ('3' if using_py3 else '') + _BAT)
+ if not _is_on_path(vpython):
+ return 'Required binary is not found on PATH: %s' % vpython
+
+ argv = ([
+ vpython, '-u', os.path.join(engine_path, 'recipe_engine', 'main.py'),
+ ] + args)
+
+ if IS_WIN:
+ # No real 'exec' on windows; set these signals to ignore so that they
+ # propagate to our children but we still wait for the child process to quit.
+ import signal
+ signal.signal(signal.SIGBREAK, signal.SIG_IGN)
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
+ return _subprocess_call(argv)
+ else:
+ os.execvp(argv[0], argv)
if __name__ == '__main__':
diff --git a/gn/infra/recipes/gn.expected/ci_linux.json b/gn/infra/recipes/gn.expected/ci_linux.json
index 128c0e6e038..27e7b3f09f2 100644
--- a/gn/infra/recipes/gn.expected/ci_linux.json
+++ b/gn/infra/recipes/gn.expected/ci_linux.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -70,6 +118,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -105,6 +165,18 @@
"init",
"[START_DIR]/rpmalloc"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -116,10 +188,22 @@
"fetch",
"--tags",
"https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
- "f4b7c52c858675f732a76bd1c73447e0fcf84b1e"
+ "+upstream/develop"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -129,16 +213,90 @@
"cmd": [
"git",
"checkout",
- "FETCH_HEAD"
+ "668a7f81b588a985c6528b70674dbcc005d9cb75"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py",
+ "/path/to/tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.read [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.write [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
"cmd": [],
"name": "rpmalloc.build rpmalloc-linux-amd64",
"~followup_annotations": [
@@ -147,7 +305,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -164,6 +322,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -182,6 +352,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -196,7 +378,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -213,6 +395,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -231,6 +425,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -249,7 +455,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -262,6 +468,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -281,6 +499,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -298,6 +528,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -316,7 +558,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -331,6 +573,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -350,6 +604,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -367,6 +633,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -400,6 +678,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -421,7 +711,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -436,6 +726,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -455,6 +757,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -488,6 +802,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
diff --git a/gn/infra/recipes/gn.expected/ci_mac.json b/gn/infra/recipes/gn.expected/ci_mac.json
index 90114aded03..66274f850b0 100644
--- a/gn/infra/recipes/gn.expected/ci_mac.json
+++ b/gn/infra/recipes/gn.expected/ci_mac.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -70,6 +118,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -103,6 +163,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed (2)",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -130,6 +202,18 @@
"[CACHE]/macos_sdk/XCode.app"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "install xcode"
},
{
@@ -140,6 +224,18 @@
"[CACHE]/macos_sdk/XCode.app"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "select XCode"
},
{
@@ -151,11 +247,23 @@
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.xcrun sdk-path",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -167,13 +275,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.xcrun toolchain",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -185,7 +305,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -198,6 +318,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -217,6 +349,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -234,6 +378,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -248,11 +404,23 @@
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun sdk-path",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -264,13 +432,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun toolchain",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -282,7 +462,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -296,6 +476,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -315,6 +507,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -332,6 +536,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -365,6 +581,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.upload.build gn/gn/mac-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -382,11 +610,23 @@
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun sdk-path (2)",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -398,13 +638,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun toolchain (2)",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -416,7 +668,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -430,6 +682,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -449,6 +713,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -482,6 +758,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-arm64.upload.build gn/gn/mac-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -501,6 +789,18 @@
"--reset"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "reset XCode"
},
{
diff --git a/gn/infra/recipes/gn.expected/ci_win.json b/gn/infra/recipes/gn.expected/ci_win.json
index fcdc6582ecc..11534b5ab8e 100644
--- a/gn/infra/recipes/gn.expected/ci_win.json
+++ b/gn/infra/recipes/gn.expected/ci_win.json
@@ -10,6 +10,18 @@
"[START_DIR]\\gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -70,6 +118,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -99,6 +159,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed (2)",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -116,12 +188,24 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
- "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "RECIPE_MODULE[recipe_engine::json]\\resources\\read.py",
"[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "read SetEnv.x64.json",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -159,7 +243,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]\\gn\\build\\gen.py",
"-d"
@@ -173,6 +257,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -193,6 +289,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -211,6 +319,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -229,7 +349,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]\\gn\\build\\gen.py",
"--use-lto",
@@ -244,6 +364,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -264,6 +396,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -282,6 +426,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -316,6 +472,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.upload.build gn/gn/windows-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -336,6 +504,18 @@
"/im",
"mspdbsrv.exe"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "taskkill mspdbsrv"
},
{
diff --git a/gn/infra/recipes/gn.expected/cipd_exists.json b/gn/infra/recipes/gn.expected/cipd_exists.json
index 9d2811dd338..64c1fb5409e 100644
--- a/gn/infra/recipes/gn.expected/cipd_exists.json
+++ b/gn/infra/recipes/gn.expected/cipd_exists.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -70,6 +118,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -105,6 +165,18 @@
"init",
"[START_DIR]/rpmalloc"
],
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -116,10 +188,22 @@
"fetch",
"--tags",
"https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
- "f4b7c52c858675f732a76bd1c73447e0fcf84b1e"
+ "+upstream/develop"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -129,16 +213,90 @@
"cmd": [
"git",
"checkout",
- "FETCH_HEAD"
+ "668a7f81b588a985c6528b70674dbcc005d9cb75"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py",
+ "/path/to/tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.read [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.write [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
"cmd": [],
"name": "rpmalloc.build rpmalloc-linux-amd64",
"~followup_annotations": [
@@ -147,7 +305,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -164,6 +322,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -182,6 +352,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -196,7 +378,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -213,6 +395,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -231,6 +425,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -249,7 +455,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -262,6 +468,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -281,6 +499,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -298,6 +528,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -316,7 +558,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -331,6 +573,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -350,6 +604,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -367,6 +633,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -400,6 +678,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -430,6 +720,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -460,7 +762,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -475,6 +777,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -494,6 +808,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -527,6 +853,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -557,6 +895,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.upload.cipd search gn/gn/linux-arm64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
diff --git a/gn/infra/recipes/gn.expected/cipd_register.json b/gn/infra/recipes/gn.expected/cipd_register.json
index 425046a04e7..650b9da20f1 100644
--- a/gn/infra/recipes/gn.expected/cipd_register.json
+++ b/gn/infra/recipes/gn.expected/cipd_register.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -70,6 +118,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -105,6 +165,18 @@
"init",
"[START_DIR]/rpmalloc"
],
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -116,10 +188,22 @@
"fetch",
"--tags",
"https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
- "f4b7c52c858675f732a76bd1c73447e0fcf84b1e"
+ "+upstream/develop"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -129,16 +213,90 @@
"cmd": [
"git",
"checkout",
- "FETCH_HEAD"
+ "668a7f81b588a985c6528b70674dbcc005d9cb75"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py",
+ "/path/to/tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.read [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.write [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
"cmd": [],
"name": "rpmalloc.build rpmalloc-linux-amd64",
"~followup_annotations": [
@@ -147,7 +305,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -164,6 +322,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -182,6 +352,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -196,7 +378,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -213,6 +395,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -231,6 +425,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -249,7 +455,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -262,6 +468,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -281,6 +499,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -298,6 +528,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -316,7 +558,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -331,6 +573,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -350,6 +604,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -367,6 +633,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -400,6 +678,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.build gn/gn/linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -430,6 +720,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -461,6 +763,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.upload.register gn/gn/linux-amd64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -483,7 +797,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -498,6 +812,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -517,6 +843,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -550,6 +888,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.upload.build gn/gn/linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
@@ -580,6 +930,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "infra-internal:ci"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.upload.cipd search gn/gn/linux-arm64 git_revision:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@3@@@",
diff --git a/gn/infra/recipes/gn.expected/cq_linux.json b/gn/infra/recipes/gn.expected/cq_linux.json
index 4e32325bb46..1259594a205 100644
--- a/gn/infra/recipes/gn.expected/cq_linux.json
+++ b/gn/infra/recipes/gn.expected/cq_linux.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -65,6 +113,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -78,6 +138,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -97,6 +169,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -132,6 +216,18 @@
"init",
"[START_DIR]/rpmalloc"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -143,10 +239,22 @@
"fetch",
"--tags",
"https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc",
- "f4b7c52c858675f732a76bd1c73447e0fcf84b1e"
+ "+upstream/develop"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -156,16 +264,90 @@
"cmd": [
"git",
"checkout",
- "FETCH_HEAD"
+ "668a7f81b588a985c6528b70674dbcc005d9cb75"
],
"cwd": "[START_DIR]/rpmalloc",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
},
{
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py",
+ "/path/to/tmp/"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.read [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']",
+ "[START_DIR]/rpmalloc/build/ninja/clang.py"
+ ],
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "rpmalloc.write [START_DIR]/rpmalloc/build/ninja/clang.py",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@1@@@",
+ "@@@STEP_LOG_LINE@clang.py@CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']@@@",
+ "@@@STEP_LOG_END@clang.py@@@"
+ ]
+ },
+ {
"cmd": [],
"name": "rpmalloc.build rpmalloc-linux-amd64",
"~followup_annotations": [
@@ -174,7 +356,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -191,6 +373,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -209,6 +403,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-amd64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -223,7 +429,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/rpmalloc/configure.py",
"-c",
@@ -240,6 +446,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.configure",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -258,6 +476,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "rpmalloc.build rpmalloc-linux-arm64.ninja",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -276,7 +506,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -289,6 +519,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -308,6 +550,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -325,6 +579,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -343,7 +609,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -358,6 +624,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -377,6 +655,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -394,6 +684,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -401,6 +703,185 @@
},
{
"cmd": [],
+ "name": "release.linux-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[recipe_engine::cas]/resources/infra.sha1",
+ "/path/to/tmp/"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.linux-amd64.upload.read infra revision",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@infra.sha1@git_revision:mock_infra_git_revision@@@",
+ "@@@STEP_LOG_END@infra.sha1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.linux-amd64.upload.install infra/tools/luci/cas",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.linux-amd64.upload.install infra/tools/luci/cas.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision",
+ "-ensure-file",
+ "infra/tools/luci/cas/${platform} git_revision:mock_infra_git_revision",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.linux-amd64.upload.install infra/tools/luci/cas.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:moc\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/luci/cas/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision/cas",
+ "archive",
+ "-cas-instance",
+ "projects/example-cas-server/instances/default_instance",
+ "-dump-digest",
+ "/path/to/tmp/",
+ "-paths",
+ "[START_DIR]/gn/out:gn"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.linux-amd64.upload.upload binary to CAS",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LINK@CAS UI@https://cas-viewer.appspot.com/projects/example-cas-server/instances/default_instance/blobs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/0/tree@@@"
+ ]
+ },
+ {
+ "cmd": [],
"name": "release.linux-arm64",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -408,7 +889,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -423,6 +904,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -442,12 +935,69 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.linux-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
},
{
+ "cmd": [],
+ "name": "release.linux-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision/cas",
+ "archive",
+ "-cas-instance",
+ "projects/example-cas-server/instances/default_instance",
+ "-dump-digest",
+ "/path/to/tmp/",
+ "-paths",
+ "[START_DIR]/gn/out:gn"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=aarch64-linux-gnu --sysroot=[START_DIR]/cipd/sysroot -static-libstdc++"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.linux-arm64.upload.upload binary to CAS",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LINK@CAS UI@https://cas-viewer.appspot.com/projects/example-cas-server/instances/default_instance/blobs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/0/tree@@@"
+ ]
+ },
+ {
"name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cq_mac.json b/gn/infra/recipes/gn.expected/cq_mac.json
index 908c60daf8a..cc3ec5f389b 100644
--- a/gn/infra/recipes/gn.expected/cq_mac.json
+++ b/gn/infra/recipes/gn.expected/cq_mac.json
@@ -10,6 +10,18 @@
"[START_DIR]/gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -65,6 +113,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -78,6 +138,18 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -97,6 +169,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -130,6 +214,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed (2)",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -157,6 +253,18 @@
"[CACHE]/macos_sdk/XCode.app"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "install xcode"
},
{
@@ -167,6 +275,18 @@
"[CACHE]/macos_sdk/XCode.app"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "select XCode"
},
{
@@ -178,11 +298,23 @@
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.xcrun sdk-path",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -194,13 +326,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.xcrun toolchain",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -212,7 +356,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"-d"
@@ -225,6 +369,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -244,6 +400,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -261,6 +429,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.mac-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -275,11 +455,23 @@
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun sdk-path",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -291,13 +483,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun toolchain",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -309,7 +513,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -323,6 +527,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -342,6 +558,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -359,21 +587,224 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
},
{
+ "cmd": [],
+ "name": "release.mac-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[recipe_engine::cas]/resources/infra.sha1",
+ "/path/to/tmp/"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.mac-amd64.upload.read infra revision",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@infra.sha1@git_revision:mock_infra_git_revision@@@",
+ "@@@STEP_LOG_END@infra.sha1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.mac-amd64.upload.install infra/tools/luci/cas",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.mac-amd64.upload.install infra/tools/luci/cas.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd",
+ "ensure",
+ "-root",
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision",
+ "-ensure-file",
+ "infra/tools/luci/cas/${platform} git_revision:mock_infra_git_revision",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.mac-amd64.upload.install infra/tools/luci/cas.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:moc\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/luci/cas/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision/cas",
+ "archive",
+ "-cas-instance",
+ "projects/example-cas-server/instances/default_instance",
+ "-dump-digest",
+ "/path/to/tmp/",
+ "-paths",
+ "[START_DIR]/gn/out:gn"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=x86_64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.mac-amd64.upload.upload binary to CAS",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LINK@CAS UI@https://cas-viewer.appspot.com/projects/example-cas-server/instances/default_instance/blobs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/0/tree@@@"
+ ]
+ },
+ {
"cmd": [
"xcrun",
"--show-sdk-path"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun sdk-path (2)",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[sdk-path]@/some/xcode/path@@@",
- "@@@STEP_LOG_END@raw_io.output[sdk-path]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[sdk-path]@/some/xcode/path@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[sdk-path]@@@"
]
},
{
@@ -385,13 +816,25 @@
"-xc++",
"-fsyntax-only",
"-Wp,-v",
- "-"
+ "/dev/null"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.xcrun toolchain (2)",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@",
- "@@@STEP_LOG_LINE@raw_io.output[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
- "@@@STEP_LOG_END@raw_io.output[toolchain]@@@"
+ "@@@STEP_LOG_LINE@raw_io.output_text[toolchain]@[CACHE]/macos_sdk/XCode.app/include/c++/v1@@@",
+ "@@@STEP_LOG_END@raw_io.output_text[toolchain]@@@"
]
},
{
@@ -403,7 +846,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]/gn/build/gen.py",
"--use-lto",
@@ -417,6 +860,18 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-arm64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -436,18 +891,87 @@
"CXX": "[START_DIR]/cipd/bin/clang++",
"LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.mac-arm64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
},
{
+ "cmd": [],
+ "name": "release.mac-arm64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]/cipd_tool/infra/tools/luci/cas/git_revision%3Amock_infra_git_revision/cas",
+ "archive",
+ "-cas-instance",
+ "projects/example-cas-server/instances/default_instance",
+ "-dump-digest",
+ "/path/to/tmp/",
+ "-paths",
+ "[START_DIR]/gn/out:gn"
+ ],
+ "cwd": "[START_DIR]/gn",
+ "env": {
+ "AR": "[START_DIR]/cipd/bin/llvm-ar",
+ "CC": "[START_DIR]/cipd/bin/clang",
+ "CFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path -nostdinc++ -cxx-isystem [CACHE]/macos_sdk/XCode.app/include/c++/v1",
+ "CXX": "[START_DIR]/cipd/bin/clang++",
+ "LDFLAGS": "--target=arm64-apple-darwin --sysroot=/some/xcode/path"
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.mac-arm64.upload.upload binary to CAS",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LINK@CAS UI@https://cas-viewer.appspot.com/projects/example-cas-server/instances/default_instance/blobs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/0/tree@@@"
+ ]
+ },
+ {
"cmd": [
"sudo",
"xcode-select",
"--reset"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "reset XCode"
},
{
diff --git a/gn/infra/recipes/gn.expected/cq_win.json b/gn/infra/recipes/gn.expected/cq_win.json
index 5612924b5b6..792e0e93ab2 100644
--- a/gn/infra/recipes/gn.expected/cq_win.json
+++ b/gn/infra/recipes/gn.expected/cq_win.json
@@ -10,6 +10,18 @@
"[START_DIR]\\gn"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.init",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -25,6 +37,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -38,6 +62,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -51,6 +87,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.rev-parse",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -65,6 +113,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -78,6 +138,18 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "git.checkout 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -97,6 +169,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -126,6 +210,18 @@
"/path/to/tmp/json"
],
"infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "ensure_installed (2)",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -143,12 +239,24 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
- "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+ "RECIPE_MODULE[recipe_engine::json]\\resources\\read.py",
"[CACHE]\\windows_sdk\\win_sdk\\bin\\SetEnv.x64.json",
"/path/to/tmp/json"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "read SetEnv.x64.json",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
@@ -186,7 +294,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]\\gn\\build\\gen.py",
"-d"
@@ -200,6 +308,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -220,6 +340,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -238,6 +370,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "debug.windows-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -256,7 +400,7 @@
},
{
"cmd": [
- "python",
+ "python3",
"-u",
"[START_DIR]\\gn\\build\\gen.py",
"--use-lto",
@@ -271,6 +415,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.generate",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -291,6 +447,18 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
@@ -309,12 +477,207 @@
"[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
]
},
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "release.windows-amd64.test",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@2@@@"
]
},
{
+ "cmd": [],
+ "name": "release.windows-amd64.upload",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@2@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "copy",
+ "RECIPE_MODULE[recipe_engine::cas]\\resources\\infra.sha1",
+ "/path/to/tmp/"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.windows-amd64.upload.read infra revision",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LOG_LINE@infra.sha1@git_revision:mock_infra_git_revision@@@",
+ "@@@STEP_LOG_END@infra.sha1@@@"
+ ]
+ },
+ {
+ "cmd": [],
+ "name": "release.windows-amd64.upload.install infra/tools/luci/cas",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "vpython3",
+ "-u",
+ "RECIPE_MODULE[recipe_engine::file]\\resources\\fileutil.py",
+ "--json-output",
+ "/path/to/tmp/json",
+ "ensure-directory",
+ "--mode",
+ "0777",
+ "[START_DIR]\\cipd_tool\\infra\\tools\\luci\\cas\\git_revision%3Amock_infra_git_revision"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.windows-amd64.upload.install infra/tools/luci/cas.ensure package directory",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "cipd.bat",
+ "ensure",
+ "-root",
+ "[START_DIR]\\cipd_tool\\infra\\tools\\luci\\cas\\git_revision%3Amock_infra_git_revision",
+ "-ensure-file",
+ "infra/tools/luci/cas/${platform} git_revision:mock_infra_git_revision",
+ "-max-threads",
+ "0",
+ "-json-output",
+ "/path/to/tmp/json"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.windows-amd64.upload.install infra/tools/luci/cas.ensure_installed",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@4@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:moc\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/luci/cas/resolved-platform\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "[START_DIR]\\cipd_tool\\infra\\tools\\luci\\cas\\git_revision%3Amock_infra_git_revision\\cas",
+ "archive",
+ "-cas-instance",
+ "projects/example-cas-server/instances/default_instance",
+ "-dump-digest",
+ "/path/to/tmp/",
+ "-paths",
+ "[START_DIR]\\gn\\out:gn.exe"
+ ],
+ "cwd": "[START_DIR]\\gn",
+ "env": {
+ "VSINSTALLDIR": "[CACHE]\\windows_sdk"
+ },
+ "env_prefixes": {
+ "PATH": [
+ "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
+ ]
+ },
+ "infra_step": true,
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
+ "name": "release.windows-amd64.upload.upload binary to CAS",
+ "~followup_annotations": [
+ "@@@STEP_NEST_LEVEL@3@@@",
+ "@@@STEP_LINK@CAS UI@https://cas-viewer.appspot.com/projects/example-cas-server/instances/default_instance/blobs/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/0/tree@@@"
+ ]
+ },
+ {
"cmd": [
"taskkill.exe",
"/f",
@@ -322,6 +685,18 @@
"/im",
"mspdbsrv.exe"
],
+ "luci_context": {
+ "realm": {
+ "name": "gn:try"
+ },
+ "resultdb": {
+ "current_invocation": {
+ "name": "invocations/build:8945511751514863184",
+ "update_token": "token"
+ },
+ "hostname": "rdbhost"
+ }
+ },
"name": "taskkill mspdbsrv"
},
{
diff --git a/gn/infra/recipes/gn.py b/gn/infra/recipes/gn.py
index 6ba79ae2823..7cb6229f6ba 100644
--- a/gn/infra/recipes/gn.py
+++ b/gn/infra/recipes/gn.py
@@ -7,6 +7,7 @@ from recipe_engine.recipe_api import Property
DEPS = [
'recipe_engine/buildbucket',
+ 'recipe_engine/cas',
'recipe_engine/cipd',
'recipe_engine/context',
'recipe_engine/file',
@@ -14,7 +15,6 @@ DEPS = [
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
- 'recipe_engine/python',
'recipe_engine/raw_io',
'recipe_engine/step',
'target',
@@ -28,7 +28,8 @@ PROPERTIES = {
# On select platforms, link the GN executable against rpmalloc for a small 10% speed boost.
RPMALLOC_GIT_URL = 'https://fuchsia.googlesource.com/third_party/github.com/mjansson/rpmalloc'
-RPMALLOC_REVISION = 'f4b7c52c858675f732a76bd1c73447e0fcf84b1e'
+RPMALLOC_BRANCH = '+upstream/develop'
+RPMALLOC_REVISION = '668a7f81b588a985c6528b70674dbcc005d9cb75'
# Used to convert os and arch strings to rpmalloc format
RPMALLOC_MAP = {
@@ -42,10 +43,10 @@ def _get_libcxx_include_path(api):
lines = api.step(
'xcrun toolchain', [
'xcrun', '--toolchain', 'clang', 'clang++', '-xc++', '-fsyntax-only',
- '-Wp,-v', '-'
+ '-Wp,-v', '/dev/null'
],
- stderr=api.raw_io.output(name='toolchain', add_output_log=True),
- step_test_data=lambda: api.raw_io.test_api.stream_output(
+ stderr=api.raw_io.output_text(name='toolchain', add_output_log=True),
+ step_test_data=lambda: api.raw_io.test_api.stream_output_text(
str(api.macos_sdk.sdk_dir.join('include', 'c++', 'v1')),
stream='stderr')).stderr.splitlines()
# Iterate over all include paths and look for the SDK libc++ one.
@@ -72,8 +73,8 @@ def _get_compilation_environment(api, target, cipd_dir):
triple = '--target=%s' % target.triple
sysroot = '--sysroot=%s' % api.step(
'xcrun sdk-path', ['xcrun', '--show-sdk-path'],
- stdout=api.raw_io.output(name='sdk-path', add_output_log=True),
- step_test_data=lambda: api.raw_io.test_api.stream_output(
+ stdout=api.raw_io.output_text(name='sdk-path', add_output_log=True),
+ step_test_data=lambda: api.raw_io.test_api.stream_output_text(
'/some/xcode/path')).stdout.strip()
stdlib = cipd_dir.join('lib', 'libc++.a')
cxx_include = _get_libcxx_include_path(api)
@@ -117,7 +118,7 @@ def RunSteps(api, repository):
api.step('checkout', ['git', 'checkout', 'FETCH_HEAD'])
revision = api.step(
'rev-parse', ['git', 'rev-parse', 'HEAD'],
- stdout=api.raw_io.output()).stdout.strip()
+ stdout=api.raw_io.output_text()).stdout.strip()
for change in build_input.gerrit_changes:
api.step('fetch %s/%s' % (change.change, change.patchset), [
'git', 'fetch', repository,
@@ -195,8 +196,22 @@ def RunSteps(api, repository):
with api.context(cwd=rpmalloc_src_dir, infra_steps=True):
api.step(
'fetch',
- ['git', 'fetch', '--tags', RPMALLOC_GIT_URL, RPMALLOC_REVISION])
- api.step('checkout', ['git', 'checkout', 'FETCH_HEAD'])
+ ['git', 'fetch', '--tags', RPMALLOC_GIT_URL, RPMALLOC_BRANCH])
+ api.step('checkout', ['git', 'checkout', RPMALLOC_REVISION])
+
+ # Patch configure.py since to add -Wno-unsafe-buffer-usage since Clang-16 will
+ # now complain about this when building rpmalloc (latest version only
+ # supports clang-15).
+ build_ninja_clang_path = api.path.join(rpmalloc_src_dir, 'build/ninja/clang.py')
+ build_ninja_clang_py = api.file.read_text('read %s' % build_ninja_clang_path,
+ build_ninja_clang_path,
+ "CXXFLAGS = ['-Wall', '-Weverything', '-Wfoo']")
+ build_ninja_clang_py = build_ninja_clang_py.replace(
+ "'-Wno-disabled-macro-expansion'",
+ "'-Wno-disabled-macro-expansion', '-Wno-unsafe-buffer-usage'")
+ api.file.write_text('write %s' % build_ninja_clang_path,
+ build_ninja_clang_path,
+ build_ninja_clang_py)
for platform in all_config_platforms:
# Convert target architecture and os to rpmalloc format.
@@ -209,10 +224,10 @@ def RunSteps(api, repository):
cipd_dir)
with api.step.nest('build rpmalloc-' + platform), api.context(
env=env, cwd=rpmalloc_src_dir):
- api.python(
+ api.step(
'configure',
- rpmalloc_src_dir.join('configure.py'),
- args=['-c', 'release', '-a', rpmalloc_arch, '--lto'])
+ ['python3', '-u', rpmalloc_src_dir.join('configure.py')] +
+ ['-c', 'release', '-a', rpmalloc_arch, '--lto'])
# NOTE: Only build the static library.
rpmalloc_static_lib = api.path.join('lib', rpmalloc_os, 'release',
@@ -235,7 +250,8 @@ def RunSteps(api, repository):
'--link-lib=%s' % rpmalloc_static_libs[target.platform]
]
- api.python('generate', src_dir.join('build', 'gen.py'), args=args)
+ api.step('generate',
+ ['python3', '-u', src_dir.join('build', 'gen.py')] + args)
# Windows requires the environment modifications when building too.
api.step('build',
@@ -245,16 +261,20 @@ def RunSteps(api, repository):
if target.is_host:
api.step('test', [src_dir.join('out', 'gn_unittests')])
- if build_input.gerrit_changes:
- continue
-
if config['name'] != 'release':
continue
with api.step.nest('upload'):
- cipd_pkg_name = 'gn/gn/%s' % target.platform
gn = 'gn' + ('.exe' if target.is_win else '')
+ if build_input.gerrit_changes:
+ # Upload to CAS from CQ.
+ api.cas.archive('upload binary to CAS', src_dir.join('out'),
+ src_dir.join('out', gn))
+ continue
+
+ cipd_pkg_name = 'gn/gn/%s' % target.platform
+
pkg_def = api.cipd.PackageDefinition(
package_name=cipd_pkg_name,
package_root=src_dir.join('out'),
@@ -306,7 +326,7 @@ def GenTests(api):
git_repo='gn.googlesource.com/gn',
revision='a' * 40,
) + api.step_data(
- 'git.rev-parse', api.raw_io.stream_output('a' * 40)
+ 'git.rev-parse', api.raw_io.stream_output_text('a' * 40)
) + api.step_data(
'release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:' +
'a' * 40,
@@ -318,7 +338,7 @@ def GenTests(api):
git_repo='gn.googlesource.com/gn',
revision='a' * 40,
) + api.step_data(
- 'git.rev-parse', api.raw_io.stream_output('a' * 40)
+ 'git.rev-parse', api.raw_io.stream_output_text('a' * 40)
) + api.step_data(
'release.linux-amd64.upload.cipd search gn/gn/linux-amd64 git_revision:' +
'a' * 40, api.cipd.example_search('gn/gn/linux-amd64', [])))
diff --git a/gn/misc/vim/syntax/gn.vim b/gn/misc/vim/syntax/gn.vim
index 851cec6bf59..df2d0d8dc26 100644
--- a/gn/misc/vim/syntax/gn.vim
+++ b/gn/misc/vim/syntax/gn.vim
@@ -43,7 +43,7 @@ hi def link gnFunctions Macro
" Variables
syn keyword gnVariable all_dependent_configs allow_circular_includes_from
-syn keyword gnVariable args asmflags assert_no_deps
+syn keyword gnVariable args arflags asmflags assert_no_deps
syn keyword gnVariable cflags cflags_c cflags_cc cflags_objc cflags_objcc
syn keyword gnVariable check_includes complete_static_lib configs
syn keyword gnVariable data data_deps data_keys defines depfile deps
diff --git a/gn/src/base/command_line.cc b/gn/src/base/command_line.cc
index 68bc58719d5..0c37d530dfa 100644
--- a/gn/src/base/command_line.cc
+++ b/gn/src/base/command_line.cc
@@ -184,6 +184,17 @@ CommandLine& CommandLine::operator=(const CommandLine& other) = default;
CommandLine::~CommandLine() = default;
#if defined(OS_WIN)
+
+// static
+std::string CommandLine::StringTypeToUTF8(const StringType& input) {
+ return UTF16ToUTF8(input);
+}
+
+// static
+CommandLine::StringType CommandLine::UTF8ToStringType(std::string_view input) {
+ return UTF8ToUTF16(input);
+}
+
// static
void CommandLine::set_slash_is_not_a_switch() {
// The last switch prefix should be slash, so adjust the size to skip it.
@@ -202,6 +213,19 @@ void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
argv_vector.push_back(UTF8ToUTF16(argv[i]));
current_process_commandline_->InitFromArgv(argv_vector);
}
+
+#else
+
+// static
+std::string CommandLine::StringTypeToUTF8(const StringType& input) {
+ return input;
+}
+
+// static
+CommandLine::StringType CommandLine::UTF8ToStringType(std::string_view input) {
+ return CommandLine::StringType(input);
+}
+
#endif
// static
@@ -292,18 +316,9 @@ bool CommandLine::HasSwitch(const char switch_constant[]) const {
return HasSwitch(std::string_view(switch_constant));
}
-std::string CommandLine::GetSwitchValueASCII(
+std::string CommandLine::GetSwitchValueString(
std::string_view switch_string) const {
- StringType value = GetSwitchValueNative(switch_string);
- if (!IsStringASCII(value)) {
- DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
- return std::string();
- }
-#if defined(OS_WIN)
- return UTF16ToASCII(value);
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- return value;
-#endif
+ return StringTypeToUTF8(GetSwitchValueNative(switch_string));
}
FilePath CommandLine::GetSwitchValuePath(std::string_view switch_string) const {
@@ -313,8 +328,43 @@ FilePath CommandLine::GetSwitchValuePath(std::string_view switch_string) const {
CommandLine::StringType CommandLine::GetSwitchValueNative(
std::string_view switch_string) const {
DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
- auto result = switches_.find(switch_string);
- return result == switches_.end() ? StringType() : result->second;
+
+ // There can be multiple matches, we want to find the last one.
+ auto iter = switches_.upper_bound(switch_string);
+ if (iter == switches_.begin())
+ return StringType();
+
+ // We want the item right before the upper bound, if it's a match.
+ --iter;
+ if (iter->first == switch_string)
+ return iter->second;
+ return StringType();
+}
+
+std::vector<std::string> CommandLine::GetSwitchValueStrings(
+ std::string_view switch_string) const {
+ std::vector<StringType> matches = GetSwitchValuesNative(switch_string);
+
+ std::vector<std::string> result;
+ result.reserve(matches.size());
+
+ for (const StringType& cur : matches) {
+ result.push_back(StringTypeToUTF8(cur));
+ }
+ return result;
+}
+
+std::vector<CommandLine::StringType> CommandLine::GetSwitchValuesNative(
+ std::string_view switch_string) const {
+ std::vector<StringType> result;
+
+ auto [iter, end] = switches_.equal_range(switch_string);
+ while (iter != end) {
+ result.push_back(iter->second);
+ ++iter;
+ }
+
+ return result;
}
void CommandLine::AppendSwitch(const std::string& switch_string) {
@@ -335,11 +385,10 @@ void CommandLine::AppendSwitchNative(const std::string& switch_string,
const std::string& switch_key = switch_string;
StringType combined_switch_string(switch_key);
#endif
+
size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
- auto insertion =
- switches_.insert(make_pair(switch_key.substr(prefix_length), value));
- if (!insertion.second)
- insertion.first->second = value;
+ switches_.insert(make_pair(switch_key.substr(prefix_length), value));
+
// Preserve existing switch prefixes in |argv_|; only append one if necessary.
if (prefix_length == 0)
combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
@@ -349,15 +398,9 @@ void CommandLine::AppendSwitchNative(const std::string& switch_string,
argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
}
-void CommandLine::AppendSwitchASCII(const std::string& switch_string,
- const std::string& value_string) {
-#if defined(OS_WIN)
- AppendSwitchNative(switch_string, ASCIIToUTF16(value_string));
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
- AppendSwitchNative(switch_string, value_string);
-#else
-#error Unsupported platform
-#endif
+void CommandLine::AppendSwitch(const std::string& switch_string,
+ const std::string& value_string) {
+ AppendSwitchNative(switch_string, UTF8ToStringType(value_string));
}
void CommandLine::CopySwitchesFrom(const CommandLine& source,
diff --git a/gn/src/base/command_line.h b/gn/src/base/command_line.h
index 3a4434666cc..ba30749e63d 100644
--- a/gn/src/base/command_line.h
+++ b/gn/src/base/command_line.h
@@ -31,6 +31,7 @@ class CommandLine {
public:
#if defined(OS_WIN)
// The native command line string type.
+ // See StringTypeToUTF8() and UTF8ToStringType() below.
using StringType = std::u16string;
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
using StringType = std::string;
@@ -38,7 +39,7 @@ class CommandLine {
using CharType = StringType::value_type;
using StringVector = std::vector<StringType>;
- using SwitchMap = std::map<std::string, StringType, std::less<>>;
+ using SwitchMap = std::multimap<std::string, StringType, std::less<>>;
// A constructor for CommandLines that only carry switches and arguments.
enum NoProgram { NO_PROGRAM };
@@ -57,6 +58,10 @@ class CommandLine {
~CommandLine();
+ // Converts the platform string type to/from UTF-8.
+ static std::string StringTypeToUTF8(const StringType& input);
+ static StringType UTF8ToStringType(std::string_view input);
+
#if defined(OS_WIN)
// By default this class will treat command-line arguments beginning with
// slashes as switches on Windows, but not other platforms.
@@ -167,24 +172,35 @@ class CommandLine {
bool HasSwitch(std::string_view switch_string) const;
bool HasSwitch(const char switch_constant[]) const;
- // Returns the value associated with the given switch. If the switch has no
- // value or isn't present, this method returns the empty string.
- // Switch names must be lowercase.
- std::string GetSwitchValueASCII(std::string_view switch_string) const;
+ // Returns the last value associated with the given switch. If the switch has
+ // no value or isn't present, this method returns the empty string. Switch
+ // names must be lowercase.
+ //
+ // The "string" version returns an 8-bit representation. On Windows, this will
+ // convert to UTF-8, on 8-bit systems it will return the raw input.
+ std::string GetSwitchValueString(std::string_view switch_string) const;
FilePath GetSwitchValuePath(std::string_view switch_string) const;
StringType GetSwitchValueNative(std::string_view switch_string) const;
+ // Returns all values associated with the given switch.
+ std::vector<std::string> GetSwitchValueStrings(
+ std::string_view switch_string) const;
+ std::vector<StringType> GetSwitchValuesNative(
+ std::string_view switch_string) const;
+
// Get a copy of all switches, along with their values.
const SwitchMap& GetSwitches() const { return switches_; }
// Append a switch [with optional value] to the command line.
// Note: Switches will precede arguments regardless of appending order.
+ //
+ // The version that takes an 8-bit switch value converts from UTF-8 on
+ // Windows.
void AppendSwitch(const std::string& switch_string);
void AppendSwitchPath(const std::string& switch_string, const FilePath& path);
void AppendSwitchNative(const std::string& switch_string,
const StringType& value);
- void AppendSwitchASCII(const std::string& switch_string,
- const std::string& value);
+ void AppendSwitch(const std::string& switch_string, const std::string& value);
// Copy a set of switches (and any values) from another command line.
// Commonly used when launching a subprocess.
diff --git a/gn/src/base/compiler_specific.h b/gn/src/base/compiler_specific.h
index ec139d26130..dd46c98b077 100644
--- a/gn/src/base/compiler_specific.h
+++ b/gn/src/base/compiler_specific.h
@@ -18,9 +18,9 @@
#endif // COMPILER_MSVC
-#if COMPILER_GCC && defined(NDEBUG)
+#if defined(COMPILER_GCC) && defined(NDEBUG)
#define ALWAYS_INLINE inline __attribute__((__always_inline__))
-#elif COMPILER_MSVC && defined(NDEBUG)
+#elif defined(COMPILER_MSVC) && defined(NDEBUG)
#define ALWAYS_INLINE __forceinline
#else
#define ALWAYS_INLINE inline
diff --git a/gn/src/base/files/file.cc b/gn/src/base/files/file.cc
index cecb76dba19..020c40f8d80 100644
--- a/gn/src/base/files/file.cc
+++ b/gn/src/base/files/file.cc
@@ -23,6 +23,14 @@ File::File(const FilePath& path, uint32_t flags)
Initialize(path, flags);
}
+File::File(ScopedPlatformFile platform_file)
+ : file_(std::move(platform_file)),
+ error_details_(FILE_OK) {
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ DCHECK_GE(file_.get(), -1);
+#endif
+}
+
File::File(PlatformFile platform_file)
: file_(platform_file),
error_details_(FILE_OK) {
diff --git a/gn/src/base/files/file.h b/gn/src/base/files/file.h
index 2c94eb4eff7..419ba870b42 100644
--- a/gn/src/base/files/file.h
+++ b/gn/src/base/files/file.h
@@ -41,10 +41,12 @@ typedef struct stat64 stat_wrapper_t;
class File {
public:
// FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one
- // of the five (possibly combining with other flags) when opening or creating
+ // of the three (possibly combining with other flags) when opening or creating
// a file.
enum Flags {
FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
+ FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
+ // already exist.
FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
FLAG_READ = 1 << 4,
FLAG_WRITE = 1 << 5,
@@ -122,6 +124,7 @@ class File {
File(const FilePath& path, uint32_t flags);
// Takes ownership of |platform_file|.
+ explicit File(ScopedPlatformFile platform_file);
explicit File(PlatformFile platform_file);
// Creates an object with a specific error_details code.
diff --git a/gn/src/base/files/file_posix.cc b/gn/src/base/files/file_posix.cc
index b1f9f5e86f3..a1d256cb615 100644
--- a/gn/src/base/files/file_posix.cc
+++ b/gn/src/base/files/file_posix.cc
@@ -330,6 +330,8 @@ void File::DoInitialize(const FilePath& path, uint32_t flags) {
DCHECK(!IsValid());
int open_flags = 0;
+ if (flags & FLAG_CREATE)
+ open_flags = O_CREAT | O_EXCL;
if (flags & FLAG_CREATE_ALWAYS) {
DCHECK(!open_flags);
diff --git a/gn/src/base/files/file_util.h b/gn/src/base/files/file_util.h
index b44129d8fee..dbb69ec4a5a 100644
--- a/gn/src/base/files/file_util.h
+++ b/gn/src/base/files/file_util.h
@@ -124,6 +124,15 @@ bool ReadFileToStringWithMaxSize(const FilePath& path,
std::string* contents,
size_t max_size);
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+// Performs the same function as CreateAndOpenTemporaryFileInDir(), but
+// returns the file-descriptor wrapped in a ScopedFD rather than a File.
+ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& dir,
+ FilePath* path);
+
+#endif
+
#if defined(OS_POSIX)
// Creates a symbolic link at |symlink| pointing to |target|. Returns
@@ -173,6 +182,10 @@ bool IsDirectoryEmpty(const FilePath& dir_path);
// Get the temporary directory provided by the system.
bool GetTempDir(FilePath* path);
+// Returns a new temporary file in |dir| with a unique name. On success,
+// |temp_file| is populated with the full path to the created file.
+File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file);
+
// Create a new directory. If prefix is provided, the new directory name is in
// the format of prefixyyyy.
// NOTE: prefix is ignored in the POSIX implementation.
diff --git a/gn/src/base/files/file_util_posix.cc b/gn/src/base/files/file_util_posix.cc
index ac281c44359..b6181511929 100644
--- a/gn/src/base/files/file_util_posix.cc
+++ b/gn/src/base/files/file_util_posix.cc
@@ -274,6 +274,16 @@ bool DirectoryExists(const FilePath& path) {
return S_ISDIR(file_info.st_mode);
}
+ScopedFD CreateAndOpenFdForTemporaryFileInDir(const FilePath& directory,
+ FilePath* path) {
+ *path = directory.Append(TempFileName());
+ const std::string& tmpdir_string = path->value();
+ // this should be OK since mkstemp just replaces characters in place
+ char* buffer = const_cast<char*>(tmpdir_string.c_str());
+
+ return ScopedFD(HANDLE_EINTR(mkstemp(buffer)));
+}
+
#if !defined(OS_FUCHSIA)
bool CreateSymbolicLink(const FilePath& target_path,
const FilePath& symlink_path) {
@@ -376,6 +386,11 @@ FilePath GetHomeDir() {
}
#endif // !defined(OS_MACOSX)
+File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+ ScopedFD fd = CreateAndOpenFdForTemporaryFileInDir(dir, temp_file);
+ return fd.is_valid() ? File(std::move(fd)) : File(File::GetLastFileError());
+}
+
static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
const FilePath::StringType& name_tmpl,
FilePath* new_dir) {
@@ -444,7 +459,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path, File::Error* error) {
i != subpaths.rend(); ++i) {
if (DirectoryExists(*i))
continue;
- if (mkdir(i->value().c_str(), 0700) == 0)
+ if (mkdir(i->value().c_str(), 0777) == 0)
continue;
// Mkdir failed, but it might have failed with EEXIST, or some other error
// due to the the directory appearing out of thin air. This can occur if
diff --git a/gn/src/base/files/file_util_win.cc b/gn/src/base/files/file_util_win.cc
index 9f4bf5b4557..c808bdebcaa 100644
--- a/gn/src/base/files/file_util_win.cc
+++ b/gn/src/base/files/file_util_win.cc
@@ -283,6 +283,50 @@ bool GetTempDir(FilePath* path) {
return true;
}
+File CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+ constexpr uint32_t kFlags =
+ File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE;
+
+ // Use GUID instead of ::GetTempFileName() to generate unique file names.
+ // "Due to the algorithm used to generate file names, GetTempFileName can
+ // perform poorly when creating a large number of files with the same prefix.
+ // In such cases, it is recommended that you construct unique file names based
+ // on GUIDs."
+ // https://msdn.microsoft.com/library/windows/desktop/aa364991.aspx
+
+ FilePath temp_name;
+ File file;
+
+ // Although it is nearly impossible to get a duplicate name with GUID, we
+ // still use a loop here in case it happens.
+ for (int i = 0; i < 100; ++i) {
+ temp_name = dir.Append(
+ FilePath(UTF8ToUTF16(GenerateGUID()) + FILE_PATH_LITERAL(".tmp")));
+ file.Initialize(temp_name, kFlags);
+ if (file.IsValid())
+ break;
+ }
+
+ if (!file.IsValid()) {
+ DPLOG(WARNING) << "Failed to get temporary file name in "
+ << UTF16ToUTF8(dir.value());
+ return file;
+ }
+
+ char16_t long_temp_name[MAX_PATH + 1];
+ const DWORD long_name_len = GetLongPathName(
+ ToWCharT(temp_name.value().c_str()), ToWCharT(long_temp_name), MAX_PATH);
+ if (long_name_len != 0 && long_name_len <= MAX_PATH) {
+ *temp_file =
+ FilePath(FilePath::StringViewType(long_temp_name, long_name_len));
+ } else {
+ // GetLongPathName() failed, but we still have a temporary file.
+ *temp_file = std::move(temp_name);
+ }
+
+ return file;
+}
+
bool CreateTemporaryDirInDir(const FilePath& base_dir,
const FilePath::StringType& prefix,
FilePath* new_dir) {
diff --git a/gn/src/base/files/file_win.cc b/gn/src/base/files/file_win.cc
index b68370b84b4..c58e071f067 100644
--- a/gn/src/base/files/file_win.cc
+++ b/gn/src/base/files/file_win.cc
@@ -274,6 +274,11 @@ void File::DoInitialize(const FilePath& path, uint32_t flags) {
if (flags & FLAG_OPEN)
disposition = OPEN_EXISTING;
+ if (flags & FLAG_CREATE) {
+ DCHECK(!disposition);
+ disposition = CREATE_NEW;
+ }
+
if (flags & FLAG_CREATE_ALWAYS) {
DCHECK(!disposition);
DCHECK(flags & FLAG_WRITE);
diff --git a/gn/src/base/logging.cc b/gn/src/base/logging.cc
index c8673e4fe96..e8929c17da2 100644
--- a/gn/src/base/logging.cc
+++ b/gn/src/base/logging.cc
@@ -77,7 +77,7 @@ const int kAlwaysPrintErrorLevel = LOG_ERROR;
} // namespace
-#if DCHECK_IS_CONFIGURABLE
+#if defined(DCHECK_IS_CONFIGURABLE)
// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be
// determined at run-time. We default it to INFO, to avoid it triggering
// crashes before the run-time has explicitly chosen the behaviour.
diff --git a/gn/src/base/logging.h b/gn/src/base/logging.h
index 2b1af7ff4f5..18f827f04f2 100644
--- a/gn/src/base/logging.h
+++ b/gn/src/base/logging.h
@@ -607,7 +607,7 @@ DEFINE_CHECK_OP_IMPL(GT, >)
#if DCHECK_IS_ON()
-#if DCHECK_IS_CONFIGURABLE
+#if defined(DCHECK_IS_CONFIGURABLE)
extern LogSeverity LOG_DCHECK;
#else
const LogSeverity LOG_DCHECK = LOG_FATAL;
diff --git a/gn/src/base/numerics/clamped_math.h b/gn/src/base/numerics/clamped_math.h
index 9e8354353a6..88e0e3afc03 100644
--- a/gn/src/base/numerics/clamped_math.h
+++ b/gn/src/base/numerics/clamped_math.h
@@ -233,12 +233,12 @@ BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <);
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=);
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >);
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=);
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==);
-BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=);
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
} // namespace internal
diff --git a/gn/src/base/numerics/safe_conversions.h b/gn/src/base/numerics/safe_conversions.h
index 35c40897862..32fb5a92633 100644
--- a/gn/src/base/numerics/safe_conversions.h
+++ b/gn/src/base/numerics/safe_conversions.h
@@ -310,14 +310,14 @@ std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
typename UnderlyingType<R>::type>(lhs, rhs); \
}
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <);
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=);
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >);
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=);
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==);
-BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=);
-
-}; // namespace internal
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
+
+} // namespace internal
using internal::as_signed;
using internal::as_unsigned;
diff --git a/gn/src/base/third_party/icu/README.chromium b/gn/src/base/third_party/icu/README.chromium
index 297e89a2edd..4f398d9fa9c 100644
--- a/gn/src/base/third_party/icu/README.chromium
+++ b/gn/src/base/third_party/icu/README.chromium
@@ -6,12 +6,12 @@ License File: NOT_SHIPPED
This file has the relevant components from ICU copied to handle basic UTF8/16/32
conversions. Components are copied from umachine.h, utf.h, utf8.h, and utf16.h
-into icu_utf.h, and from utf_impl.cpp into icu_utf.cc.
+into icu_utf.h.
-The main change is that U_/U8_/U16_ prefixes have been replaced with
-CBU_/CBU8_/CBU16_ (for "Chrome Base") to avoid confusion with the "real" ICU
-macros should ICU be in use on the system. For the same reason, the functions
-and types have been put in the "base_icu" namespace.
+The main change is that U_/U8_/U16_/UPRV_ prefixes have been replaced with
+CBU_/CBU8_/CBU16_/CBUPRV_ (for "Chrome Base") to avoid confusion with the "real"
+ICU macros should ICU be in use on the system. For the same reason, the
+functions and types have been put in the "base_icu" namespace.
Note that this license file is marked as NOT_SHIPPED, since a more complete
ICU license is included from //third_party/icu/README.chromium
diff --git a/gn/src/base/third_party/icu/icu_utf.cc b/gn/src/base/third_party/icu/icu_utf.cc
deleted file mode 100644
index 8dcb401fd9a..00000000000
--- a/gn/src/base/third_party/icu/icu_utf.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// © 2016 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-/*
-******************************************************************************
-*
-* Copyright (C) 1999-2012, International Business Machines
-* Corporation and others. All Rights Reserved.
-*
-******************************************************************************
-* file name: utf_impl.cpp
-* encoding: UTF-8
-* tab size: 8 (not used)
-* indentation:4
-*
-* created on: 1999sep13
-* created by: Markus W. Scherer
-*
-* This file provides implementation functions for macros in the utfXX.h
-* that would otherwise be too long as macros.
-*/
-
-#include "base/third_party/icu/icu_utf.h"
-
-namespace base_icu {
-
-// source/common/utf_impl.cpp
-
-static const UChar32 utf8_errorValue[6] = {
- // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE,
- // but without relying on the obsolete unicode/utf_old.h.
- 0x15, 0x9f, 0xffff, 0x10ffff};
-
-static UChar32 errorValue(int32_t count, int8_t strict) {
- if (strict >= 0) {
- return utf8_errorValue[count];
- } else if (strict == -3) {
- return 0xfffd;
- } else {
- return CBU_SENTINEL;
- }
-}
-
-/*
- * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros
- * and their obsolete sibling UTF8_NEXT_CHAR_SAFE().
- *
- * U8_NEXT() supports NUL-terminated strings indicated via length<0.
- *
- * The "strict" parameter controls the error behavior:
- * <0 "Safe" behavior of U8_NEXT():
- * -1: All illegal byte sequences yield U_SENTINEL=-1.
- * -2: Same as -1, except for lenient treatment of surrogate code points as
- * legal. Some implementations use this for roundtripping of Unicode 16-bit
- * strings that are not well-formed UTF-16, that is, they contain
- * unpaired surrogates. -3: All illegal byte sequences yield U+FFFD. 0
- * Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE): All illegal
- * byte sequences yield a positive code point such that this result code
- * point would be encoded with the same number of bytes as the illegal
- * sequence. >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(...,
- * TRUE): Same as the obsolete "safe" behavior, but non-characters are also
- * treated like illegal sequences.
- *
- * Note that a UBool is the same as an int8_t.
- */
-UChar32 utf8_nextCharSafeBody(const uint8_t* s,
- int32_t* pi,
- int32_t length,
- UChar32 c,
- UBool strict) {
- // *pi is one after byte c.
- int32_t i = *pi;
- // length can be negative for NUL-terminated strings: Read and validate one
- // byte at a time.
- if (i == length || c > 0xf4) {
- // end of string, or not a lead byte
- } else if (c >= 0xf0) {
- // Test for 4-byte sequences first because
- // U8_NEXT() handles shorter valid sequences inline.
- uint8_t t1 = s[i], t2, t3;
- c &= 7;
- if (CBU8_IS_VALID_LEAD4_AND_T1(c, t1) && ++i != length &&
- (t2 = s[i] - 0x80) <= 0x3f && ++i != length &&
- (t3 = s[i] - 0x80) <= 0x3f) {
- ++i;
- c = (c << 18) | ((t1 & 0x3f) << 12) | (t2 << 6) | t3;
- // strict: forbid non-characters like U+fffe
- if (strict <= 0 || !CBU_IS_UNICODE_NONCHAR(c)) {
- *pi = i;
- return c;
- }
- }
- } else if (c >= 0xe0) {
- c &= 0xf;
- if (strict != -2) {
- uint8_t t1 = s[i], t2;
- if (CBU8_IS_VALID_LEAD3_AND_T1(c, t1) && ++i != length &&
- (t2 = s[i] - 0x80) <= 0x3f) {
- ++i;
- c = (c << 12) | ((t1 & 0x3f) << 6) | t2;
- // strict: forbid non-characters like U+fffe
- if (strict <= 0 || !CBU_IS_UNICODE_NONCHAR(c)) {
- *pi = i;
- return c;
- }
- }
- } else {
- // strict=-2 -> lenient: allow surrogates
- uint8_t t1 = s[i] - 0x80, t2;
- if (t1 <= 0x3f && (c > 0 || t1 >= 0x20) && ++i != length &&
- (t2 = s[i] - 0x80) <= 0x3f) {
- *pi = i + 1;
- return (c << 12) | (t1 << 6) | t2;
- }
- }
- } else if (c >= 0xc2) {
- uint8_t t1 = s[i] - 0x80;
- if (t1 <= 0x3f) {
- *pi = i + 1;
- return ((c - 0xc0) << 6) | t1;
- }
- } // else 0x80<=c<0xc2 is not a lead byte
-
- /* error handling */
- c = errorValue(i - *pi, strict);
- *pi = i;
- return c;
-}
-
-} // namespace base_icu
diff --git a/gn/src/base/third_party/icu/icu_utf.h b/gn/src/base/third_party/icu/icu_utf.h
index b626b398404..16792c49040 100644
--- a/gn/src/base/third_party/icu/icu_utf.h
+++ b/gn/src/base/third_party/icu/icu_utf.h
@@ -60,6 +60,25 @@ typedef int32_t UChar32;
*/
#define CBU_SENTINEL (-1)
+/**
+ * \def UPRV_BLOCK_MACRO_BEGIN
+ * Defined as the "do" keyword by default.
+ * @internal
+ */
+#ifndef CBUPRV_BLOCK_MACRO_BEGIN
+#define CBUPRV_BLOCK_MACRO_BEGIN do
+#endif
+
+/**
+ * \def UPRV_BLOCK_MACRO_END
+ * Defined as "while (FALSE)" by default.
+ * @internal
+ */
+#ifndef CBUPRV_BLOCK_MACRO_END
+#define CBUPRV_BLOCK_MACRO_END while (0)
+#endif
+
+
// source/common/unicode/utf.h
/**
@@ -68,9 +87,9 @@ typedef int32_t UChar32;
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU_IS_UNICODE_NONCHAR(c) \
- ((c) >= 0xfdd0 && ((c) <= 0xfdef || ((c)&0xfffe) == 0xfffe) && \
- (c) <= 0x10ffff)
+#define CBU_IS_UNICODE_NONCHAR(c) \
+ ((c)>=0xfdd0 && \
+ ((c)<=0xfdef || ((c)&0xfffe)==0xfffe) && (c)<=0x10ffff)
/**
* Is c a Unicode code point value (0..U+10ffff)
@@ -78,9 +97,9 @@ typedef int32_t UChar32;
*
* Code points that are not characters include:
* - single surrogate code points (U+d800..U+dfff, 2048 code points)
- * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code
- * points) - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) - the highest
- * Unicode code point value is U+10ffff
+ * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points)
+ * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points)
+ * - the highest Unicode code point value is U+10ffff
*
* This means that all code points below U+d800 are character code points,
* and that boundary is tested first for performance.
@@ -90,8 +109,8 @@ typedef int32_t UChar32;
* @stable ICU 2.4
*/
#define CBU_IS_UNICODE_CHAR(c) \
- ((uint32_t)(c) < 0xd800 || \
- (0xdfff < (c) && (c) <= 0x10ffff && !CBU_IS_UNICODE_NONCHAR(c)))
+ ((uint32_t)(c)<0xd800 || \
+ (0xdfff<(c) && (c)<=0x10ffff && !CBU_IS_UNICODE_NONCHAR(c)))
/**
* Is this code point a surrogate (U+d800..U+dfff)?
@@ -99,7 +118,7 @@ typedef int32_t UChar32;
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU_IS_SURROGATE(c) (((c)&0xfffff800) == 0xd800)
+#define CBU_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)
/**
* Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
@@ -108,69 +127,43 @@ typedef int32_t UChar32;
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400) == 0)
+#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
// source/common/unicode/utf8.h
/**
- * Internal bit vector for 3-byte UTF-8 validity check, for use in
- * U8_IS_VALID_LEAD3_AND_T1. Each bit indicates whether one lead byte + first
- * trail byte pair starts a valid sequence. Lead byte E0..EF bits 3..0 are used
- * as byte index, first trail byte bits 7..5 are used as bit index into that
- * byte.
+ * Internal bit vector for 3-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD3_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * Lead byte E0..EF bits 3..0 are used as byte index,
+ * first trail byte bits 7..5 are used as bit index into that byte.
* @see U8_IS_VALID_LEAD3_AND_T1
* @internal
*/
-#define CBU8_LEAD3_T1_BITS \
- "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
+#define CBU8_LEAD3_T1_BITS "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
/**
* Internal 3-byte UTF-8 validity check.
- * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid
- * sequence.
+ * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid sequence.
* @internal
*/
-#define CBU8_IS_VALID_LEAD3_AND_T1(lead, t1) \
- (CBU8_LEAD3_T1_BITS[(lead)&0xf] & (1 << ((uint8_t)(t1) >> 5)))
+#define CBU8_IS_VALID_LEAD3_AND_T1(lead, t1) (CBU8_LEAD3_T1_BITS[(lead)&0xf]&(1<<((uint8_t)(t1)>>5)))
/**
- * Internal bit vector for 4-byte UTF-8 validity check, for use in
- * U8_IS_VALID_LEAD4_AND_T1. Each bit indicates whether one lead byte + first
- * trail byte pair starts a valid sequence. First trail byte bits 7..4 are used
- * as byte index, lead byte F0..F4 bits 2..0 are used as bit index into that
- * byte.
+ * Internal bit vector for 4-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD4_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * First trail byte bits 7..4 are used as byte index,
+ * lead byte F0..F4 bits 2..0 are used as bit index into that byte.
* @see U8_IS_VALID_LEAD4_AND_T1
* @internal
*/
-#define CBU8_LEAD4_T1_BITS \
- "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
+#define CBU8_LEAD4_T1_BITS "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
/**
* Internal 4-byte UTF-8 validity check.
- * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid
- * sequence.
- * @internal
- */
-#define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) \
- (CBU8_LEAD4_T1_BITS[(uint8_t)(t1) >> 4] & (1 << ((lead)&7)))
-
-/**
- * Function for handling "next code point" with error-checking.
- *
- * This is internal since it is not meant to be called directly by external clie
-nts;
- * however it is U_STABLE (not U_INTERNAL) since it is called by public macros i
-n this
- * file and thus must remain stable, and should not be hidden when other interna
-l
- * functions are hidden (otherwise public macros would fail to compile).
+ * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid sequence.
* @internal
*/
-UChar32 utf8_nextCharSafeBody(const uint8_t* s,
- int32_t* pi,
- int32_t length,
- ::base_icu::UChar32 c,
- ::base_icu::UBool strict);
+#define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) (CBU8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7)))
/**
* Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
@@ -178,7 +171,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU8_IS_SINGLE(c) (((c)&0x80) == 0)
+#define CBU8_IS_SINGLE(c) (((c)&0x80)==0)
/**
* Is this code unit (byte) a UTF-8 lead byte? (0xC2..0xF4)
@@ -186,7 +179,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU8_IS_LEAD(c) ((uint8_t)((c)-0xc2) <= 0x32)
+#define CBU8_IS_LEAD(c) ((uint8_t)((c)-0xc2)<=0x32)
/**
* Is this code unit (byte) a UTF-8 trail byte? (0x80..0xBF)
@@ -194,7 +187,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU8_IS_TRAIL(c) ((int8_t)(c) < -0x40)
+#define CBU8_IS_TRAIL(c) ((int8_t)(c)<-0x40)
/**
* How many code units (bytes) are used for the UTF-8 encoding
@@ -203,20 +196,19 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return 1..4, or 0 if c is a surrogate or not a Unicode code point
* @stable ICU 2.4
*/
-#define CBU8_LENGTH(c) \
- ((uint32_t)(c) <= 0x7f \
- ? 1 \
- : ((uint32_t)(c) <= 0x7ff \
- ? 2 \
- : ((uint32_t)(c) <= 0xd7ff \
- ? 3 \
- : ((uint32_t)(c) <= 0xdfff || (uint32_t)(c) > 0x10ffff \
- ? 0 \
- : ((uint32_t)(c) <= 0xffff ? 3 : 4)))))
+#define CBU8_LENGTH(c) \
+ ((uint32_t)(c)<=0x7f ? 1 : \
+ ((uint32_t)(c)<=0x7ff ? 2 : \
+ ((uint32_t)(c)<=0xd7ff ? 3 : \
+ ((uint32_t)(c)<=0xdfff || (uint32_t)(c)>0x10ffff ? 0 : \
+ ((uint32_t)(c)<=0xffff ? 3 : 4)\
+ ) \
+ ) \
+ ) \
+ )
/**
- * The maximum number of UTF-8 code units (bytes) per Unicode code point
- * (U+0000..U+10ffff).
+ * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff).
* @return 4
* @stable ICU 2.4
*/
@@ -242,37 +234,43 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @see U8_NEXT_UNSAFE
* @stable ICU 2.4
*/
-#define CBU8_NEXT(s, i, length, c) \
- { \
- (c) = (uint8_t)(s)[(i)++]; \
- if (!CBU8_IS_SINGLE(c)) { \
- uint8_t __t1, __t2; \
- if (/* handle U+0800..U+FFFF inline */ \
- (0xe0 <= (c) && (c) < 0xf0) && \
- (((i) + 1) < (length) || (length) < 0) && \
- CBU8_IS_VALID_LEAD3_AND_T1((c), __t1 = (s)[i]) && \
- (__t2 = (s)[(i) + 1] - 0x80) <= 0x3f) { \
- (c) = (((c)&0xf) << 12) | ((__t1 & 0x3f) << 6) | __t2; \
- (i) += 2; \
- } else if (/* handle U+0080..U+07FF inline */ \
- ((c) < 0xe0 && (c) >= 0xc2) && ((i) != (length)) && \
- (__t1 = (s)[i] - 0x80) <= 0x3f) { \
- (c) = (((c)&0x1f) << 6) | __t1; \
- ++(i); \
- } else { \
- /* function call for "complicated" and error cases */ \
- (c) = ::base_icu::utf8_nextCharSafeBody((const uint8_t*)s, &(i), \
- (length), c, -1); \
- } \
- } \
- }
+#define CBU8_NEXT(s, i, length, c) CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, CBU_SENTINEL)
+
+/** @internal */
+#define CBU8_INTERNAL_NEXT_OR_SUB(s, i, length, c, sub) CBUPRV_BLOCK_MACRO_BEGIN { \
+ (c)=(uint8_t)(s)[(i)++]; \
+ if(!CBU8_IS_SINGLE(c)) { \
+ uint8_t __t = 0; \
+ if((i)!=(length) && \
+ /* fetch/validate/assemble all but last trail byte */ \
+ ((c)>=0xe0 ? \
+ ((c)<0xf0 ? /* U+0800..U+FFFF except surrogates */ \
+ CBU8_LEAD3_T1_BITS[(c)&=0xf]&(1<<((__t=(s)[i])>>5)) && \
+ (__t&=0x3f, 1) \
+ : /* U+10000..U+10FFFF */ \
+ ((c)-=0xf0)<=4 && \
+ CBU8_LEAD4_T1_BITS[(__t=(s)[i])>>4]&(1<<(c)) && \
+ ((c)=((c)<<6)|(__t&0x3f), ++(i)!=(length)) && \
+ (__t=(s)[i]-0x80)<=0x3f) && \
+ /* valid second-to-last trail byte */ \
+ ((c)=((c)<<6)|__t, ++(i)!=(length)) \
+ : /* U+0080..U+07FF */ \
+ (c)>=0xc2 && ((c)&=0x1f, 1)) && \
+ /* last trail byte */ \
+ (__t=(s)[i]-0x80)<=0x3f && \
+ ((c)=((c)<<6)|__t, ++(i), 1)) { \
+ } else { \
+ (c)=(sub); /* ill-formed*/ \
+ } \
+ } \
+} CBUPRV_BLOCK_MACRO_END
/**
* Append a code point to a string, overwriting 1 to 4 bytes.
* The offset points to the current end of the string contents
* and is advanced (post-increment).
- * "Unsafe" macro, assumes a valid code point and sufficient space in the
- * string. Otherwise, the result is undefined.
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
+ * Otherwise, the result is undefined.
*
* @param s const uint8_t * string buffer
* @param i string offset
@@ -280,25 +278,25 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @see U8_APPEND
* @stable ICU 2.4
*/
-#define CBU8_APPEND_UNSAFE(s, i, c) \
- { \
- if ((uint32_t)(c) <= 0x7f) { \
- (s)[(i)++] = (uint8_t)(c); \
- } else { \
- if ((uint32_t)(c) <= 0x7ff) { \
- (s)[(i)++] = (uint8_t)(((c) >> 6) | 0xc0); \
- } else { \
- if ((uint32_t)(c) <= 0xffff) { \
- (s)[(i)++] = (uint8_t)(((c) >> 12) | 0xe0); \
- } else { \
- (s)[(i)++] = (uint8_t)(((c) >> 18) | 0xf0); \
- (s)[(i)++] = (uint8_t)((((c) >> 12) & 0x3f) | 0x80); \
- } \
- (s)[(i)++] = (uint8_t)((((c) >> 6) & 0x3f) | 0x80); \
- } \
- (s)[(i)++] = (uint8_t)(((c)&0x3f) | 0x80); \
- } \
- }
+#define CBU8_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+ uint32_t __uc=(c); \
+ if(__uc<=0x7f) { \
+ (s)[(i)++]=(uint8_t)__uc; \
+ } else { \
+ if(__uc<=0x7ff) { \
+ (s)[(i)++]=(uint8_t)((__uc>>6)|0xc0); \
+ } else { \
+ if(__uc<=0xffff) { \
+ (s)[(i)++]=(uint8_t)((__uc>>12)|0xe0); \
+ } else { \
+ (s)[(i)++]=(uint8_t)((__uc>>18)|0xf0); \
+ (s)[(i)++]=(uint8_t)(((__uc>>12)&0x3f)|0x80); \
+ } \
+ (s)[(i)++]=(uint8_t)(((__uc>>6)&0x3f)|0x80); \
+ } \
+ (s)[(i)++]=(uint8_t)((__uc&0x3f)|0x80); \
+ } \
+} CBUPRV_BLOCK_MACRO_END
// source/common/unicode/utf16.h
@@ -316,7 +314,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU16_IS_LEAD(c) (((c)&0xfffffc00) == 0xd800)
+#define CBU16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
/**
* Is this code unit a trail surrogate (U+dc00..U+dfff)?
@@ -324,7 +322,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00) == 0xdc00)
+#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
/**
* Is this code unit a surrogate (U+d800..U+dfff)?
@@ -341,13 +339,13 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return TRUE or FALSE
* @stable ICU 2.4
*/
-#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400) == 0)
+#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
/**
* Helper constant for U16_GET_SUPPLEMENTARY.
* @internal
*/
-#define CBU16_SURROGATE_OFFSET ((0xd800 << 10UL) + 0xdc00 - 0x10000)
+#define CBU16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
/**
* Get a supplementary code point value (U+10000..U+10ffff)
@@ -361,8 +359,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @stable ICU 2.4
*/
#define CBU16_GET_SUPPLEMENTARY(lead, trail) \
- (((::base_icu::UChar32)(lead) << 10UL) + \
- (::base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET)
+ (((::base_icu::UChar32)(lead)<<10UL)+(::base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET)
/**
* Get the lead surrogate (0xd800..0xdbff) for a
@@ -371,8 +368,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return lead surrogate (U+d800..U+dbff) for supplementary
* @stable ICU 2.4
*/
-#define CBU16_LEAD(supplementary) \
- (::base_icu::UChar)(((supplementary) >> 10) + 0xd7c0)
+#define CBU16_LEAD(supplementary) (::base_icu::UChar)(((supplementary)>>10)+0xd7c0)
/**
* Get the trail surrogate (0xdc00..0xdfff) for a
@@ -381,28 +377,64 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @return trail surrogate (U+dc00..U+dfff) for supplementary
* @stable ICU 2.4
*/
-#define CBU16_TRAIL(supplementary) \
- (::base_icu::UChar)(((supplementary)&0x3ff) | 0xdc00)
+#define CBU16_TRAIL(supplementary) (::base_icu::UChar)(((supplementary)&0x3ff)|0xdc00)
/**
- * How many 16-bit code units are used to encode this Unicode code point? (1 or
- * 2) The result is not defined if c is not a Unicode code point
- * (U+0000..U+10ffff).
+ * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
+ * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
* @param c 32-bit code point
* @return 1 or 2
* @stable ICU 2.4
*/
-#define CBU16_LENGTH(c) ((uint32_t)(c) <= 0xffff ? 1 : 2)
+#define CBU16_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2)
/**
- * The maximum number of 16-bit code units per Unicode code point
- * (U+0000..U+10ffff).
+ * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff).
* @return 2
* @stable ICU 2.4
*/
#define CBU16_MAX_LENGTH 2
/**
+ * Get a code point from a string at a random-access offset,
+ * without changing the offset.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The offset may point to either the lead or trail surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the adjacent matching surrogate as well.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * If the offset points to a single, unpaired surrogate, then
+ * c is set to that unpaired surrogate.
+ * Iteration through a string is more efficient with U16_NEXT_UNSAFE or U16_NEXT.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i<length
+ * @param length string length
+ * @param c output UChar32 variable
+ * @see U16_GET_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_GET(s, start, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+ (c)=(s)[i]; \
+ if(CBU16_IS_SURROGATE(c)) { \
+ uint16_t __c2; \
+ if(CBU16_IS_SURROGATE_LEAD(c)) { \
+ if((i)+1!=(length) && CBU16_IS_TRAIL(__c2=(s)[(i)+1])) { \
+ (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \
+ } \
+ } else { \
+ if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \
+ (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \
+ } \
+ } \
+ } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
* Get a code point from a string at a code point boundary offset,
* and advance the offset to the next code point boundary.
* (Post-incrementing forward iteration.)
@@ -414,8 +446,7 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* for a supplementary code point, in which case the macro will read
* the following trail surrogate as well.
* If the offset points to a trail surrogate or
- * to a single, unpaired lead surrogate, then c is set to that unpaired
- * surrogate.
+ * to a single, unpaired lead surrogate, then c is set to that unpaired surrogate.
*
* @param s const UChar * string
* @param i string offset, must be i<length
@@ -424,24 +455,23 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @see U16_NEXT_UNSAFE
* @stable ICU 2.4
*/
-#define CBU16_NEXT(s, i, length, c) \
- { \
- (c) = (s)[(i)++]; \
- if (CBU16_IS_LEAD(c)) { \
- uint16_t __c2; \
- if ((i) != (length) && CBU16_IS_TRAIL(__c2 = (s)[(i)])) { \
- ++(i); \
- (c) = CBU16_GET_SUPPLEMENTARY((c), __c2); \
- } \
- } \
- }
+#define CBU16_NEXT(s, i, length, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+ (c)=(s)[(i)++]; \
+ if(CBU16_IS_LEAD(c)) { \
+ uint16_t __c2; \
+ if((i)!=(length) && CBU16_IS_TRAIL(__c2=(s)[(i)])) { \
+ ++(i); \
+ (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \
+ } \
+ } \
+} CBUPRV_BLOCK_MACRO_END
/**
* Append a code point to a string, overwriting 1 or 2 code units.
* The offset points to the current end of the string contents
* and is advanced (post-increment).
- * "Unsafe" macro, assumes a valid code point and sufficient space in the
- * string. Otherwise, the result is undefined.
+ * "Unsafe" macro, assumes a valid code point and sufficient space in the string.
+ * Otherwise, the result is undefined.
*
* @param s const UChar * string buffer
* @param i string offset
@@ -449,16 +479,89 @@ UChar32 utf8_nextCharSafeBody(const uint8_t* s,
* @see U16_APPEND
* @stable ICU 2.4
*/
-#define CBU16_APPEND_UNSAFE(s, i, c) \
- { \
- if ((uint32_t)(c) <= 0xffff) { \
- (s)[(i)++] = (uint16_t)(c); \
- } else { \
- (s)[(i)++] = (uint16_t)(((c) >> 10) + 0xd7c0); \
- (s)[(i)++] = (uint16_t)(((c)&0x3ff) | 0xdc00); \
- } \
- }
-
-} // namespace base_icu
+#define CBU16_APPEND_UNSAFE(s, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+ if((uint32_t)(c)<=0xffff) { \
+ (s)[(i)++]=(uint16_t)(c); \
+ } else { \
+ (s)[(i)++]=(uint16_t)(((c)>>10)+0xd7c0); \
+ (s)[(i)++]=(uint16_t)(((c)&0x3ff)|0xdc00); \
+ } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary
+ * at the start of a code point.
+ * If the offset points to the trail surrogate of a surrogate pair,
+ * then the offset is decremented.
+ * Otherwise, it is not modified.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<=i
+ * @see U16_SET_CP_START_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_SET_CP_START(s, start, i) CBUPRV_BLOCK_MACRO_BEGIN { \
+ if(CBU16_IS_TRAIL((s)[i]) && (i)>(start) && CBU16_IS_LEAD((s)[(i)-1])) { \
+ --(i); \
+ } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Move the string offset from one code point boundary to the previous one
+ * and get the code point between them.
+ * (Pre-decrementing backward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The input offset may be the same as the string length.
+ * If the offset is behind a trail surrogate unit
+ * for a supplementary code point, then the macro will read
+ * the preceding lead surrogate as well.
+ * If the offset is behind a lead surrogate or behind a single, unpaired
+ * trail surrogate, then c is set to that unpaired surrogate.
+ *
+ * @param s const UChar * string
+ * @param start starting string offset (usually 0)
+ * @param i string offset, must be start<i
+ * @param c output UChar32 variable
+ * @see U16_PREV_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_PREV(s, start, i, c) CBUPRV_BLOCK_MACRO_BEGIN { \
+ (c)=(s)[--(i)]; \
+ if(CBU16_IS_TRAIL(c)) { \
+ uint16_t __c2; \
+ if((i)>(start) && CBU16_IS_LEAD(__c2=(s)[(i)-1])) { \
+ --(i); \
+ (c)=CBU16_GET_SUPPLEMENTARY(__c2, (c)); \
+ } \
+ } \
+} CBUPRV_BLOCK_MACRO_END
+
+/**
+ * Adjust a random-access offset to a code point boundary after a code point.
+ * If the offset is behind the lead surrogate of a surrogate pair,
+ * then the offset is incremented.
+ * Otherwise, it is not modified.
+ * The input offset may be the same as the string length.
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * @param s const UChar * string
+ * @param start int32_t starting string offset (usually 0)
+ * @param i int32_t string offset, start<=i<=length
+ * @param length int32_t string length
+ * @see U16_SET_CP_LIMIT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU16_SET_CP_LIMIT(s, start, i, length) CBUPRV_BLOCK_MACRO_BEGIN { \
+ if((start)<(i) && ((i)<(length) || (length)<0) && CBU16_IS_LEAD((s)[(i)-1]) && CBU16_IS_TRAIL((s)[i])) { \
+ ++(i); \
+ } \
+} CBUPRV_BLOCK_MACRO_END
+
+} // namesapce base_icu
#endif // BASE_THIRD_PARTY_ICU_ICU_UTF_H_
diff --git a/gn/src/gn/action_target_generator.cc b/gn/src/gn/action_target_generator.cc
index 73fa28895a7..513015bae64 100644
--- a/gn/src/gn/action_target_generator.cc
+++ b/gn/src/gn/action_target_generator.cc
@@ -6,6 +6,7 @@
#include "base/stl_util.h"
#include "gn/build_settings.h"
+#include "gn/config_values_generator.h"
#include "gn/err.h"
#include "gn/filesystem_utils.h"
#include "gn/functions.h"
@@ -63,9 +64,19 @@ void ActionTargetGenerator::DoRun() {
if (!FillCheckIncludes())
return;
+ if (!FillConfigs())
+ return;
+
if (!CheckOutputs())
return;
+ // Config values (compiler flags, etc.) set directly on this target.
+ ConfigValuesGenerator gen(&target_->config_values(), scope_,
+ scope_->GetSourceDir(), err_);
+ gen.Run();
+ if (err_->has_error())
+ return;
+
// Action outputs don't depend on the current toolchain so we can skip adding
// that dependency.
@@ -172,7 +183,7 @@ bool ActionTargetGenerator::FillPool() {
LabelPtrPair<Pool> pair(label);
pair.origin = target_->defined_from();
- target_->action_values().set_pool(std::move(pair));
+ target_->set_pool(std::move(pair));
return true;
}
diff --git a/gn/src/gn/action_values.h b/gn/src/gn/action_values.h
index 30436b7a6fe..9daf9182c0a 100644
--- a/gn/src/gn/action_values.h
+++ b/gn/src/gn/action_values.h
@@ -51,17 +51,12 @@ class ActionValues {
}
bool uses_rsp_file() const { return !rsp_file_contents_.list().empty(); }
- // Pool option
- const LabelPtrPair<Pool>& pool() const { return pool_; }
- void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
-
private:
SourceFile script_;
SubstitutionList args_;
SubstitutionList outputs_;
SubstitutionPattern depfile_;
SubstitutionList rsp_file_contents_;
- LabelPtrPair<Pool> pool_;
ActionValues(const ActionValues&) = delete;
ActionValues& operator=(const ActionValues&) = delete;
diff --git a/gn/src/gn/analyzer.cc b/gn/src/gn/analyzer.cc
index fd93879ec88..be646ee8512 100644
--- a/gn/src/gn/analyzer.cc
+++ b/gn/src/gn/analyzer.cc
@@ -89,7 +89,7 @@ void WriteString(base::DictionaryValue& dict,
const std::string& key,
const std::string& value) {
dict.SetKey(key, base::Value(value));
-};
+}
void WriteLabels(const Label& default_toolchain,
base::DictionaryValue& dict,
@@ -183,7 +183,7 @@ Err JSONToInputs(const Label& default_toolchain,
}
}
- for (const auto& kv : dict->DictItems()) {
+ for (const auto kv : dict->DictItems()) {
if (kv.first == kFilesKey || kv.first == kAdditonalCompileTargetsKey ||
kv.first == kTestTargetsKey) {
continue;
@@ -254,10 +254,11 @@ Analyzer::Analyzer(const Builder& builder,
dep_map_.insert(std::make_pair(item->AsTarget()->toolchain(), item));
- if (item->AsTarget()->output_type() == Target::ACTION ||
+ if (item->AsTarget()->IsBinary() ||
+ item->AsTarget()->output_type() == Target::ACTION ||
item->AsTarget()->output_type() == Target::ACTION_FOREACH) {
const LabelPtrPair<Pool>& pool =
- item->AsTarget()->action_values().pool();
+ item->AsTarget()->pool();
if (pool.ptr)
dep_map_.insert(std::make_pair(pool.ptr, item));
}
diff --git a/gn/src/gn/analyzer_unittest.cc b/gn/src/gn/analyzer_unittest.cc
index 98bef673bcb..e3d5696e377 100644
--- a/gn/src/gn/analyzer_unittest.cc
+++ b/gn/src/gn/analyzer_unittest.cc
@@ -480,7 +480,7 @@ TEST_F(AnalyzerTest, AffectedPoolpropagatesToDependentTargets) {
t->set_output_type(Target::ACTION);
std::unique_ptr<Pool> p = MakePool("//dir", "pool_name");
Pool* p_raw = p.get();
- t->action_values().set_pool(LabelPtrPair<Pool>(p.get()));
+ t->set_pool(LabelPtrPair<Pool>(p.get()));
builder_.ItemDefined(std::move(t));
builder_.ItemDefined(std::move(p));
diff --git a/gn/src/gn/args.cc b/gn/src/gn/args.cc
index 133eff1d05c..fbc0ee4a44b 100644
--- a/gn/src/gn/args.cc
+++ b/gn/src/gn/args.cc
@@ -269,7 +269,8 @@ Args::ValueWithOverrideMap Args::GetAllArguments() const {
std::lock_guard<std::mutex> lock(lock_);
// Sort the keys from declared_arguments_per_toolchain_ so
- // the return value will be deterministic.
+ // the return value will be deterministic. Always prioritize
+ // the default toolchain.
std::vector<const Settings*> keys;
keys.reserve(declared_arguments_per_toolchain_.size());
for (const auto& map_pair : declared_arguments_per_toolchain_) {
@@ -277,7 +278,8 @@ Args::ValueWithOverrideMap Args::GetAllArguments() const {
}
std::sort(keys.begin(), keys.end(),
[](const Settings* a, const Settings* b) -> bool {
- return a->toolchain_label() < b->toolchain_label();
+ return a->is_default() ||
+ a->toolchain_label() < b->toolchain_label();
});
// Default values.
diff --git a/gn/src/gn/binary_target_generator.cc b/gn/src/gn/binary_target_generator.cc
index 4cac96f97ad..07df7bff647 100644
--- a/gn/src/gn/binary_target_generator.cc
+++ b/gn/src/gn/binary_target_generator.cc
@@ -64,6 +64,9 @@ void BinaryTargetGenerator::DoRun() {
if (!FillCompleteStaticLib())
return;
+ if (!FillPool())
+ return;
+
if (!ValidateSources())
return;
@@ -215,8 +218,12 @@ bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
}
}
if (!found_dep) {
+ bool with_toolchain = scope_->settings()->ShouldShowToolchain({
+ &target_->label(),
+ &cur
+ });
*err_ = Err(*value, "Label not in deps.",
- "The label \"" + cur.GetUserVisibleName(false) +
+ "The label \"" + cur.GetUserVisibleName(with_toolchain) +
"\"\nwas not in the deps of this target. "
"allow_circular_includes_from only allows\ntargets "
"present in the "
@@ -231,6 +238,25 @@ bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
return true;
}
+bool BinaryTargetGenerator::FillPool() {
+ const Value* value = scope_->GetValue(variables::kPool, true);
+ if (!value)
+ return true;
+
+ Label label =
+ Label::Resolve(scope_->GetSourceDir(),
+ scope_->settings()->build_settings()->root_path_utf8(),
+ ToolchainLabelForScope(scope_), *value, err_);
+ if (err_->has_error())
+ return false;
+
+ LabelPtrPair<Pool> pair(label);
+ pair.origin = target_->defined_from();
+
+ target_->set_pool(std::move(pair));
+ return true;
+}
+
bool BinaryTargetGenerator::ValidateSources() {
// For Rust targets, if the only source file is the root `sources` can be
// omitted/empty.
diff --git a/gn/src/gn/binary_target_generator.h b/gn/src/gn/binary_target_generator.h
index 2c8b7695bb8..0bafd683c34 100644
--- a/gn/src/gn/binary_target_generator.h
+++ b/gn/src/gn/binary_target_generator.h
@@ -30,6 +30,7 @@ class BinaryTargetGenerator : public TargetGenerator {
bool FillOutputPrefixOverride();
bool FillOutputDir();
bool FillAllowCircularIncludesFrom();
+ bool FillPool();
bool ValidateSources();
Target::OutputType output_type_;
diff --git a/gn/src/gn/build_settings.h b/gn/src/gn/build_settings.h
index 9a6c5147aa9..6c925e985e0 100644
--- a/gn/src/gn/build_settings.h
+++ b/gn/src/gn/build_settings.h
@@ -61,6 +61,15 @@ class BuildSettings {
}
void set_ninja_required_version(Version v) { ninja_required_version_ = v; }
+ // The 'no_stamp_files' boolean flag can be set to generate Ninja files
+ // that use phony rules instead of stamp files in most cases. This reduces
+ // the size of the generated Ninja build plans, but requires Ninja 1.11
+ // or greater to properly process them.
+ bool no_stamp_files() const { return no_stamp_files_; }
+ void set_no_stamp_files(bool no_stamp_files) {
+ no_stamp_files_ = no_stamp_files;
+ }
+
const SourceFile& build_config_file() const { return build_config_file_; }
void set_build_config_file(const SourceFile& f) { build_config_file_ = f; }
@@ -134,6 +143,7 @@ class BuildSettings {
// See 40045b9 for the reason behind using 1.7.2 as the default version.
Version ninja_required_version_{1, 7, 2};
+ bool no_stamp_files_ = false;
SourceFile build_config_file_;
SourceFile arg_file_template_path_;
diff --git a/gn/src/gn/builder.cc b/gn/src/gn/builder.cc
index adb39ab1a8f..81335f86ff2 100644
--- a/gn/src/gn/builder.cc
+++ b/gn/src/gn/builder.cc
@@ -75,8 +75,9 @@ void Builder::ItemDefined(std::unique_ptr<Item> item) {
// Check that it's not been already defined.
if (record->item()) {
+ bool with_toolchain = item->settings()->ShouldShowToolchain({&item->label()});
err = Err(item->defined_from(), "Duplicate definition.",
- "The item\n " + item->label().GetUserVisibleName(false) +
+ "The item\n " + item->label().GetUserVisibleName(with_toolchain) +
"\nwas already defined.");
err.AppendSubErr(
Err(record->item()->defined_from(), "Previous definition:"));
@@ -232,7 +233,7 @@ bool Builder::CheckForBadItems(Err* err) const {
"possibly due to an\ninternal error:";
for (auto* bad_record : bad_records) {
depstring +=
- "\n\"" + bad_record->label().GetUserVisibleName(false) + "\"";
+ "\n\"" + bad_record->label().GetUserVisibleName(true) + "\"";
}
*err = Err(Location(), "", depstring);
} else {
@@ -254,7 +255,7 @@ bool Builder::TargetDefined(BuilderRecord* record, Err* err) {
!AddDeps(record, target->all_dependent_configs(), err) ||
!AddDeps(record, target->public_configs(), err) ||
!AddGenDeps(record, target->gen_deps(), err) ||
- !AddActionValuesDep(record, target->action_values(), err) ||
+ !AddPoolDep(record, target, err) ||
!AddToolchainDep(record, target, err))
return false;
@@ -330,7 +331,7 @@ BuilderRecord* Builder::GetOrCreateRecordOfType(const Label& label,
// Check types, if the record was not just created.
if (!pair.first && record->type() != type) {
std::string msg =
- "The type of " + label.GetUserVisibleName(false) + "\nhere is a " +
+ "The type of " + label.GetUserVisibleName(true) + "\nhere is a " +
BuilderRecord::GetNameForType(type) + " but was previously seen as a " +
BuilderRecord::GetNameForType(record->type()) +
".\n\n"
@@ -354,7 +355,7 @@ BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
BuilderRecord* record = GetRecord(label);
if (!record) {
*err = Err(origin, "Item not found",
- "\"" + label.GetUserVisibleName(false) +
+ "\"" + label.GetUserVisibleName(true) +
"\" doesn't\n"
"refer to an existent thing.");
return nullptr;
@@ -364,7 +365,7 @@ BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
if (!item) {
*err = Err(
origin, "Item not resolved.",
- "\"" + label.GetUserVisibleName(false) + "\" hasn't been resolved.\n");
+ "\"" + label.GetUserVisibleName(true) + "\" hasn't been resolved.\n");
return nullptr;
}
@@ -372,7 +373,7 @@ BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
*err =
Err(origin,
std::string("This is not a ") + BuilderRecord::GetNameForType(type),
- "\"" + label.GetUserVisibleName(false) + "\" refers to a " +
+ "\"" + label.GetUserVisibleName(true) + "\" refers to a " +
item->GetItemTypeName() + " instead of a " +
BuilderRecord::GetNameForType(type) + ".");
return nullptr;
@@ -432,14 +433,14 @@ bool Builder::AddGenDeps(BuilderRecord* record,
return true;
}
-bool Builder::AddActionValuesDep(BuilderRecord* record,
- const ActionValues& action_values,
- Err* err) {
- if (action_values.pool().label.is_null())
+bool Builder::AddPoolDep(BuilderRecord* record,
+ const Target* target,
+ Err* err) {
+ if (target->pool().label.is_null())
return true;
BuilderRecord* pool_record = GetOrCreateRecordOfType(
- action_values.pool().label, action_values.pool().origin,
+ target->pool().label, target->pool().origin,
BuilderRecord::ITEM_POOL, err);
if (!pool_record)
return false;
@@ -500,7 +501,7 @@ bool Builder::ResolveItem(BuilderRecord* record, Err* err) {
!ResolveConfigs(&target->configs(), err) ||
!ResolveConfigs(&target->all_dependent_configs(), err) ||
!ResolveConfigs(&target->public_configs(), err) ||
- !ResolveActionValues(&target->action_values(), err) ||
+ !ResolvePool(target, err) ||
!ResolveToolchain(target, err))
return false;
} else if (record->type() == BuilderRecord::ITEM_CONFIG) {
@@ -579,16 +580,16 @@ bool Builder::ResolveToolchain(Target* target, Err* err) {
return true;
}
-bool Builder::ResolveActionValues(ActionValues* action_values, Err* err) {
- if (action_values->pool().label.is_null())
+bool Builder::ResolvePool(Target* target, Err* err) {
+ if (target->pool().label.is_null())
return true;
BuilderRecord* record = GetResolvedRecordOfType(
- action_values->pool().label, action_values->pool().origin,
+ target->pool().label, target->pool().origin,
BuilderRecord::ITEM_POOL, err);
if (!record)
return false;
- action_values->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
+ target->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
return true;
}
diff --git a/gn/src/gn/builder.h b/gn/src/gn/builder.h
index bebaa8143b8..e89e2e1a2c6 100644
--- a/gn/src/gn/builder.h
+++ b/gn/src/gn/builder.h
@@ -101,9 +101,9 @@ class Builder {
bool AddGenDeps(BuilderRecord* record,
const LabelTargetVector& targets,
Err* err);
- bool AddActionValuesDep(BuilderRecord* record,
- const ActionValues& action_values,
- Err* err);
+ bool AddPoolDep(BuilderRecord* record,
+ const Target* target,
+ Err* err);
bool AddToolchainDep(BuilderRecord* record, const Target* target, Err* err);
// Given a target, sets the "should generate" bit and pushes it through the
@@ -129,7 +129,7 @@ class Builder {
// if anything isn't found or if the type doesn't match.
bool ResolveDeps(LabelTargetVector* deps, Err* err);
bool ResolveConfigs(UniqueVector<LabelConfigPair>* configs, Err* err);
- bool ResolveActionValues(ActionValues* action_values, Err* err);
+ bool ResolvePool(Target* target, Err* err);
bool ResolveToolchain(Target* target, Err* err);
bool ResolvePools(Toolchain* toolchain, Err* err);
diff --git a/gn/src/gn/builtin_tool.cc b/gn/src/gn/builtin_tool.cc
new file mode 100644
index 00000000000..e9f17ef116f
--- /dev/null
+++ b/gn/src/gn/builtin_tool.cc
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gn/builtin_tool.h"
+#include "base/logging.h"
+#include "gn/target.h"
+
+// static
+const char BuiltinTool::kBuiltinToolPhony[] = "phony";
+
+BuiltinTool::BuiltinTool(const char* n) : Tool(n) {
+ CHECK(ValidateName(n));
+ // Unlike regular tools, which are read from a file,
+ // builtin-tools are always ready to go and do not need
+ // phased construction.
+ SetToolComplete();
+}
+
+BuiltinTool::~BuiltinTool() = default;
+
+BuiltinTool* BuiltinTool::AsBuiltin() {
+ return this;
+}
+const BuiltinTool* BuiltinTool::AsBuiltin() const {
+ return this;
+}
+
+bool BuiltinTool::ValidateName(const char* name) const {
+ return name == kBuiltinToolPhony;
+}
+
+void BuiltinTool::SetComplete() {
+ // Already performed in constructor
+}
+
+bool BuiltinTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ return Tool::InitTool(scope, toolchain, err);
+}
+
+bool BuiltinTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kBuiltinToolPhony)
+ return IsValidToolSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/src/gn/builtin_tool.h b/gn/src/gn/builtin_tool.h
new file mode 100644
index 00000000000..0aff49d071b
--- /dev/null
+++ b/gn/src/gn/builtin_tool.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_BUILTIN_TOOL_H_
+#define TOOLS_GN_BUILTIN_TOOL_H_
+
+#include <string>
+
+#include "gn/substitution_list.h"
+#include "gn/substitution_pattern.h"
+#include "gn/tool.h"
+
+// A built-in tool that is always available regardless of toolchain. So far, the
+// only example of this is the phony rule that ninja provides.
+class BuiltinTool : public Tool {
+ public:
+ // Builtin tools
+ static const char kBuiltinToolPhony[];
+
+ // Explicit constructor. Note that |name| must be one of the kBuiltinToolXXX
+ // constant pointers defined above.
+ explicit BuiltinTool(const char* name);
+ ~BuiltinTool();
+
+ // Manual RTTI and required functions ---------------------------------------
+
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+ bool ValidateName(const char* name) const override;
+ void SetComplete() override;
+ bool ValidateSubstitution(const Substitution* sub_type) const override;
+
+ BuiltinTool* AsBuiltin() override;
+ const BuiltinTool* AsBuiltin() const override;
+
+ private:
+ BuiltinTool(const BuiltinTool&) = delete;
+ BuiltinTool& operator=(const BuiltinTool&) = delete;
+};
+
+#endif // TOOLS_GN_BUILTIN_TOOL_H_
diff --git a/gn/src/gn/bundle_data.h b/gn/src/gn/bundle_data.h
index 527e0786733..5b3bca6a742 100644
--- a/gn/src/gn/bundle_data.h
+++ b/gn/src/gn/bundle_data.h
@@ -162,6 +162,11 @@ class BundleData {
// Recursive collection of all bundle_data that the target depends on.
const UniqueTargets& bundle_deps() const { return bundle_deps_; }
+ // Returns whether the bundle is an application bundle.
+ bool is_application() const {
+ return product_type_ == "com.apple.product-type.application";
+ }
+
// Returns whether the bundle is a framework bundle.
bool is_framework() const {
return product_type_ == "com.apple.product-type.framework";
diff --git a/gn/src/gn/c_substitution_type.cc b/gn/src/gn/c_substitution_type.cc
index ea47ce9da9c..971d87a46ff 100644
--- a/gn/src/gn/c_substitution_type.cc
+++ b/gn/src/gn/c_substitution_type.cc
@@ -92,6 +92,19 @@ bool IsValidCompilerSubstitution(const Substitution* type) {
type == &CSubstitutionModuleDepsNoSelf;
}
+bool IsValidCompilerScriptArgsSubstitution(const Substitution* type) {
+ return type == &CSubstitutionAsmFlags || type == &CSubstitutionCFlags ||
+ type == &CSubstitutionCFlagsC || type == &CSubstitutionCFlagsCc ||
+ type == &CSubstitutionCFlagsObjC ||
+ type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
+ type == &CSubstitutionFrameworkDirs ||
+ type == &CSubstitutionIncludeDirs ||
+ type == &CSubstitutionSwiftModuleName ||
+ type == &CSubstitutionSwiftBridgeHeader ||
+ type == &CSubstitutionSwiftModuleDirs ||
+ type == &CSubstitutionSwiftFlags;
+}
+
bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
// All tool types except "output" (which would be infinitely recursive).
return (IsValidToolSubstitution(type) && type != &SubstitutionOutput) ||
diff --git a/gn/src/gn/c_substitution_type.h b/gn/src/gn/c_substitution_type.h
index 1f4e90e06a3..d0d1f8c4167 100644
--- a/gn/src/gn/c_substitution_type.h
+++ b/gn/src/gn/c_substitution_type.h
@@ -54,4 +54,7 @@ bool IsValidLinkerSubstitution(const Substitution* type);
bool IsValidLinkerOutputsSubstitution(const Substitution* type);
bool IsValidALinkSubstitution(const Substitution* type);
+// action targets may rely on some compiler substitutions.
+bool IsValidCompilerScriptArgsSubstitution(const Substitution* type);
+
#endif // TOOLS_GN_C_SUBSTITUTION_TYPE_H_
diff --git a/gn/src/gn/command_args.cc b/gn/src/gn/command_args.cc
index e74c59862ee..6f4600bdd99 100644
--- a/gn/src/gn/command_args.cc
+++ b/gn/src/gn/command_args.cc
@@ -212,7 +212,7 @@ int ListArgs(const std::string& build_dir) {
Args::ValueWithOverrideMap args =
setup->build_settings().build_args().GetAllArguments();
std::string list_value =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kSwitchList);
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueString(kSwitchList);
if (!list_value.empty()) {
// List just the one specified as the parameter to --list.
auto found = args.find(list_value);
diff --git a/gn/src/gn/command_clean.cc b/gn/src/gn/command_clean.cc
index 5290dd65253..6dcc1631496 100644
--- a/gn/src/gn/command_clean.cc
+++ b/gn/src/gn/command_clean.cc
@@ -5,45 +5,16 @@
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
-#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "gn/commands.h"
#include "gn/err.h"
#include "gn/filesystem_utils.h"
+#include "gn/ninja_build_writer.h"
#include "gn/setup.h"
namespace {
-// Extracts from a build.ninja the commands to run GN.
-//
-// The commands to run GN are the gn rule and build.ninja build step at the top
-// of the build.ninja file. We want to keep these when deleting GN builds since
-// we want to preserve the command-line flags to GN.
-//
-// On error, returns the empty string.
-std::string ExtractGNBuildCommands(const base::FilePath& build_ninja_file) {
- std::string file_contents;
- if (!base::ReadFileToString(build_ninja_file, &file_contents))
- return std::string();
-
- std::vector<std::string_view> lines = base::SplitStringPiece(
- file_contents, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
-
- std::string result;
- int num_blank_lines = 0;
- for (const auto& line : lines) {
- result.append(line);
- result.push_back('\n');
- if (line.empty())
- ++num_blank_lines;
- if (num_blank_lines == 3)
- break;
- }
-
- return result;
-}
-
bool CleanOneDir(const std::string& dir) {
// Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
@@ -53,10 +24,12 @@ bool CleanOneDir(const std::string& dir) {
base::FilePath build_dir(setup->build_settings().GetFullPath(
SourceDir(setup->build_settings().build_dir().value())));
- // NOTE: Not all GN builds have args.gn file hence we check here
+ // NOTE: Not all GN builds have args.gn file hence we also check here
// if a build.ninja.d files exists instead.
+ base::FilePath args_gn_file = build_dir.AppendASCII("args.gn");
base::FilePath build_ninja_d_file = build_dir.AppendASCII("build.ninja.d");
- if (!base::PathExists(build_ninja_d_file)) {
+ if (!base::PathExists(args_gn_file) &&
+ !base::PathExists(build_ninja_d_file)) {
Err(Location(),
base::StringPrintf(
"%s does not look like a build directory.\n",
@@ -65,47 +38,32 @@ bool CleanOneDir(const std::string& dir) {
return false;
}
- // Erase everything but the args file, and write a dummy build.ninja file that
- // will automatically rerun GN the next time Ninja is run.
- base::FilePath build_ninja_file = build_dir.AppendASCII("build.ninja");
- std::string build_commands = ExtractGNBuildCommands(build_ninja_file);
- if (build_commands.empty()) {
- // Couldn't parse the build.ninja file.
- Err(Location(), "Couldn't read build.ninja in this directory.",
- "Try running \"gn gen\" on it and then re-running \"gn clean\".")
- .PrintToStdout();
+ // Replace existing build.ninja with just enough for ninja to call GN and
+ // regenerate ninja files.
+ if (!commands::PrepareForRegeneration(&setup->build_settings())) {
return false;
}
+ // Erase everything but (user-created) args.gn and the build.ninja files we
+ // just wrote.
+ const base::FilePath::CharType* remaining[]{
+ FILE_PATH_LITERAL("args.gn"),
+ FILE_PATH_LITERAL("build.ninja"),
+ FILE_PATH_LITERAL("build.ninja.d"),
+ };
base::FileEnumerator traversal(
build_dir, false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
for (base::FilePath current = traversal.Next(); !current.empty();
current = traversal.Next()) {
- if (base::ToLowerASCII(current.BaseName().value()) !=
- FILE_PATH_LITERAL("args.gn")) {
+ base::FilePath::StringType basename =
+ base::ToLowerASCII(current.BaseName().value());
+ if (std::none_of(std::begin(remaining), std::end(remaining),
+ [&](auto rem) { return basename == rem; })) {
base::DeleteFile(current, true);
}
}
- // Write the build.ninja file sufficiently to regenerate itself.
- if (base::WriteFile(build_ninja_file, build_commands.data(),
- static_cast<int>(build_commands.size())) == -1) {
- Err(Location(), std::string("Failed to write build.ninja."))
- .PrintToStdout();
- return false;
- }
-
- // Write a .d file for the build which references a nonexistent file.
- // This will make Ninja always mark the build as dirty.
- std::string dummy_content("build.ninja: nonexistant_file.gn\n");
- if (base::WriteFile(build_ninja_d_file, dummy_content.data(),
- static_cast<int>(dummy_content.size())) == -1) {
- Err(Location(), std::string("Failed to write build.ninja.d."))
- .PrintToStdout();
- return false;
- }
-
return true;
}
diff --git a/gn/src/gn/command_desc.cc b/gn/src/gn/command_desc.cc
index fcb343442fb..e3cd1eb0716 100644
--- a/gn/src/gn/command_desc.cc
+++ b/gn/src/gn/command_desc.cc
@@ -653,7 +653,7 @@ int RunDesc(const std::vector<std::string>& args) {
if (args.size() == 3)
what_to_print = args[2];
- bool json = cmdline->GetSwitchValueASCII("format") == "json";
+ bool json = cmdline->GetSwitchValueString("format") == "json";
if (target_matches.empty() && config_matches.empty()) {
OutputString(
diff --git a/gn/src/gn/command_format.cc b/gn/src/gn/command_format.cc
index a3fda2120be..69077104386 100644
--- a/gn/src/gn/command_format.cc
+++ b/gn/src/gn/command_format.cc
@@ -170,6 +170,10 @@ class Printer {
// 'visibility': same as 'deps'.
void SortIfApplicable(const BinaryOpNode* binop);
+ // Traverse a binary op node tree and apply a callback to each leaf node.
+ void TraverseBinaryOpNode(const ParseNode* node,
+ std::function<void(const ParseNode*)> callback);
+
// Sort contiguous import() function calls in the given ordered list of
// statements (the body of a block or scope).
template <class PARSENODE>
@@ -403,17 +407,37 @@ void Printer::SortIfApplicable(const BinaryOpNode* binop) {
}
}
const IdentifierNode* ident = binop->left()->AsIdentifier();
- const ListNode* list = binop->right()->AsList();
if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
binop->op().value() == "-=") &&
- ident && list) {
+ ident) {
const std::string_view lhs = ident->value().value();
if (base::EndsWith(lhs, "sources", base::CompareCase::SENSITIVE) ||
- lhs == "public")
- const_cast<ListNode*>(list)->SortAsStringsList();
- else if (base::EndsWith(lhs, "deps", base::CompareCase::SENSITIVE) ||
- lhs == "visibility")
- const_cast<ListNode*>(list)->SortAsTargetsList();
+ lhs == "public") {
+ TraverseBinaryOpNode(binop->right(), [](const ParseNode* node) {
+ const ListNode* list = node->AsList();
+ if (list)
+ const_cast<ListNode*>(list)->SortAsStringsList();
+ });
+ } else if (base::EndsWith(lhs, "deps", base::CompareCase::SENSITIVE) ||
+ lhs == "visibility") {
+ TraverseBinaryOpNode(binop->right(), [](const ParseNode* node) {
+ const ListNode* list = node->AsList();
+ if (list)
+ const_cast<ListNode*>(list)->SortAsTargetsList();
+ });
+ }
+ }
+}
+
+void Printer::TraverseBinaryOpNode(
+ const ParseNode* node,
+ std::function<void(const ParseNode*)> callback) {
+ const BinaryOpNode* binop = node->AsBinaryOp();
+ if (binop) {
+ TraverseBinaryOpNode(binop->left(), callback);
+ TraverseBinaryOpNode(binop->right(), callback);
+ } else {
+ callback(node);
}
}
@@ -519,7 +543,7 @@ int SuffixCommentTreeWalk(const ParseNode* node) {
#define RETURN_IF_SET(x) \
if (int result = (x); result >= 0) \
- return result;
+ return result
if (const AccessorNode* accessor = node->AsAccessor()) {
RETURN_IF_SET(SuffixCommentTreeWalk(accessor->subscript()));
@@ -559,7 +583,7 @@ int SuffixCommentTreeWalk(const ParseNode* node) {
}
return -1;
-};
+}
// If there are suffix comments on the first node or its children, they might
// carry down multiple lines. Otherwise, use the node's normal end range. This
@@ -1282,7 +1306,7 @@ int RunFormat(const std::vector<std::string>& args) {
TreeDumpMode dump_tree = TreeDumpMode::kInactive;
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
std::string tree_type =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
kSwitchDumpTree);
if (tree_type == kSwitchTreeTypeJSON) {
dump_tree = TreeDumpMode::kJSON;
@@ -1337,7 +1361,7 @@ int RunFormat(const std::vector<std::string>& args) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchReadTree)) {
std::string tree_type =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
kSwitchReadTree);
if (tree_type != kSwitchTreeTypeJSON) {
Err(Location(), "Only json supported for read-tree.\n").PrintToStdout();
diff --git a/gn/src/gn/command_format_unittest.cc b/gn/src/gn/command_format_unittest.cc
index 9112dd36229..de7e004182f 100644
--- a/gn/src/gn/command_format_unittest.cc
+++ b/gn/src/gn/command_format_unittest.cc
@@ -136,3 +136,4 @@ FORMAT_TEST(080)
FORMAT_TEST(081)
FORMAT_TEST(082)
FORMAT_TEST(083)
+FORMAT_TEST(084)
diff --git a/gn/src/gn/command_gen.cc b/gn/src/gn/command_gen.cc
index 23f15a099f5..880fb20759a 100644
--- a/gn/src/gn/command_gen.cc
+++ b/gn/src/gn/command_gen.cc
@@ -14,6 +14,7 @@
#include "gn/eclipse_writer.h"
#include "gn/filesystem_utils.h"
#include "gn/json_project_writer.h"
+#include "gn/label_pattern.h"
#include "gn/ninja_target_writer.h"
#include "gn/ninja_tools.h"
#include "gn/ninja_writer.h"
@@ -56,6 +57,11 @@ const char kSwitchXcodeProject[] = "xcode-project";
const char kSwitchXcodeBuildSystem[] = "xcode-build-system";
const char kSwitchXcodeBuildsystemValueLegacy[] = "legacy";
const char kSwitchXcodeBuildsystemValueNew[] = "new";
+const char kSwitchXcodeConfigurations[] = "xcode-configs";
+const char kSwitchXcodeConfigurationBuildPath[] = "xcode-config-build-dir";
+const char kSwitchXcodeAdditionalFilesPatterns[] =
+ "xcode-additional-files-patterns";
+const char kSwitchXcodeAdditionalFilesRoots[] = "xcode-additional-files-roots";
const char kSwitchJsonFileName[] = "json-file-name";
const char kSwitchJsonIdeScript[] = "json-ide-script";
const char kSwitchJsonIdeScriptArgs[] = "json-ide-script-args";
@@ -224,22 +230,22 @@ bool RunIdeWriter(const std::string& ide,
std::string sln_name;
if (command_line->HasSwitch(kSwitchSln))
- sln_name = command_line->GetSwitchValueASCII(kSwitchSln);
+ sln_name = command_line->GetSwitchValueString(kSwitchSln);
std::string filters;
if (command_line->HasSwitch(kSwitchFilters))
- filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+ filters = command_line->GetSwitchValueString(kSwitchFilters);
std::string win_kit;
if (command_line->HasSwitch(kSwitchIdeValueWinSdk))
- win_kit = command_line->GetSwitchValueASCII(kSwitchIdeValueWinSdk);
+ win_kit = command_line->GetSwitchValueString(kSwitchIdeValueWinSdk);
std::string ninja_extra_args;
if (command_line->HasSwitch(kSwitchNinjaExtraArgs)) {
ninja_extra_args =
- command_line->GetSwitchValueASCII(kSwitchNinjaExtraArgs);
+ command_line->GetSwitchValueString(kSwitchNinjaExtraArgs);
}
std::string ninja_executable;
if (command_line->HasSwitch(kSwitchNinjaExecutable)) {
ninja_executable =
- command_line->GetSwitchValueASCII(kSwitchNinjaExecutable);
+ command_line->GetSwitchValueString(kSwitchNinjaExecutable);
}
bool no_deps = command_line->HasSwitch(kSwitchNoDeps);
bool res = VisualStudioWriter::RunAndWriteFiles(
@@ -253,10 +259,14 @@ bool RunIdeWriter(const std::string& ide,
return res;
} else if (ide == kSwitchIdeValueXcode) {
XcodeWriter::Options options = {
- command_line->GetSwitchValueASCII(kSwitchXcodeProject),
- command_line->GetSwitchValueASCII(kSwitchIdeRootTarget),
- command_line->GetSwitchValueASCII(kSwitchNinjaExecutable),
- command_line->GetSwitchValueASCII(kSwitchFilters),
+ command_line->GetSwitchValueString(kSwitchXcodeProject),
+ command_line->GetSwitchValueString(kSwitchIdeRootTarget),
+ command_line->GetSwitchValueString(kSwitchNinjaExecutable),
+ command_line->GetSwitchValueString(kSwitchFilters),
+ command_line->GetSwitchValueString(kSwitchXcodeConfigurations),
+ command_line->GetSwitchValuePath(kSwitchXcodeConfigurationBuildPath),
+ command_line->GetSwitchValueNative(kSwitchXcodeAdditionalFilesPatterns),
+ command_line->GetSwitchValueNative(kSwitchXcodeAdditionalFilesRoots),
XcodeBuildSystem::kLegacy,
};
@@ -265,7 +275,7 @@ bool RunIdeWriter(const std::string& ide,
}
const std::string build_system =
- command_line->GetSwitchValueASCII(kSwitchXcodeBuildSystem);
+ command_line->GetSwitchValueString(kSwitchXcodeBuildSystem);
if (!build_system.empty()) {
if (build_system == kSwitchXcodeBuildsystemValueNew) {
options.build_system = XcodeBuildSystem::kNew;
@@ -288,7 +298,7 @@ bool RunIdeWriter(const std::string& ide,
} else if (ide == kSwitchIdeValueQtCreator) {
std::string root_target;
if (command_line->HasSwitch(kSwitchIdeRootTarget))
- root_target = command_line->GetSwitchValueASCII(kSwitchIdeRootTarget);
+ root_target = command_line->GetSwitchValueString(kSwitchIdeRootTarget);
bool res = QtCreatorWriter::RunAndWriteFile(build_settings, builder, err,
root_target);
if (res && !quiet) {
@@ -299,14 +309,14 @@ bool RunIdeWriter(const std::string& ide,
return res;
} else if (ide == kSwitchIdeValueJson) {
std::string file_name =
- command_line->GetSwitchValueASCII(kSwitchJsonFileName);
+ command_line->GetSwitchValueString(kSwitchJsonFileName);
if (file_name.empty())
file_name = "project.json";
std::string exec_script =
- command_line->GetSwitchValueASCII(kSwitchJsonIdeScript);
+ command_line->GetSwitchValueString(kSwitchJsonIdeScript);
std::string exec_script_extra_args =
- command_line->GetSwitchValueASCII(kSwitchJsonIdeScriptArgs);
- std::string filters = command_line->GetSwitchValueASCII(kSwitchFilters);
+ command_line->GetSwitchValueString(kSwitchJsonIdeScriptArgs);
+ std::string filters = command_line->GetSwitchValueString(kSwitchFilters);
bool res = JSONProjectWriter::RunAndWriteFiles(
build_settings, builder, file_name, exec_script, exec_script_extra_args,
@@ -342,26 +352,44 @@ bool RunRustProjectWriter(const BuildSettings* build_settings,
return res;
}
-bool RunCompileCommandsWriter(const BuildSettings* build_settings,
- const Builder& builder,
- Err* err) {
+bool RunCompileCommandsWriter(Setup& setup, Err* err) {
+ // The compilation database is written if either the .gn setting is set or if
+ // the command line flag is set. The command line flag takes precedence.
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
+ bool has_legacy_switch =
+ command_line->HasSwitch(kSwitchExportCompileCommands);
+
+ bool has_patterns = !setup.export_compile_commands().empty();
+ if (!has_legacy_switch && !has_patterns)
+ return true; // No compilation database needs to be written.
+
bool quiet = command_line->HasSwitch(switches::kQuiet);
base::ElapsedTimer timer;
- std::string file_name = "compile_commands.json";
- std::string target_filters =
- command_line->GetSwitchValueASCII(kSwitchExportCompileCommands);
+ // The compilation database file goes in the build directory.
+ SourceFile output_file =
+ setup.build_settings().build_dir().ResolveRelativeFile(
+ Value(nullptr, "compile_commands.json"), err);
+ if (output_file.is_null())
+ return false;
+ base::FilePath output_path = setup.build_settings().GetFullPath(output_file);
+
+ std::optional<std::string> legacy_target_filters;
+ if (has_legacy_switch) {
+ legacy_target_filters =
+ command_line->GetSwitchValueString(kSwitchExportCompileCommands);
+ }
- bool res = CompileCommandsWriter::RunAndWriteFiles(
- build_settings, builder, file_name, target_filters, quiet, err);
- if (res && !quiet) {
+ bool ok = CompileCommandsWriter::RunAndWriteFiles(
+ &setup.build_settings(), setup.builder().GetAllResolvedTargets(),
+ setup.export_compile_commands(), legacy_target_filters, output_path, err);
+ if (ok && !quiet) {
OutputString("Generating compile_commands took " +
base::Int64ToString(timer.Elapsed().InMilliseconds()) +
"ms\n");
}
- return res;
+ return ok;
}
bool RunNinjaPostProcessTools(const BuildSettings* build_settings,
@@ -399,7 +427,7 @@ bool RunNinjaPostProcessTools(const BuildSettings* build_settings,
return false;
}
- if(!InvokeNinjaRecompactTool(ninja_executable, build_dir, err)) {
+ if (!InvokeNinjaRecompactTool(ninja_executable, build_dir, err)) {
return false;
}
}
@@ -516,6 +544,34 @@ Xcode Flags
"legacy" - Legacy Build system
"new" - New Build System
+ --xcode-configs=<config_name_list>
+ Configure the list of build configuration supported by the generated
+ project. If specified, must be a list of semicolon-separated strings.
+ If ommitted, a single configuration will be used in the generated
+ project derived from the build directory.
+
+ --xcode-config-build-dir=<string>
+ If present, must be a path relative to the source directory. It will
+ default to $root_out_dir if ommitted. The path is assumed to point to
+ the directory where ninja needs to be invoked. This variable can be
+ used to build for multiple configuration / platform / environment from
+ the same generated Xcode project (assuming that the user has created a
+ gn build directory with the correct args.gn for each).
+
+ One useful value is to use Xcode variables such as '${CONFIGURATION}'
+ or '${EFFECTIVE_PLATFORM}'.
+
+ --xcode-additional-files-patterns=<pattern_list>
+ If present, must be a list of semicolon-separated file patterns. It
+ will be used to add all files matching the pattern located in the
+ source tree to the project. It can be used to add, e.g. documentation
+ files to the project to allow easily edit them.
+
+ --xcode-additional-files-roots=<path_list>
+ If present, must be a list of semicolon-separated paths. It will be used
+ as roots when looking for additional files to add. If ommitted, defaults
+ to "//".
+
--ninja-executable=<string>
Can be used to specify the ninja executable to use when building.
@@ -572,21 +628,37 @@ Compilation Database
replay of individual compilations independent of the build system.
This is an unstable format and likely to change without warning.
+ --add-export-compile-commands=<label_pattern>
+ Adds an additional label pattern (see "gn help label_pattern") of a
+ target to add to the compilation database. This pattern is appended to any
+ list values specified in the export_compile_commands variable in the
+ .gn file (see "gn help dotfile"). This allows the user to add additional
+ targets to the compilation database that the project doesn't add by default.
+
+ To add more than one value, specify this switch more than once. Each
+ invocation adds an additional label pattern.
+
+ Example:
+ --add-export-compile-commands=//tools:my_tool
+ --add-export-compile-commands="//base/*"
+
--export-compile-commands[=<target_name1,target_name2...>]
- Produces a compile_commands.json file in the root of the build directory
- containing an array of “command objects”, where each command object
- specifies one way a translation unit is compiled in the project. If a list
- of target_name is supplied, only targets that are reachable from any
- target in any build file whose name is target_name will be used for
- “command objects” generation, otherwise all available targets will be used.
- This is used for various Clang-based tooling, allowing for the replay of
- individual compilations independent of the build system.
- e.g. "foo" will match:
- - "//path/to/src:foo"
- - "//other/path:foo"
- - "//foo:foo"
+ DEPRECATED https://bugs.chromium.org/p/gn/issues/detail?id=302.
+ Please use --add-export-compile-commands for per-user configuration, and
+ the "export_compile_commands" value in the project-level .gn file (see
+ "gn help dotfile") for per-project configuration.
+
+ Overrides the value of the export_compile_commands in the .gn file (see
+ "gn help dotfile") as well as the --add-export-compile-commands switch.
+
+ Unlike the .gn setting, this switch takes a legacy format which is a list
+ of target names that are matched in any directory. For example, "foo" will
+ match:
+ - "//path/to/src:foo"
+ - "//other/path:foo"
+ - "//foo:foo"
and not match:
- - "//foo:bar"
+ - "//foo:bar"
)";
int RunGen(const std::vector<std::string>& args) {
@@ -613,10 +685,20 @@ int RunGen(const std::vector<std::string>& args) {
base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kSwitchCheck)) {
setup->set_check_public_headers(true);
- if (command_line->GetSwitchValueASCII(kSwitchCheck) == "system")
+ if (command_line->GetSwitchValueString(kSwitchCheck) == "system")
setup->set_check_system_includes(true);
}
+ // If this is a regeneration, replace existing build.ninja and build.ninja.d
+ // with just enough for ninja to call GN and regenerate ninja files. This
+ // removes any potential soon-to-be-dangling references and ensures that
+ // regeneration can be restarted if interrupted.
+ if (command_line->HasSwitch(switches::kRegeneration)) {
+ if (!commands::PrepareForRegeneration(&setup->build_settings())) {
+ return false;
+ }
+ }
+
// Cause the load to also generate the ninja files for each target.
TargetWriteInfo write_info;
setup->builder().set_resolved_and_generated_callback(
@@ -665,15 +747,13 @@ int RunGen(const std::vector<std::string>& args) {
return 1;
if (command_line->HasSwitch(kSwitchIde) &&
- !RunIdeWriter(command_line->GetSwitchValueASCII(kSwitchIde),
+ !RunIdeWriter(command_line->GetSwitchValueString(kSwitchIde),
&setup->build_settings(), setup->builder(), &err)) {
err.PrintToStdout();
return 1;
}
- if (command_line->HasSwitch(kSwitchExportCompileCommands) &&
- !RunCompileCommandsWriter(&setup->build_settings(), setup->builder(),
- &err)) {
+ if (!RunCompileCommandsWriter(*setup, &err)) {
err.PrintToStdout();
return 1;
}
diff --git a/gn/src/gn/command_meta.cc b/gn/src/gn/command_meta.cc
index aa5e88c38ba..25f6de06bbd 100644
--- a/gn/src/gn/command_meta.cc
+++ b/gn/src/gn/command_meta.cc
@@ -87,9 +87,9 @@ int RunMeta(const std::vector<std::string>& args) {
return 1;
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- std::string rebase_dir = cmdline->GetSwitchValueASCII("rebase");
- std::string data_keys_str = cmdline->GetSwitchValueASCII("data");
- std::string walk_keys_str = cmdline->GetSwitchValueASCII("walk");
+ std::string rebase_dir = cmdline->GetSwitchValueString("rebase");
+ std::string data_keys_str = cmdline->GetSwitchValueString("data");
+ std::string walk_keys_str = cmdline->GetSwitchValueString("walk");
std::vector<std::string> inputs(args.begin() + 1, args.end());
@@ -119,9 +119,8 @@ int RunMeta(const std::vector<std::string>& args) {
if (rebase_dir.empty()) {
rebase_source_dir = SourceDir();
}
- std::vector<Value> result =
- WalkMetadata(targets, data_keys, walk_keys, rebase_source_dir,
- &targets_walked, &err);
+ std::vector<Value> result = WalkMetadata(
+ targets, data_keys, walk_keys, rebase_source_dir, &targets_walked, &err);
if (err.has_error()) {
err.PrintToStdout();
return 1;
diff --git a/gn/src/gn/commands.cc b/gn/src/gn/commands.cc
index 24a16b1357b..f81f7abb024 100644
--- a/gn/src/gn/commands.cc
+++ b/gn/src/gn/commands.cc
@@ -4,12 +4,15 @@
#include "gn/commands.h"
+#include <fstream>
#include <optional>
#include "base/command_line.h"
#include "base/environment.h"
+#include "base/files/file_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "gn/builder.h"
#include "gn/config_values_extractors.h"
@@ -17,9 +20,12 @@
#include "gn/item.h"
#include "gn/label.h"
#include "gn/label_pattern.h"
+#include "gn/ninja_build_writer.h"
#include "gn/setup.h"
#include "gn/standard_out.h"
+#include "gn/switches.h"
#include "gn/target.h"
+#include "util/atomic_write.h"
#include "util/build_config.h"
namespace commands {
@@ -127,45 +133,12 @@ bool ResolveStringFromCommandLineInput(
return true;
}
-enum TargetPrintingMode {
- TARGET_PRINT_BUILDFILE,
- TARGET_PRINT_LABEL,
- TARGET_PRINT_OUTPUT,
-};
-
// Retrieves the target printing mode based on the command line flags for the
// current process. Returns true on success. On error, prints a message to the
// console and returns false.
-bool GetTargetPrintingMode(TargetPrintingMode* mode) {
- std::string switch_key = "as";
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-
- if (!cmdline->HasSwitch(switch_key)) {
- // Default to labels.
- *mode = TARGET_PRINT_LABEL;
- return true;
- }
-
- std::string value = cmdline->GetSwitchValueASCII(switch_key);
- if (value == "buildfile") {
- *mode = TARGET_PRINT_BUILDFILE;
- return true;
- }
- if (value == "label") {
- *mode = TARGET_PRINT_LABEL;
- return true;
- }
- if (value == "output") {
- *mode = TARGET_PRINT_OUTPUT;
- return true;
- }
-
- Err(Location(), "Invalid value for \"--as\".",
- "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
- "said \"" +
- value + "\".")
- .PrintToStdout();
- return false;
+bool GetTargetPrintingMode(CommandSwitches::TargetPrintMode* mode) {
+ *mode = CommandSwitches::Get().target_print_mode();
+ return true;
}
// Returns the target type filter based on the command line flags for the
@@ -176,72 +149,20 @@ bool GetTargetPrintingMode(TargetPrintingMode* mode) {
// will never be returned. Code applying the filters should apply Target::ACTION
// to both ACTION and ACTION_FOREACH.
bool GetTargetTypeFilter(Target::OutputType* type) {
- std::string switch_key = "type";
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
-
- if (!cmdline->HasSwitch(switch_key)) {
- // Default to unknown -> no filtering.
- *type = Target::UNKNOWN;
- return true;
- }
-
- std::string value = cmdline->GetSwitchValueASCII(switch_key);
- if (value == "group") {
- *type = Target::GROUP;
- return true;
- }
- if (value == "executable") {
- *type = Target::EXECUTABLE;
- return true;
- }
- if (value == "shared_library") {
- *type = Target::SHARED_LIBRARY;
- return true;
- }
- if (value == "loadable_module") {
- *type = Target::LOADABLE_MODULE;
- return true;
- }
- if (value == "static_library") {
- *type = Target::STATIC_LIBRARY;
- return true;
- }
- if (value == "source_set") {
- *type = Target::SOURCE_SET;
- return true;
- }
- if (value == "copy") {
- *type = Target::COPY_FILES;
- return true;
- }
- if (value == "action") {
- *type = Target::ACTION;
- return true;
- }
-
- Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
- return false;
+ *type = CommandSwitches::Get().target_type();
+ return true;
}
// Applies any testonly filtering specified on the command line to the given
// target set. On failure, prints an error and returns false.
bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
- const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
- std::string testonly_key = "testonly";
+ CommandSwitches::TestonlyMode testonly_mode =
+ CommandSwitches::Get().testonly_mode();
- if (targets->empty() || !cmdline->HasSwitch(testonly_key))
+ if (targets->empty() || testonly_mode == CommandSwitches::TESTONLY_NONE)
return true;
- std::string testonly_value = cmdline->GetSwitchValueASCII(testonly_key);
- bool testonly = false;
- if (testonly_value == "true") {
- testonly = true;
- } else if (testonly_value != "false") {
- Err(Location(), "Bad value for --testonly.",
- "I was expecting --testonly=true or --testonly=false.")
- .PrintToStdout();
- return false;
- }
+ bool testonly = (testonly_mode == CommandSwitches::TESTONLY_TRUE);
// Filter into a copy of the vector, then replace the output.
std::vector<const Target*> result;
@@ -409,6 +330,14 @@ std::optional<HowTargetContainsFile> TargetContainsFile(
return std::nullopt;
}
+std::string ToUTF8(base::FilePath::StringType in) {
+#if defined(OS_WIN)
+ return base::UTF16ToUTF8(in);
+#else
+ return in;
+#endif
+}
+
} // namespace
CommandInfo::CommandInfo()
@@ -438,13 +367,172 @@ const CommandInfoMap& GetCommands() {
INSERT_COMMAND(Outputs)
INSERT_COMMAND(Path)
INSERT_COMMAND(Refs)
- INSERT_COMMAND(CleanStale);
+ INSERT_COMMAND(CleanStale)
#undef INSERT_COMMAND
}
return info_map;
}
+// static
+CommandSwitches CommandSwitches::s_global_switches_ = {};
+
+// static
+bool CommandSwitches::Init(const base::CommandLine& cmdline) {
+ CHECK(!s_global_switches_.is_initialized())
+ << "Only call this once from main()";
+ return s_global_switches_.InitFrom(cmdline);
+}
+
+// static
+const CommandSwitches& CommandSwitches::Get() {
+ CHECK(s_global_switches_.is_initialized())
+ << "Missing previous succesful call to CommandSwitches::Init()";
+ return s_global_switches_;
+}
+
+// static
+CommandSwitches CommandSwitches::Set(CommandSwitches new_switches) {
+ CHECK(s_global_switches_.is_initialized())
+ << "Missing previous succesful call to CommandSwitches::Init()";
+ CommandSwitches result = std::move(s_global_switches_);
+ s_global_switches_ = std::move(new_switches);
+ return result;
+}
+
+bool CommandSwitches::InitFrom(const base::CommandLine& cmdline) {
+ CommandSwitches result;
+ result.initialized_ = true;
+ result.has_quiet_ = cmdline.HasSwitch("a");
+ result.has_force_ = cmdline.HasSwitch("force");
+ result.has_all_ = cmdline.HasSwitch("all");
+ result.has_blame_ = cmdline.HasSwitch("blame");
+ result.has_tree_ = cmdline.HasSwitch("tree");
+ result.has_format_json_ = cmdline.GetSwitchValueString("format") == "json";
+ result.has_default_toolchain_ =
+ cmdline.HasSwitch(switches::kDefaultToolchain);
+
+ result.has_check_generated_ = cmdline.HasSwitch("check-generated");
+ result.has_check_system_ = cmdline.HasSwitch("check-system");
+ result.has_public_ = cmdline.HasSwitch("public");
+ result.has_with_data_ = cmdline.HasSwitch("with-data");
+
+ std::string_view target_print_switch = "as";
+ if (cmdline.HasSwitch(target_print_switch)) {
+ std::string value = cmdline.GetSwitchValueString(target_print_switch);
+ if (value == "buildfile") {
+ result.target_print_mode_ = TARGET_PRINT_BUILDFILE;
+ } else if (value == "label") {
+ result.target_print_mode_ = TARGET_PRINT_LABEL;
+ } else if (value == "output") {
+ result.target_print_mode_ = TARGET_PRINT_OUTPUT;
+ } else {
+ Err(Location(), "Invalid value for \"--as\".",
+ "I was expecting \"buildfile\", \"label\", or \"output\" but you\n"
+ "said \"" +
+ value + "\".")
+ .PrintToStdout();
+ return false;
+ }
+ }
+
+ std::string_view target_type_switch = "type";
+ if (cmdline.HasSwitch(target_type_switch)) {
+ std::string value = cmdline.GetSwitchValueString(target_type_switch);
+ static const struct {
+ const char* name;
+ Target::OutputType type;
+ } kTypes[] = {
+ {"group", Target::GROUP},
+ {"executable", Target::EXECUTABLE},
+ {"shared_library", Target::SHARED_LIBRARY},
+ {"loadable_module", Target::LOADABLE_MODULE},
+ {"static_library", Target::STATIC_LIBRARY},
+ {"source_set", Target::SOURCE_SET},
+ {"copy", Target::COPY_FILES},
+ {"action", Target::ACTION},
+ };
+ bool found = false;
+ for (const auto& type : kTypes) {
+ if (value == type.name) {
+ result.target_type_ = type.type;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ Err(Location(), "Invalid value for \"--type\".").PrintToStdout();
+ return false;
+ }
+ }
+ std::string_view testonly_switch = "testonly";
+ if (cmdline.HasSwitch(testonly_switch)) {
+ std::string value = cmdline.GetSwitchValueString(testonly_switch);
+ if (value == "true") {
+ result.testonly_mode_ = TESTONLY_TRUE;
+ } else if (value == "false") {
+ result.testonly_mode_ = TESTONLY_FALSE;
+ } else {
+ Err(Location(), "Bad value for --testonly.",
+ "I was expecting --testonly=true or --testonly=false.")
+ .PrintToStdout();
+ return false;
+ }
+ }
+
+ result.meta_rebase_dir_ = cmdline.GetSwitchValueString("rebase");
+ result.meta_data_keys_ = cmdline.GetSwitchValueString("data");
+ result.meta_walk_keys_ = cmdline.GetSwitchValueString("walk");
+ *this = result;
+ return true;
+}
+
+bool PrepareForRegeneration(const BuildSettings* settings) {
+ // Write a .d file for the build which references a nonexistent file.
+ // This will make Ninja always mark the build as dirty.
+ base::FilePath build_ninja_d_file(settings->GetFullPath(
+ SourceFile(settings->build_dir().value() + "build.ninja.d")));
+ std::string dummy_depfile("build.ninja.stamp: nonexistent_file.gn\n");
+ if (util::WriteFileAtomically(build_ninja_d_file, dummy_depfile.data(),
+ static_cast<int>(dummy_depfile.size())) == -1) {
+ Err(Location(), std::string("Failed to write build.ninja.d."))
+ .PrintToStdout();
+ return false;
+ }
+
+ // Write a stripped down build.ninja file with just the commands needed
+ // for ninja to call GN and regenerate ninja files.
+ base::FilePath build_ninja_path(settings->GetFullPath(
+ SourceFile(settings->build_dir().value() + "build.ninja")));
+ std::ifstream build_ninja_file(ToUTF8(build_ninja_path.value()));
+ if (!build_ninja_file) {
+ // Couldn't open the build.ninja file.
+ Err(Location(), "Couldn't open build.ninja in this directory.",
+ "Try running \"gn gen\" on it and then re-running \"gn clean\".")
+ .PrintToStdout();
+ return false;
+ }
+ std::string build_commands =
+ NinjaBuildWriter::ExtractRegenerationCommands(build_ninja_file);
+ if (build_commands.empty()) {
+ Err(Location(), "Unexpected build.ninja contents in this directory.",
+ "Try running \"gn gen\" on it and then re-running \"gn clean\".")
+ .PrintToStdout();
+ return false;
+ }
+ // Close build.ninja or else WriteFileAtomically will fail on Windows.
+ build_ninja_file.close();
+ if (util::WriteFileAtomically(build_ninja_path, build_commands.data(),
+ static_cast<int>(build_commands.size())) ==
+ -1) {
+ Err(Location(), std::string("Failed to write build.ninja."))
+ .PrintToStdout();
+ return false;
+ }
+
+ return true;
+}
+
const Target* ResolveTargetFromCommandLineString(
Setup* setup,
const std::string& label_string) {
@@ -583,17 +671,18 @@ void FilterAndPrintTargets(std::vector<const Target*>* targets,
if (!ApplyTypeFilter(targets))
return;
- TargetPrintingMode printing_mode = TARGET_PRINT_LABEL;
+ CommandSwitches::TargetPrintMode printing_mode =
+ CommandSwitches::TARGET_PRINT_LABEL;
if (targets->empty() || !GetTargetPrintingMode(&printing_mode))
return;
switch (printing_mode) {
- case TARGET_PRINT_BUILDFILE:
+ case CommandSwitches::TARGET_PRINT_BUILDFILE:
PrintTargetsAsBuildfiles(*targets, out);
break;
- case TARGET_PRINT_LABEL:
+ case CommandSwitches::TARGET_PRINT_LABEL:
PrintTargetsAsLabels(*targets, out);
break;
- case TARGET_PRINT_OUTPUT:
+ case CommandSwitches::TARGET_PRINT_OUTPUT:
PrintTargetsAsOutputs(*targets, out);
break;
}
diff --git a/gn/src/gn/commands.h b/gn/src/gn/commands.h
index 4824b7904c2..702bf0cd378 100644
--- a/gn/src/gn/commands.h
+++ b/gn/src/gn/commands.h
@@ -23,6 +23,10 @@ class SourceFile;
class Target;
class Toolchain;
+namespace base {
+class CommandLine;
+} // namespace base
+
// Each "Run" command returns the value we should return from main().
namespace commands {
@@ -116,8 +120,136 @@ using CommandInfoMap = std::map<std::string_view, CommandInfo>;
const CommandInfoMap& GetCommands();
+// Command switches as flags and enums -----------------------------------------
+
+// A class that models a set of command-line flags and values that
+// can affect the output of various GN commands. For example --tree
+// can be used with `gn desc <out_dir> <label> deps --tree`.
+//
+// Each flag or value is checked by an accessor method which returns
+// a boolean or an enum.
+//
+// Use CommandSwitches::Get() to get a reference to the current
+// global set of switches for the process.
+//
+// Use CommandSwitches::Set() to update its value. This may be
+// useful when implementing a REPL in GN, where each evaluation
+// might need a different set of command switches.
+class CommandSwitches {
+ public:
+ // Default constructor.
+ CommandSwitches() = default;
+
+ // For --quiet, used by `refs`.
+ bool has_quiet() const { return has_quiet_; }
+
+ // For --force, used by `check`.
+ bool has_force() const { return has_force_; }
+
+ // For --all, used by `desc` and `refs`.
+ bool has_all() const { return has_all_; }
+
+ // For --blame used by `desc`.
+ bool has_blame() const { return has_blame_; }
+
+ // For --tree used by `desc` and `refs`.
+ bool has_tree() const { return has_tree_; }
+
+ // For --format=json used by `desc`.
+ bool has_format_json() const { return has_format_json_; }
+
+ // For --default-toolchain used by `desc`, `refs`.
+ bool has_default_toolchain() const { return has_default_toolchain_; }
+
+ // For --check-generated
+ bool has_check_generated() const { return has_check_generated_; }
+
+ // For --check-system
+ bool has_check_system() const { return has_check_system_; }
+
+ // For --public
+ bool has_public() const { return has_public_; }
+
+ // For --with-data
+ bool has_with_data() const { return has_with_data_; }
+
+ // For --as=(buildtype|label|output).
+ enum TargetPrintMode {
+ TARGET_PRINT_BUILDFILE,
+ TARGET_PRINT_LABEL,
+ TARGET_PRINT_OUTPUT,
+ };
+ TargetPrintMode target_print_mode() const { return target_print_mode_; }
+
+ // For --type=TARGET_TYPE
+ Target::OutputType target_type() const { return target_type_; }
+
+ enum TestonlyMode {
+ TESTONLY_NONE, // no --testonly used.
+ TESTONLY_FALSE, // --testonly=false
+ TESTONLY_TRUE, // --testonly=true
+ };
+ TestonlyMode testonly_mode() const { return testonly_mode_; }
+
+ // For --rebase, --walk and --data in `gn meta`
+ std::string meta_rebase_dir() const { return meta_rebase_dir_; }
+ std::string meta_data_keys() const { return meta_data_keys_; }
+ std::string meta_walk_keys() const { return meta_walk_keys_; }
+
+ // Initialize the global set from a given command line.
+ // Must be called early from main(). On success return true,
+ // on failure return false after printing an error message.
+ static bool Init(const base::CommandLine& cmdline);
+
+ // Retrieve a reference to the current global set of command switches.
+ static const CommandSwitches& Get();
+
+ // Change the current global set of command switches, and return
+ // the previous value.
+ static CommandSwitches Set(CommandSwitches new_switches);
+
+ private:
+ bool is_initialized() const { return initialized_; }
+
+ bool InitFrom(const base::CommandLine&);
+
+ static CommandSwitches s_global_switches_;
+
+ bool initialized_ = false;
+ bool has_quiet_ = false;
+ bool has_force_ = false;
+ bool has_all_ = false;
+ bool has_blame_ = false;
+ bool has_tree_ = false;
+ bool has_format_json_ = false;
+ bool has_default_toolchain_ = false;
+ bool has_check_generated_ = false;
+ bool has_check_system_ = false;
+ bool has_public_ = false;
+ bool has_with_data_ = false;
+
+ TargetPrintMode target_print_mode_ = TARGET_PRINT_LABEL;
+ Target::OutputType target_type_ = Target::UNKNOWN;
+ TestonlyMode testonly_mode_ = TESTONLY_NONE;
+
+ std::string meta_rebase_dir_;
+ std::string meta_data_keys_;
+ std::string meta_walk_keys_;
+};
+
// Helper functions for some commands ------------------------------------------
+// Modifies the existing build.ninja to only contain the commands necessary to
+// run GN and regenerate and build.ninja.d such that build.ninja will be
+// treated as dirty and regenerated.
+//
+// This is used by commands like gen and clean before they modify or delete
+// other ninja files, and ensures that ninja can still call GN if the commands
+// are interrupted before completion.
+//
+// On error, returns false.
+bool PrepareForRegeneration(const BuildSettings* settings);
+
// Given a setup that has already been run and some command-line input,
// resolves that input as a target label and returns the corresponding target.
// On failure, returns null and prints the error to the standard output.
diff --git a/gn/src/gn/compile_commands_writer.cc b/gn/src/gn/compile_commands_writer.cc
index 16f2a1a90e9..23d6029da3f 100644
--- a/gn/src/gn/compile_commands_writer.cc
+++ b/gn/src/gn/compile_commands_writer.cc
@@ -66,7 +66,7 @@ std::string FlagsGetter(RecursiveWriterConfig config,
RecursiveTargetConfigToStream<T>(config, target, getter, writer, out);
base::EscapeJSONString(out.str(), false, &result);
return result;
-};
+}
void SetupCompileFlags(const Target* target,
PathOutput& path_output,
@@ -105,7 +105,7 @@ void SetupCompileFlags(const Target* target,
std::ostringstream out;
WriteOneFlag(config, target, substitution, has_precompiled_headers,
tool_name, getter, opts, path_output, out,
- /*write_substitution=*/false);
+ /*write_substitution=*/false, /*indent=*/false);
base::EscapeJSONString(out.str(), false, &result);
return result;
};
@@ -299,68 +299,125 @@ std::string CompileCommandsWriter::RenderJSON(
bool CompileCommandsWriter::RunAndWriteFiles(
const BuildSettings* build_settings,
- const Builder& builder,
- const std::string& file_name,
- const std::string& target_filters,
- bool quiet,
+ const std::vector<const Target*>& all_targets,
+ const std::vector<LabelPattern>& patterns,
+ const std::optional<std::string>& legacy_target_filters,
+ const base::FilePath& output_path,
Err* err) {
- SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
- Value(nullptr, file_name), err);
- if (output_file.is_null())
+ std::vector<const Target*> to_write = CollectTargets(
+ build_settings, all_targets, patterns, legacy_target_filters, err);
+ if (err->has_error())
return false;
- base::FilePath output_path = build_settings->GetFullPath(output_file);
-
- std::vector<const Target*> all_targets = builder.GetAllResolvedTargets();
-
- std::set<std::string> target_filters_set;
- for (auto& target :
- base::SplitString(target_filters, ",", base::TRIM_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY)) {
- target_filters_set.insert(target);
- }
-
StringOutputBuffer json;
std::ostream output_to_json(&json);
- if (target_filters_set.empty()) {
- OutputJSON(build_settings, all_targets, output_to_json);
- } else {
- std::vector<const Target*> preserved_targets =
- FilterTargets(all_targets, target_filters_set);
- OutputJSON(build_settings, preserved_targets, output_to_json);
- }
+ OutputJSON(build_settings, to_write, output_to_json);
return json.WriteToFileIfChanged(output_path, err);
}
-std::vector<const Target*> CompileCommandsWriter::FilterTargets(
+std::vector<const Target*> CompileCommandsWriter::CollectTargets(
+ const BuildSettings* build_setting,
const std::vector<const Target*>& all_targets,
- const std::set<std::string>& target_filters_set) {
- std::vector<const Target*> preserved_targets;
+ const std::vector<LabelPattern>& patterns,
+ const std::optional<std::string>& legacy_target_filters,
+ Err* err) {
+ if (legacy_target_filters && legacy_target_filters->empty()) {
+ // The legacy filter was specified but has no parameter. This matches
+ // everything and we can skip any other kinds of matching.
+ return all_targets;
+ }
- TargetSet visited;
- for (auto& target : all_targets) {
- if (target_filters_set.count(target->label().name())) {
- VisitDeps(target, &visited);
+ // Collect the first level of target matches. These are the ones that the
+ // patterns match directly.
+ std::vector<const Target*> input_targets;
+ for (const Target* target : all_targets) {
+ if (LabelPattern::VectorMatches(patterns, target->label()))
+ input_targets.push_back(target);
+ }
+
+ // Add in any legacy filter matches.
+ if (legacy_target_filters) {
+ std::vector<const Target*> legacy_matches =
+ FilterLegacyTargets(all_targets, *legacy_target_filters);
+
+ // This can produce some duplicates with the patterns but the "collect
+ // deps" phase will eliminate them.
+ input_targets.insert(input_targets.end(), legacy_matches.begin(),
+ legacy_matches.end());
+ }
+
+ return CollectDepsOfMatches(input_targets);
+}
+
+std::vector<const Target*> CompileCommandsWriter::CollectDepsOfMatches(
+ const std::vector<const Target*>& input_targets) {
+ // The current set of matched targets.
+ TargetSet collected;
+
+ // Represents the next layer of the breadth-first seach. These are all targets
+ // that we haven't checked so far.
+ std::vector<const Target*> frontier;
+
+ // Collect the first level of target matches specified in the input. There may
+ // be duplicates so we still need to do the set checking.
+ // patterns match directly.
+ for (const Target* target : input_targets) {
+ if (!collected.contains(target)) {
+ collected.add(target);
+ frontier.push_back(target);
}
}
- preserved_targets.reserve(visited.size());
- // Preserve the original ordering of all_targets
- // to allow easier debugging and testing.
- for (auto& target : all_targets) {
- if (visited.contains(target)) {
- preserved_targets.push_back(target);
+ // Collects the dependencies for the next level of iteration. This could be
+ // inside the loop but is kept outside to avoid reallocating in every
+ // iteration.
+ std::vector<const Target*> next_frontier;
+
+ // Loop for each level of the search.
+ while (!frontier.empty()) {
+ for (const Target* target : frontier) {
+ // Check the target's dependencies.
+ for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
+ if (!collected.contains(pair.ptr)) {
+ // New dependency found.
+ collected.add(pair.ptr);
+ next_frontier.push_back(pair.ptr);
+ }
+ }
}
+
+ // Swap to the new level and clear out the next one without deallocating the
+ // buffer (in most STL implementations, clear() doesn't free the existing
+ // buffer).
+ std::swap(frontier, next_frontier);
+ next_frontier.clear();
+ }
+
+ // Convert to vector for output.
+ std::vector<const Target*> output;
+ output.reserve(collected.size());
+ for (const Target* target : collected) {
+ output.push_back(target);
}
- return preserved_targets;
+ return output;
}
-void CompileCommandsWriter::VisitDeps(const Target* target,
- TargetSet* visited) {
- if (visited->add(target)) {
- for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
- VisitDeps(pair.ptr, visited);
- }
+std::vector<const Target*> CompileCommandsWriter::FilterLegacyTargets(
+ const std::vector<const Target*>& all_targets,
+ const std::string& target_filter_string) {
+ std::set<std::string> target_filters_set;
+ for (auto& target :
+ base::SplitString(target_filter_string, ",", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY)) {
+ target_filters_set.insert(target);
}
+
+ std::vector<const Target*> result;
+ for (auto& target : all_targets) {
+ if (target_filters_set.count(target->label().name()))
+ result.push_back(target);
+ }
+
+ return result;
}
diff --git a/gn/src/gn/compile_commands_writer.h b/gn/src/gn/compile_commands_writer.h
index 03006c8a9eb..6a006ef887c 100644
--- a/gn/src/gn/compile_commands_writer.h
+++ b/gn/src/gn/compile_commands_writer.h
@@ -5,7 +5,11 @@
#ifndef TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
#define TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
+#include <optional>
+#include <vector>
+
#include "gn/err.h"
+#include "gn/label_pattern.h"
#include "gn/target.h"
class Builder;
@@ -13,30 +17,49 @@ class BuildSettings;
class CompileCommandsWriter {
public:
- // Write compile commands into a json file located by parameter file_name.
+ // Writes a compilation database to the given file name consisting of the
+ // recursive dependencies of all targets that match or are dependencies of
+ // targets that match any given pattern.
+ //
+ // The legacy target filters takes a deprecated list of comma-separated target
+ // names ("target_name1,target_name2...") which are matched against targets in
+ // any directory. This is passed as an optional to encapsulate the legacy
+ // behavior that providing the switch with no patterns matches everything, but
+ // not passing the flag (nullopt for the function parameter) matches nothing.
//
- // Parameter target_filters should be in "target_name1,target_name2..."
- // format. If it is not empty, only targets that are reachable from targets
- // in target_filters are used to generate compile commands.
+ // The union of the legacy matches and the target patterns are used.
//
- // Parameter quiet is not used.
- static bool RunAndWriteFiles(const BuildSettings* build_setting,
- const Builder& builder,
- const std::string& file_name,
- const std::string& target_filters,
- bool quiet,
- Err* err);
+ // TODO(https://bugs.chromium.org/p/gn/issues/detail?id=302):
+ // Remove this legacy target filters behavior.
+ static bool RunAndWriteFiles(
+ const BuildSettings* build_setting,
+ const std::vector<const Target*>& all_targets,
+ const std::vector<LabelPattern>& patterns,
+ const std::optional<std::string>& legacy_target_filters,
+ const base::FilePath& output_path,
+ Err* err);
+
+ // Collects all the targets whose commands should get written as part of
+ // RunAndWriteFiles() (separated out for unit testing).
+ static std::vector<const Target*> CollectTargets(
+ const BuildSettings* build_setting,
+ const std::vector<const Target*>& all_targets,
+ const std::vector<LabelPattern>& patterns,
+ const std::optional<std::string>& legacy_target_filters,
+ Err* err);
static std::string RenderJSON(const BuildSettings* build_settings,
std::vector<const Target*>& all_targets);
- static std::vector<const Target*> FilterTargets(
- const std::vector<const Target*>& all_targets,
- const std::set<std::string>& target_filters_set);
+ // Does a depth-first search of the graph starting at the input target and
+ // collects all recursive dependencies of those targets.
+ static std::vector<const Target*> CollectDepsOfMatches(
+ const std::vector<const Target*>& input_targets);
- private:
- // This function visits the deps graph of a target in a DFS fashion.
- static void VisitDeps(const Target* target, TargetSet* visited);
+ // Performs the legacy target_name filtering.
+ static std::vector<const Target*> FilterLegacyTargets(
+ const std::vector<const Target*>& all_targets,
+ const std::string& target_filter_string);
};
#endif // TOOLS_GN_COMPILE_COMMANDS_WRITER_H_
diff --git a/gn/src/gn/compile_commands_writer_unittest.cc b/gn/src/gn/compile_commands_writer_unittest.cc
index 29e6a573699..2f0294c0530 100644
--- a/gn/src/gn/compile_commands_writer_unittest.cc
+++ b/gn/src/gn/compile_commands_writer_unittest.cc
@@ -19,6 +19,10 @@
namespace {
+bool CompareLabel(const Target* a, const Target* b) {
+ return a->label() < b->label();
+}
+
// InputConversion needs a global scheduler object.
class CompileCommandsTest : public TestWithScheduler {
public:
@@ -29,6 +33,15 @@ class CompileCommandsTest : public TestWithScheduler {
const TestWithScope& setup() { return setup_; }
const Toolchain* toolchain() { return setup_.toolchain(); }
+ // Returns true if the two target vectors contain the same targets, order
+ // independent.
+ bool VectorsEqual(std::vector<const Target*> a,
+ std::vector<const Target*> b) const {
+ std::sort(a.begin(), a.end(), &CompareLabel);
+ std::sort(b.begin(), b.end(), &CompareLabel);
+ return a == b;
+ }
+
private:
TestWithScope setup_;
};
@@ -586,55 +599,138 @@ TEST_F(CompileCommandsTest, EscapedFlags) {
EXPECT_EQ(expected, out);
}
-TEST_F(CompileCommandsTest, CompDBFilter) {
+TEST_F(CompileCommandsTest, CollectTargets) {
+ // Contruct the dependency tree:
+ //
+ // //foo:bar1
+ // //base:base
+ // //foo:bar2
+ // //base:i18n
+ // //base:base
+ // //third_party:icu
+ // //random:random
Err err;
-
std::vector<const Target*> targets;
+
+ Target icu_target(settings(), Label(SourceDir("//third_party/"), "icu"));
+ icu_target.set_output_type(Target::SOURCE_SET);
+ icu_target.visibility().SetPublic();
+ icu_target.SetToolchain(toolchain());
+ ASSERT_TRUE(icu_target.OnResolved(&err));
+ targets.push_back(&icu_target);
+
+ Target base_target(settings(), Label(SourceDir("//base/"), "base"));
+ base_target.set_output_type(Target::SOURCE_SET);
+ base_target.visibility().SetPublic();
+ base_target.SetToolchain(toolchain());
+ ASSERT_TRUE(base_target.OnResolved(&err));
+ targets.push_back(&base_target);
+
+ Target base_i18n(settings(), Label(SourceDir("//base/"), "i18n"));
+ base_i18n.set_output_type(Target::SOURCE_SET);
+ base_i18n.visibility().SetPublic();
+ base_i18n.private_deps().push_back(LabelTargetPair(&icu_target));
+ base_i18n.public_deps().push_back(LabelTargetPair(&base_target));
+ base_i18n.SetToolchain(toolchain());
+ ASSERT_TRUE(base_i18n.OnResolved(&err))
+ << err.message() << " " << err.help_text();
+ targets.push_back(&base_i18n);
+
Target target1(settings(), Label(SourceDir("//foo/"), "bar1"));
target1.set_output_type(Target::SOURCE_SET);
- target1.sources().push_back(SourceFile("//foo/input1.c"));
- target1.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target1.public_deps().push_back(LabelTargetPair(&base_target));
target1.SetToolchain(toolchain());
ASSERT_TRUE(target1.OnResolved(&err));
targets.push_back(&target1);
Target target2(settings(), Label(SourceDir("//foo/"), "bar2"));
target2.set_output_type(Target::SOURCE_SET);
- target2.sources().push_back(SourceFile("//foo/input2.c"));
- target2.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
+ target2.public_deps().push_back(LabelTargetPair(&base_i18n));
target2.SetToolchain(toolchain());
ASSERT_TRUE(target2.OnResolved(&err));
targets.push_back(&target2);
- Target target3(settings(), Label(SourceDir("//foo/"), "bar3"));
- target3.set_output_type(Target::SOURCE_SET);
- target3.sources().push_back(SourceFile("//foo/input3.c"));
- target3.config_values().cflags_c().push_back("-DCONFIG=\"/config\"");
- target3.SetToolchain(toolchain());
- ASSERT_TRUE(target3.OnResolved(&err));
- targets.push_back(&target3);
-
- target1.private_deps().push_back(LabelTargetPair(&target2));
- target1.private_deps().push_back(LabelTargetPair(&target3));
-
- CompileCommandsWriter writer;
-
- std::set<std::string> filter1;
- std::vector<const Target*> test_results1 =
- writer.FilterTargets(targets, filter1);
- ASSERT_TRUE(test_results1.empty());
-
- std::set<std::string> filter2;
- filter2.insert(target1.label().name());
- std::vector<const Target*> test_results2 =
- writer.FilterTargets(targets, filter2);
- ASSERT_EQ(test_results2, targets);
-
- std::set<std::string> filter3;
- filter3.insert(target2.label().name());
- std::vector<const Target*> test_result3 =
- writer.FilterTargets(targets, filter3);
- std::vector<const Target*> expected_results3;
- expected_results3.push_back(&target2);
- ASSERT_EQ(test_result3, expected_results3);
+ Target random_target(settings(), Label(SourceDir("//random/"), "random"));
+ random_target.set_output_type(Target::SOURCE_SET);
+ random_target.SetToolchain(toolchain());
+ ASSERT_TRUE(random_target.OnResolved(&err));
+ targets.push_back(&random_target);
+
+ // Collect everything, the result should match the input.
+ const std::string source_root("/home/me/build/");
+ LabelPattern wildcard_pattern = LabelPattern::GetPattern(
+ SourceDir(), source_root, Value(nullptr, "//*"), &err);
+ ASSERT_FALSE(err.has_error());
+ std::vector<const Target*> output = CompileCommandsWriter::CollectTargets(
+ build_settings(), targets, std::vector<LabelPattern>{wildcard_pattern},
+ std::nullopt, &err);
+ EXPECT_TRUE(VectorsEqual(output, targets));
+
+ // Collect nothing.
+ output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
+ std::vector<LabelPattern>(),
+ std::nullopt, &err);
+ EXPECT_TRUE(output.empty());
+
+ // Collect all deps of "//foo/*".
+ LabelPattern foo_wildcard = LabelPattern::GetPattern(
+ SourceDir(), source_root, Value(nullptr, "//foo/*"), &err);
+ ASSERT_FALSE(err.has_error());
+ output = CompileCommandsWriter::CollectTargets(
+ build_settings(), targets, std::vector<LabelPattern>{foo_wildcard},
+ std::nullopt, &err);
+
+ // The result should be everything except "random".
+ std::sort(output.begin(), output.end(), &CompareLabel);
+ ASSERT_EQ(5u, output.size());
+ EXPECT_EQ(&base_target, output[0]);
+ EXPECT_EQ(&base_i18n, output[1]);
+ EXPECT_EQ(&target1, output[2]);
+ EXPECT_EQ(&target2, output[3]);
+ EXPECT_EQ(&icu_target, output[4]);
+
+ // Collect everything using the legacy filter (present string but empty).
+ output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
+ std::vector<LabelPattern>{},
+ std::string(), &err);
+ EXPECT_TRUE(VectorsEqual(output, targets));
+
+ // Collect all deps of "bar2" using the legacy filter.
+ output = CompileCommandsWriter::CollectTargets(build_settings(), targets,
+ std::vector<LabelPattern>{},
+ std::string("bar2"), &err);
+ std::sort(output.begin(), output.end(), &CompareLabel);
+ ASSERT_EQ(4u, output.size());
+ EXPECT_EQ(&base_target, output[0]);
+ EXPECT_EQ(&base_i18n, output[1]);
+ EXPECT_EQ(&target2, output[2]);
+ EXPECT_EQ(&icu_target, output[3]);
+
+ // Collect all deps of "bar1" and "bar2" using the legacy filter.
+ output = CompileCommandsWriter::CollectTargets(
+ build_settings(), targets, std::vector<LabelPattern>{},
+ std::string("bar2,bar1"), &err);
+ std::sort(output.begin(), output.end(), &CompareLabel);
+ ASSERT_EQ(5u, output.size());
+ EXPECT_EQ(&base_target, output[0]);
+ EXPECT_EQ(&base_i18n, output[1]);
+ EXPECT_EQ(&target1, output[2]);
+ EXPECT_EQ(&target2, output[3]);
+ EXPECT_EQ(&icu_target, output[4]);
+
+ // Combine the legacy (bar1) and pattern (bar2) filters, we should get the
+ // union.
+ LabelPattern foo_bar2 = LabelPattern::GetPattern(
+ SourceDir(), source_root, Value(nullptr, "//foo:bar2"), &err);
+ ASSERT_FALSE(err.has_error());
+ output = CompileCommandsWriter::CollectTargets(
+ build_settings(), targets, std::vector<LabelPattern>{foo_bar2},
+ std::string("bar1"), &err);
+ std::sort(output.begin(), output.end(), &CompareLabel);
+ ASSERT_EQ(5u, output.size());
+ EXPECT_EQ(&base_target, output[0]);
+ EXPECT_EQ(&base_i18n, output[1]);
+ EXPECT_EQ(&target1, output[2]);
+ EXPECT_EQ(&target2, output[3]);
+ EXPECT_EQ(&icu_target, output[4]);
}
diff --git a/gn/src/gn/config_values_generator.h b/gn/src/gn/config_values_generator.h
index d006140102e..ff9cd03d692 100644
--- a/gn/src/gn/config_values_generator.h
+++ b/gn/src/gn/config_values_generator.h
@@ -37,10 +37,10 @@ class ConfigValuesGenerator {
};
// For using in documentation for functions which use this.
-#define CONFIG_VALUES_VARS_HELP \
- " Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,\n" \
- " asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,\n" \
- " libs, precompiled_header, precompiled_source, rustflags,\n" \
- " rustenv, swiftflags, testonly\n"
+#define CONFIG_VALUES_VARS_HELP \
+ " Flags: asmflags, cflags, cflags_c, cflags_cc, cflags_objc,\n" \
+ " cflags_objcc, defines, include_dirs, inputs, ldflags,\n" \
+ " lib_dirs, libs, precompiled_header, precompiled_source,\n" \
+ " rustenv, rustflags, swiftflags, testonly\n"
#endif // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/gn/src/gn/exec_process.cc b/gn/src/gn/exec_process.cc
index 69d631e9ebd..e1e123fbbbe 100644
--- a/gn/src/gn/exec_process.cc
+++ b/gn/src/gn/exec_process.cc
@@ -253,6 +253,8 @@ bool ExecProcess(const base::CommandLine& cmdline,
argv_cstr[argv.size()] = nullptr;
execvp(argv_cstr[0], argv_cstr.get());
_exit(127);
+ // Avoid `fall through` warning on some gcc versions.
+ break;
}
default: // parent
{
diff --git a/gn/src/gn/filesystem_utils.cc b/gn/src/gn/filesystem_utils.cc
index 64713782d2c..d0a1ee0edf2 100644
--- a/gn/src/gn/filesystem_utils.cc
+++ b/gn/src/gn/filesystem_utils.cc
@@ -1006,6 +1006,8 @@ OutputFile GetBuildDirAsOutputFile(const BuildDirContext& context,
result.value().append("gen/");
else if (type == BuildDirType::OBJ)
result.value().append("obj/");
+ else if (type == BuildDirType::PHONY)
+ result.value().append("phony/");
return result;
}
@@ -1023,10 +1025,25 @@ OutputFile GetSubBuildDirAsOutputFile(const BuildDirContext& context,
OutputFile result = GetBuildDirAsOutputFile(context, type);
if (source_dir.is_source_absolute()) {
- // The source dir is source-absolute, so we trim off the two leading
- // slashes to append to the toolchain object directory.
- result.value().append(&source_dir.value()[2],
- source_dir.value().size() - 2);
+ std::string_view build_dir = context.build_settings->build_dir().value();
+ std::string_view source_dir_path = source_dir.value();
+ if (source_dir_path.substr(0, build_dir.size()) == build_dir) {
+ // The source dir is source-absolute, but in the build directory
+ // (e.g. `//out/Debug/gen/src/foo.cc` or
+ // `//out/Debug/toolchain1/gen/foo.cc`), which happens for generated
+ // sources. In this case, remove the build directory prefix, and replace
+ // it with `BUILD_DIR`. This will create results like `obj/BUILD_DIR/gen`
+ // or `toolchain2/obj/BUILD_DIR/toolchain1/gen` which look surprising,
+ // but guarantee unicity.
+ result.value().append("BUILD_DIR/");
+ result.value().append(source_dir_path.substr(build_dir.size()));
+
+ } else {
+ // The source dir is source-absolute, so we trim off the two leading
+ // slashes to append to the toolchain object directory.
+ result.value().append(&source_dir.value()[2],
+ source_dir.value().size() - 2);
+ }
} else {
// System-absolute.
AppendFixedAbsolutePathSuffix(context.build_settings, source_dir, &result);
diff --git a/gn/src/gn/filesystem_utils.h b/gn/src/gn/filesystem_utils.h
index 2c826b5234f..590f9e34d41 100644
--- a/gn/src/gn/filesystem_utils.h
+++ b/gn/src/gn/filesystem_utils.h
@@ -222,6 +222,12 @@ enum class BuildDirType {
// Output file directory.
OBJ,
+
+ // Phony file directory. As the name implies, this is not a real file
+ // directory, but a path that is used for the declaration of phony targets.
+ // This is done to avoid duplicate target names between real files and phony
+ // aliases that point to them.
+ PHONY,
};
// In different contexts, different information is known about the toolchain in
diff --git a/gn/src/gn/format_test_data/084.gn b/gn/src/gn/format_test_data/084.gn
new file mode 100644
index 00000000000..b58b634011e
--- /dev/null
+++ b/gn/src/gn/format_test_data/084.gn
@@ -0,0 +1,34 @@
+# Checks that lists are also sorted if concatenated during an assignment
+other_sources = [
+ "f.cc",
+ "e.cc"
+] + [
+ "h.cc",
+ "g.cc"
+]
+
+other_sources = [
+ ":f",
+ ":e"
+] + [
+ ":h",
+ "g"
+]
+
+executable("foo") {
+ sources = [
+ "b.cc",
+ "a.cc"
+ ] + [
+ "d.cc",
+ "c.cc"
+ ] + other_sources
+
+ deps = [
+ ":b",
+ ":a"
+ ] + [
+ ":d",
+ ":c"
+ ] + other_sources
+}
diff --git a/gn/src/gn/format_test_data/084.golden b/gn/src/gn/format_test_data/084.golden
new file mode 100644
index 00000000000..fa1803794a2
--- /dev/null
+++ b/gn/src/gn/format_test_data/084.golden
@@ -0,0 +1,38 @@
+# Checks that lists are also sorted if concatenated during an assignment
+other_sources = [
+ "e.cc",
+ "f.cc",
+ ] +
+ [
+ "g.cc",
+ "h.cc",
+ ]
+
+other_sources = [
+ ":e",
+ ":f",
+ ] +
+ [
+ ":h",
+ "g",
+ ]
+
+executable("foo") {
+ sources = [
+ "a.cc",
+ "b.cc",
+ ] +
+ [
+ "c.cc",
+ "d.cc",
+ ] + other_sources
+
+ deps = [
+ ":a",
+ ":b",
+ ] +
+ [
+ ":c",
+ ":d",
+ ] + other_sources
+}
diff --git a/gn/src/gn/function_get_target_outputs.cc b/gn/src/gn/function_get_target_outputs.cc
index fa851f3c47c..c9f10c0fe0f 100644
--- a/gn/src/gn/function_get_target_outputs.cc
+++ b/gn/src/gn/function_get_target_outputs.cc
@@ -105,8 +105,9 @@ Value RunGetTargetOutputs(Scope* scope,
}
if (!target) {
+ bool with_toolchain = !scope->settings()->is_default();
*err = Err(function, "Target not found in this context.",
- label.GetUserVisibleName(false) +
+ label.GetUserVisibleName(with_toolchain) +
"\nwas not found. get_target_outputs() can only be used for "
"targets\n"
"previously defined in the current file.");
diff --git a/gn/src/gn/functions.cc b/gn/src/gn/functions.cc
index 6294f79b712..59d97c44212 100644
--- a/gn/src/gn/functions.cc
+++ b/gn/src/gn/functions.cc
@@ -968,6 +968,68 @@ Value RunPrint(Scope* scope,
return Value();
}
+// print_stack_trace -----------------------------------------------------------
+
+const char kPrintStackTrace[] = "print_stack_trace";
+const char kPrintStackTrace_HelpShort[] =
+ "print_stack_trace: Prints a stack trace.";
+const char kPrintStackTrace_Help[] =
+ R"(print_stack_trace: Prints a stack trace.
+
+ Prints the current file location, and all template invocations that led up to
+ this location, to the console.
+
+Examples
+
+ template("foo"){
+ print_stack_trace()
+ }
+ template("bar"){
+ foo(target_name + ".foo") {
+ baz = invoker.baz
+ }
+ }
+ bar("lala") {
+ baz = 42
+ }
+
+ will print out the following:
+
+ print_stack_trace() initiated at //build.gn:2
+ bar("lala") //BUILD.gn:9
+ foo("lala.foo") //BUILD.gn:5
+ print_stack_trace() //BUILD.gn:2
+
+)";
+
+Value RunPrintStackTrace(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ std::string location_str = function->GetRange().begin().Describe(false);
+ std::string toolchain =
+ scope->settings()->toolchain_label().GetUserVisibleName(false);
+ std::string output =
+ "print_stack_trace() initiated at: " + location_str + " using: " + toolchain;
+ output.push_back('\n');
+
+ for (const auto& entry : scope->GetTemplateInvocationEntries()) {
+ output.append(" " + entry.Describe() + "\n");
+ }
+ output.append(" print_stack_trace() " + location_str + "\n");
+
+ const BuildSettings::PrintCallback& cb =
+ scope->settings()->build_settings()->print_callback();
+ if (cb) {
+ cb(output);
+ } else {
+ printf("%s", output.c_str());
+ fflush(stdout);
+ }
+
+ return Value();
+}
+
// split_list ------------------------------------------------------------------
const char kSplitList[] = "split_list";
@@ -1393,6 +1455,7 @@ struct FunctionInfoInitializer {
INSERT_FUNCTION(NotNeeded, false)
INSERT_FUNCTION(Pool, false)
INSERT_FUNCTION(Print, false)
+ INSERT_FUNCTION(PrintStackTrace, false)
INSERT_FUNCTION(ProcessFileTemplate, false)
INSERT_FUNCTION(ReadFile, false)
INSERT_FUNCTION(RebasePath, false)
diff --git a/gn/src/gn/functions_target.cc b/gn/src/gn/functions_target.cc
index 7120b3f7512..4ca2c10550c 100644
--- a/gn/src/gn/functions_target.cc
+++ b/gn/src/gn/functions_target.cc
@@ -15,15 +15,22 @@
#define DEPENDENT_CONFIG_VARS \
" Dependent configs: all_dependent_configs, public_configs\n"
-#define DEPS_VARS " Deps: data_deps, deps, public_deps\n"
+#define DEPS_VARS \
+ " Deps: assert_no_deps, data_deps, deps, public_deps, runtime_deps,\n" \
+ " write_runtime_deps\n"
#define GENERAL_TARGET_VARS \
" General: check_includes, configs, data, friend, inputs, metadata,\n" \
- " output_name, output_extension, public, sources, testonly,\n" \
+ " output_extension, output_name, public, sources, testonly,\n" \
" visibility\n"
#define RUST_VARS \
" Rust variables: aliased_deps, crate_root, crate_name\n"
#define RUST_SHARED_VARS \
" Rust variables: aliased_deps, crate_root, crate_name, crate_type\n"
+#define ACTION_VARS \
+ " Action variables: args, bridge_header, configs, data, depfile,\n" \
+ " framework_dirs, inputs, module_deps, module_name,\n" \
+ " outputs*, pool, response_file_contents, script*,\n" \
+ " sources\n"
namespace functions {
@@ -138,6 +145,12 @@ Inputs
It is recommended you put inputs to your script in the "sources" variable,
and stuff like other Python files required to run your script in the "inputs"
variable.
+
+ Actions can take the configs and public_configs lists, as well as any of the
+ configs variables (defines, include_dirs, etc.) set directly on the target.
+ These behave exactly as they would on a binary target and can be accessed
+ using substitution patterns in the script args (see "gn help args") to
+ implement custom compiler-like tools.
)"
ACTION_DEPS
@@ -160,9 +173,10 @@ File name handling
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources
- * = required
+)" CONFIG_VALUES_VARS_HELP DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+ACTION_VARS
+
+R"( * = required
Example
@@ -230,9 +244,10 @@ File name handling
R"(
Variables
- args, data, data_deps, depfile, deps, inputs, metadata, outputs*, pool,
- response_file_contents, script*, sources*
- * = required
+)" CONFIG_VALUES_VARS_HELP DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+ACTION_VARS
+
+R"( * = required
Example
@@ -294,7 +309,9 @@ const char kBundleData_Help[] =
Variables
- sources*, outputs*, deps, data_deps, metadata, public_deps, visibility
+)" DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+
+R"( Bundle-specific: sources*, outputs*
* = required
Examples
@@ -380,11 +397,13 @@ Code signing
Variables
- bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
- bundle_executable_dir, bundle_deps_filter, deps, data_deps, public_deps,
- visibility, product_type, code_signing_args, code_signing_script,
- code_signing_sources, code_signing_outputs, xcode_extra_attributes,
- xcode_test_application_name, partial_info_plist, metadata
+)" DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+
+R"( Bundle vars: bundle_root_dir, bundle_contents_dir, bundle_resources_dir,
+ bundle_executable_dir, bundle_deps_filter, product_type,
+ code_signing_args, code_signing_script, code_signing_sources,
+ code_signing_outputs, xcode_extra_attributes,
+ xcode_test_application_name, partial_info_plist
Example
@@ -516,6 +535,17 @@ File name handling
(see "gn help source_expansion"). The placeholders will look like
"{{source_name_part}}", for example.
+ If you want to copy the output of a previous build step, the target that
+ generates the file to copy must be reachable from the deps or public_deps of
+ the copy target.
+
+Variables
+
+)" DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+
+R"( Copy variables: sources*, outputs*
+ * = required
+
Examples
# Write a rule that copies a checked-in DLL to the output directory.
@@ -532,6 +562,24 @@ Examples
# names in the gen dir. This will just copy each file.
outputs = [ "$target_gen_dir/{{source_file_part}}" ]
}
+
+ # Copy the output of a generated executable.
+ copy("package_melon") {
+ # This example uses get_label_info() to compute the output directory of the
+ # dependency. This allows the copy rule to work regardless of the toolchain.
+ #
+ # In some cases (particularly actions defined previously in the same file)
+ # you can use get_target_outputs() to get the input file which can eliminate
+ # the assumptions about the output file name of the dependency.
+
+ input_dir = get_label_info("//src/tools/melon", "root_out_dir");
+ sources = [ "$input_dir/melon" ]
+
+ outputs = [ "$target_gen_dir/{{source_file_part}}" ]
+
+ # Depend on the target to build the file before copying.
+ deps = [ "//src/tools/melon" ]
+ }
)";
Value RunCopy(const FunctionCallNode* function,
@@ -584,7 +632,7 @@ const char kGroup_Help[] =
Variables
-)" DEPS_VARS DEPENDENT_CONFIG_VARS
+)" DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
R"(
Example
@@ -811,9 +859,9 @@ Value RunStaticLibrary(Scope* scope,
const char kTarget[] = "target";
const char kTarget_HelpShort[] =
- "target: Declare an target with the given programmatic type.";
+ "target: Declare a target with the given programmatic type.";
const char kTarget_Help[] =
- R"(target: Declare an target with the given programmatic type.
+ R"(target: Declare a target with the given programmatic type.
target(target_type_string, target_name_string) { ... }
@@ -830,6 +878,14 @@ const char kTarget_Help[] =
Is equivalent to:
source_set("doom_melon") {
+Common target variables
+
+)" DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+
+R"(
+ Targets will also have variables specific to that type, see "gn help <type>"
+ for more.
+
Example
if (foo_build_as_shared) {
@@ -897,6 +953,12 @@ const char kGeneratedFile_Help[] =
Collected metadata, if specified, will be returned in postorder of
dependencies. See the example for details.
+Variables
+
+)" DEPENDENT_CONFIG_VARS DEPS_VARS GENERAL_TARGET_VARS
+
+R"( Generated file: contents, data_keys, rebase, walk_keys, output_conversion
+
Example (metadata collection)
Given the following targets defined in //base/BUILD.gn, where A depends on B
@@ -992,16 +1054,7 @@ Example (metadata collection)
"../base/bar.cpp", // from //base:b via //base:a
"../base/foo.cpp", // from //base:a
]
-
-
-Variables
-
- contents
- data_keys
- rebase
- walk_keys
- output_conversion
-)" DEPS_VARS DEPENDENT_CONFIG_VARS;
+)";
Value RunGeneratedFile(Scope* scope,
const FunctionCallNode* function,
diff --git a/gn/src/gn/functions_unittest.cc b/gn/src/gn/functions_unittest.cc
index 40ac5d9ced8..6498415bcce 100644
--- a/gn/src/gn/functions_unittest.cc
+++ b/gn/src/gn/functions_unittest.cc
@@ -457,3 +457,189 @@ TEST(Functions, NotNeeded) {
ASSERT_FALSE(err.has_error())
<< err.message() << err.location().Describe(true);
}
+
+TEST(Template, PrintStackTraceWithOneTemplate) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " print(invoker.foo_value)\n"
+ " print_stack_trace()\n"
+ "}\n"
+ "foo(\"lala\") {\n"
+ " foo_value = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ "lala\n"
+ "42\n"
+ "print_stack_trace() initiated at: //test:4 using: //toolchain:default\n"
+ " foo(\"lala\") //test:6\n"
+ " print_stack_trace() //test:4\n",
+ setup.print_output());
+}
+
+TEST(Template, PrintStackTraceWithNoTemplates) {
+ TestWithScope setup;
+ TestParseInput input("print_stack_trace()\n");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text();
+
+ EXPECT_EQ(
+ "print_stack_trace() initiated at: //test:1 using: //toolchain:default\n"
+ " print_stack_trace() //test:1\n",
+ setup.print_output());
+}
+
+
+TEST(Template, PrintStackTraceWithNestedTemplates) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " print(invoker.foo_value)\n"
+ " print_stack_trace()\n"
+ "}\n"
+ "template(\"baz\") {\n"
+ " foo(\"${target_name}.foo\") {\n"
+ " foo_value = invoker.bar\n"
+ " }\n"
+ "}\n"
+ "baz(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text();
+
+ EXPECT_EQ(
+ "lala.foo\n"
+ "42\n"
+ "print_stack_trace() initiated at: //test:4 using: //toolchain:default\n"
+ " baz(\"lala\") //test:11\n"
+ " foo(\"lala.foo\") //test:7\n"
+ " print_stack_trace() //test:4\n",
+ setup.print_output());
+}
+
+TEST(Template, PrintStackTraceWithNonTemplateScopes) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " if (defined(invoker.foo_value)) {\n"
+ " print(invoker.foo_value)\n"
+ " print_stack_trace()\n"
+ " }\n"
+ "}\n"
+ "template(\"baz\") {\n"
+ " foo(\"${target_name}.foo\") {\n"
+ " foo_value = invoker.bar\n"
+ " }\n"
+ "}\n"
+ "baz(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text();
+
+ EXPECT_EQ(
+ "lala.foo\n"
+ "42\n"
+ "print_stack_trace() initiated at: //test:5 using: //toolchain:default\n"
+ " baz(\"lala\") //test:13\n"
+ " foo(\"lala.foo\") //test:9\n"
+ " print_stack_trace() //test:5\n",
+ setup.print_output());
+}
+
+TEST(Template, PrintStackTraceWithNonTemplateScopesBetweenTemplateInvocations) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " if (defined(invoker.foo_value)) {\n"
+ " print(invoker.foo_value)\n"
+ " print_stack_trace()\n"
+ " }\n"
+ "}\n"
+ "template(\"baz\") {\n"
+ " if (invoker.bar == 42) {\n"
+ " foo(\"${target_name}.foo\") {\n"
+ " foo_value = invoker.bar\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "baz(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text();
+
+ EXPECT_EQ(
+ "lala.foo\n"
+ "42\n"
+ "print_stack_trace() initiated at: //test:5 using: //toolchain:default\n"
+ " baz(\"lala\") //test:15\n"
+ " foo(\"lala.foo\") //test:10\n"
+ " print_stack_trace() //test:5\n",
+ setup.print_output());
+}
+
+TEST(Template, PrintStackTraceWithTemplateDefinedWithinATemplate) {
+ TestWithScope setup;
+ TestParseInput input(
+ "template(\"foo\") {\n"
+ " print(target_name)\n"
+ " if (defined(invoker.foo_value)) {\n"
+ " template(\"foo_internal\") {"
+ " print(target_name)\n"
+ " print(invoker.foo_internal_value)\n"
+ " print_stack_trace()\n"
+ " }\n"
+ " foo_internal(target_name+\".internal\") {"
+ " foo_internal_value = invoker.foo_value\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "template(\"baz\") {\n"
+ " if (invoker.bar == 42) {\n"
+ " foo(\"${target_name}.foo\") {\n"
+ " foo_value = invoker.bar\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "baz(\"lala\") {\n"
+ " bar = 42\n"
+ "}");
+ ASSERT_FALSE(input.has_error());
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << "\n\n" << err.help_text();
+
+ EXPECT_EQ(
+ "lala.foo\n"
+ "lala.foo.internal\n"
+ "42\n"
+ "print_stack_trace() initiated at: //test:6 using: //toolchain:default\n"
+ " baz(\"lala\") //test:19\n"
+ " foo(\"lala.foo\") //test:14\n"
+ " foo_internal(\"lala.foo.internal\") //test:8\n"
+ " print_stack_trace() //test:6\n",
+ setup.print_output());
+}
diff --git a/gn/src/gn/gn_main.cc b/gn/src/gn/gn_main.cc
index e839b322d38..e1a05177bee 100644
--- a/gn/src/gn/gn_main.cc
+++ b/gn/src/gn/gn_main.cc
@@ -63,6 +63,9 @@ int main(int argc, char** argv) {
args.erase(args.begin());
}
+ if (!commands::CommandSwitches::Init(cmdline))
+ return 1;
+
const commands::CommandInfoMap& command_map = commands::GetCommands();
commands::CommandInfoMap::const_iterator found_command =
command_map.find(command);
diff --git a/gn/src/gn/header_checker.cc b/gn/src/gn/header_checker.cc
index 8d2d313fd80..7bad781402f 100644
--- a/gn/src/gn/header_checker.cc
+++ b/gn/src/gn/header_checker.cc
@@ -214,6 +214,16 @@ void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
files_to_public[source].is_public = default_public;
}
+ // If the target includes some .swift files, it may also use a header file
+ // to provide bridging Objective-C code. This header needs to be considered
+ // as a source file with the default visibility.
+ if (target->has_swift_values()) {
+ const SourceFile& bridge_header = target->swift_values().bridge_header();
+ if (!bridge_header.is_null()) {
+ files_to_public[bridge_header].is_public = default_public;
+ }
+ }
+
// Add in the public files, forcing them to public. This may overwrite some
// entries, and it may add new ones.
if (default_public) // List only used when default is not public.
diff --git a/gn/src/gn/header_checker.h b/gn/src/gn/header_checker.h
index d4d2f9f20d5..55c925935d9 100644
--- a/gn/src/gn/header_checker.h
+++ b/gn/src/gn/header_checker.h
@@ -77,6 +77,8 @@ class HeaderChecker : public base::RefCountedThreadSafe<HeaderChecker> {
FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, SourceFileForInclude);
FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
SourceFileForInclude_FileNotFound);
+ FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest,
+ SourceFileForInclude_SwiftBridgeHeader);
FRIEND_TEST_ALL_PREFIXES(HeaderCheckerTest, Friend);
~HeaderChecker();
diff --git a/gn/src/gn/header_checker_unittest.cc b/gn/src/gn/header_checker_unittest.cc
index a290d453183..2a168fd50f6 100644
--- a/gn/src/gn/header_checker_unittest.cc
+++ b/gn/src/gn/header_checker_unittest.cc
@@ -391,6 +391,16 @@ TEST_F(HeaderCheckerTest, SourceFileForInclude_FileNotFound) {
EXPECT_FALSE(err.has_error());
}
+TEST_F(HeaderCheckerTest, SourceFileForInclude_SwiftBridgeHeader) {
+ const SourceFile bridge_header("//a/bridge_header.h");
+ a_.swift_values().bridge_header() = bridge_header;
+ auto checker = CreateChecker();
+
+ HeaderChecker::FileMap file_map;
+ checker->AddTargetToFileMap(&a_, &file_map);
+ EXPECT_NE(file_map.find(bridge_header), file_map.end());
+}
+
TEST_F(HeaderCheckerTest, Friend) {
// Note: we have a public dependency chain A -> B -> C set up already.
InputFile input_file(SourceFile("//some_file.cc"));
diff --git a/gn/src/gn/import_manager.cc b/gn/src/gn/import_manager.cc
index a4928bf10c2..69f44b48a79 100644
--- a/gn/src/gn/import_manager.cc
+++ b/gn/src/gn/import_manager.cc
@@ -125,14 +125,14 @@ bool ImportManager::DoImport(const SourceFile& file,
if (TracingEnabled() &&
TicksDelta(import_block_end, import_block_begin).InMilliseconds() >
kImportBlockTraceThresholdMS) {
- auto* import_block_trace =
- new TraceItem(TraceItem::TRACE_IMPORT_BLOCK, file.value(),
- std::this_thread::get_id());
+ auto import_block_trace = std::make_unique<TraceItem>(
+ TraceItem::TRACE_IMPORT_BLOCK, file.value(),
+ std::this_thread::get_id());
import_block_trace->set_begin(import_block_begin);
import_block_trace->set_end(import_block_end);
import_block_trace->set_toolchain(
scope->settings()->toolchain_label().GetUserVisibleName(false));
- AddTrace(import_block_trace);
+ AddTrace(std::move(import_block_trace));
}
}
diff --git a/gn/src/gn/json_project_writer.cc b/gn/src/gn/json_project_writer.cc
index a54e2548dc4..27368ff82d2 100644
--- a/gn/src/gn/json_project_writer.cc
+++ b/gn/src/gn/json_project_writer.cc
@@ -313,7 +313,6 @@ class SimpleJSONWriter {
if (line_end == std::string_view::npos) {
out_ << json;
- ;
comma_ = {};
return;
}
@@ -455,6 +454,9 @@ StringOutputBuffer JSONProjectWriter::GenerateJSON(
base::Value toolchain{base::Value::Type::DICTIONARY};
const auto& tools = tool_chain_kv.second->tools();
for (const auto& tool_kv : tools) {
+ // Do not list builtin tools
+ if (tool_kv.second->AsBuiltin())
+ continue;
base::Value tool_info{base::Value::Type::DICTIONARY};
auto setIfNotEmptry = [&](const auto& key, const auto& value) {
if (value.size())
diff --git a/gn/src/gn/ninja_action_target_writer.cc b/gn/src/gn/ninja_action_target_writer.cc
index bfceef90b8f..60d4204d8d3 100644
--- a/gn/src/gn/ninja_action_target_writer.cc
+++ b/gn/src/gn/ninja_action_target_writer.cc
@@ -80,9 +80,12 @@ void NinjaActionTargetWriter::Run() {
if (target_->action_values().has_depfile()) {
WriteDepfile(SourceFile());
}
- if (target_->action_values().pool().ptr) {
+
+ WriteNinjaVariablesForAction();
+
+ if (target_->pool().ptr) {
out_ << " pool = ";
- out_ << target_->action_values().pool().ptr->GetNinjaName(
+ out_ << target_->pool().ptr->GetNinjaName(
settings_->default_toolchain_label());
out_ << std::endl;
}
@@ -149,7 +152,8 @@ std::string NinjaActionTargetWriter::WriteRuleDefinition() {
out_ << std::endl;
out_ << " description = ACTION " << target_label << std::endl;
out_ << " restat = 1" << std::endl;
- const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
+ const Tool* tool =
+ target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
if (tool && tool->pool().ptr) {
out_ << " pool = ";
out_ << tool->pool().ptr->GetNinjaName(
@@ -204,13 +208,14 @@ void NinjaActionTargetWriter::WriteSourceRules(
target_, settings_, sources[i],
target_->action_values().rsp_file_contents().required_types(),
args_escape_options, out_);
+ WriteNinjaVariablesForAction();
if (target_->action_values().has_depfile()) {
WriteDepfile(sources[i]);
}
- if (target_->action_values().pool().ptr) {
+ if (target_->pool().ptr) {
out_ << " pool = ";
- out_ << target_->action_values().pool().ptr->GetNinjaName(
+ out_ << target_->pool().ptr->GetNinjaName(
settings_->default_toolchain_label());
out_ << std::endl;
}
@@ -248,3 +253,10 @@ void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
out_ << " deps = gcc" << std::endl;
}
}
+
+void NinjaActionTargetWriter::WriteNinjaVariablesForAction() {
+ SubstitutionBits subst;
+ target_->action_values().args().FillRequiredTypes(&subst);
+ WriteRustCompilerVars(subst, /*indent=*/true, /*always_write=*/false);
+ WriteCCompilerVars(subst, /*indent=*/true, /*respect_source_types=*/false);
+}
diff --git a/gn/src/gn/ninja_action_target_writer.h b/gn/src/gn/ninja_action_target_writer.h
index ef91f4e1024..58b9489a9d0 100644
--- a/gn/src/gn/ninja_action_target_writer.h
+++ b/gn/src/gn/ninja_action_target_writer.h
@@ -50,6 +50,10 @@ class NinjaActionTargetWriter : public NinjaTargetWriter {
void WriteDepfile(const SourceFile& source);
+ // Writes variables that we make available to all actions, irrespective
+ // of whether they're associated with a specific source file.
+ void WriteNinjaVariablesForAction();
+
// Path output writer that doesn't do any escaping or quoting. It does,
// however, convert slashes. Used for
// computing intermediate strings.
diff --git a/gn/src/gn/ninja_action_target_writer_unittest.cc b/gn/src/gn/ninja_action_target_writer_unittest.cc
index 80b125b8f30..d57f1492b13 100644
--- a/gn/src/gn/ninja_action_target_writer_unittest.cc
+++ b/gn/src/gn/ninja_action_target_writer_unittest.cc
@@ -5,6 +5,7 @@
#include <algorithm>
#include <sstream>
+#include "gn/config.h"
#include "gn/ninja_action_target_writer.h"
#include "gn/pool.h"
#include "gn/substitution_list.h"
@@ -90,7 +91,7 @@ TEST(NinjaActionTargetWriter, ActionNoSourcesConsole) {
Label(SourceDir("//"), "console", setup.toolchain()->label().dir(),
setup.toolchain()->label().name()));
pool.set_depth(1);
- target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
+ target.set_pool(LabelPtrPair<Pool>(&pool));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -376,7 +377,7 @@ TEST(NinjaActionTargetWriter, ForEachWithPool) {
Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
setup.toolchain()->label().name()));
pool.set_depth(5);
- target.action_values().set_pool(LabelPtrPair<Pool>(&pool));
+ target.set_pool(LabelPtrPair<Pool>(&pool));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -486,3 +487,63 @@ TEST(NinjaActionTargetWriter, NoTransitiveHardDeps) {
EXPECT_EQ(expected_linux, out.str());
}
}
+
+TEST(NinjaActionTargetWriter, SeesConfig) {
+ Err err;
+ TestWithScope setup;
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+
+ Config farcfg(setup.settings(), Label(SourceDir("//foo/"), "farcfg"));
+ farcfg.own_values().defines().push_back("MY_DEFINE2");
+ farcfg.own_values().cflags().push_back("-isysroot=baz");
+ farcfg.visibility().SetPublic();
+ ASSERT_TRUE(farcfg.OnResolved(&err));
+
+ Config cfgdep(setup.settings(), Label(SourceDir("//foo/"), "cfgdep"));
+ cfgdep.own_values().rustenv().push_back("my_rustenv");
+ cfgdep.own_values().include_dirs().push_back(SourceDir("//my_inc_dir/"));
+ cfgdep.own_values().defines().push_back("MY_DEFINE");
+ cfgdep.visibility().SetPublic();
+ cfgdep.configs().push_back(LabelConfigPair(&farcfg));
+ ASSERT_TRUE(cfgdep.OnResolved(&err));
+
+ Target foo(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ foo.set_output_type(Target::ACTION);
+ foo.visibility().SetPublic();
+ foo.sources().push_back(SourceFile("//foo/input1.txt"));
+ foo.action_values().set_script(SourceFile("//foo/script.py"));
+ foo.action_values().args() = SubstitutionList::MakeForTest(
+ "{{rustenv}}", "{{include_dirs}}", "{{defines}}", "{{cflags}}");
+ foo.configs().push_back(LabelConfigPair(&cfgdep));
+ foo.SetToolchain(setup.toolchain());
+ foo.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/foo.out");
+ ASSERT_TRUE(foo.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaActionTargetWriter writer(&foo, out);
+ writer.Run();
+
+ const char expected[] =
+ "rule __foo_foo___rule\n"
+ // These come from the args.
+ " command = /usr/bin/python ../../foo/script.py ${rustenv} "
+ "${include_dirs} ${defines} ${cflags}\n"
+ " description = ACTION //foo:foo()\n"
+ " restat = 1\n"
+ "\n"
+ "build foo.out: __foo_foo___rule | ../../foo/script.py"
+ " ../../foo/input1.txt\n"
+ " rustenv = my_rustenv\n"
+ " defines = -DMY_DEFINE -DMY_DEFINE2\n"
+ " include_dirs = -I../../my_inc_dir\n"
+ " cflags = -isysroot=baz\n"
+ "\n"
+ "build obj/foo/foo.stamp: stamp foo.out\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/gn/src/gn/ninja_binary_target_writer.cc b/gn/src/gn/ninja_binary_target_writer.cc
index 48f485ad181..b960cc01bf5 100644
--- a/gn/src/gn/ninja_binary_target_writer.cc
+++ b/gn/src/gn/ninja_binary_target_writer.cc
@@ -15,6 +15,7 @@
#include "gn/ninja_rust_binary_target_writer.h"
#include "gn/ninja_target_command_util.h"
#include "gn/ninja_utils.h"
+#include "gn/pool.h"
#include "gn/settings.h"
#include "gn/string_utils.h"
#include "gn/substitution_writer.h"
@@ -156,7 +157,7 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
// don't link at all.
bool can_link_libs = target_->IsFinal();
- if (can_link_libs && dep->swift_values().builds_module())
+ if (can_link_libs && dep->builds_swift_module())
classified_deps->swiftmodule_deps.push_back(dep);
if (target_->source_types_used().RustSourceUsed() &&
@@ -275,7 +276,8 @@ void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
const std::vector<OutputFile>& extra_deps,
const std::vector<OutputFile>& order_only_deps,
const char* tool_name,
- const std::vector<OutputFile>& outputs) {
+ const std::vector<OutputFile>& outputs,
+ bool can_write_source_info) {
out_ << "build";
path_output_.WriteFiles(out_, outputs);
@@ -292,6 +294,16 @@ void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
path_output_.WriteFiles(out_, order_only_deps);
}
out_ << std::endl;
+
+ if (!sources.empty() && can_write_source_info) {
+ out_ << " "
+ << "source_file_part = " << sources[0].GetName();
+ out_ << std::endl;
+ out_ << " "
+ << "source_name_part = "
+ << FindFilenameNoExtension(&sources[0].value());
+ out_ << std::endl;
+ }
}
void NinjaBinaryTargetWriter::WriteCustomLinkerFlags(
@@ -409,3 +421,12 @@ void NinjaBinaryTargetWriter::WriteSwiftModules(
swiftmodule_path_output.WriteFile(out, swiftmodule);
}
}
+
+void NinjaBinaryTargetWriter::WritePool(std::ostream& out) {
+ if (target_->pool().ptr) {
+ out << " pool = ";
+ out << target_->pool().ptr->GetNinjaName(
+ settings_->default_toolchain_label());
+ out << std::endl;
+ }
+}
diff --git a/gn/src/gn/ninja_binary_target_writer.h b/gn/src/gn/ninja_binary_target_writer.h
index e53e28eb245..c7b6eead554 100644
--- a/gn/src/gn/ninja_binary_target_writer.h
+++ b/gn/src/gn/ninja_binary_target_writer.h
@@ -63,7 +63,8 @@ class NinjaBinaryTargetWriter : public NinjaTargetWriter {
const std::vector<OutputFile>& extra_deps,
const std::vector<OutputFile>& order_only_deps,
const char* tool_name,
- const std::vector<OutputFile>& outputs);
+ const std::vector<OutputFile>& outputs,
+ bool can_write_source_info = true);
void WriteLinkerFlags(std::ostream& out,
const Tool* tool,
@@ -75,6 +76,7 @@ class NinjaBinaryTargetWriter : public NinjaTargetWriter {
void WriteSwiftModules(std::ostream& out,
const Tool* tool,
const std::vector<OutputFile>& swiftmodules);
+ void WritePool(std::ostream& out);
void AddSourceSetFiles(const Target* source_set,
UniqueVector<OutputFile>* obj_files) const;
diff --git a/gn/src/gn/ninja_binary_target_writer_unittest.cc b/gn/src/gn/ninja_binary_target_writer_unittest.cc
index 970aa82f7d1..5fd066b68f8 100644
--- a/gn/src/gn/ninja_binary_target_writer_unittest.cc
+++ b/gn/src/gn/ninja_binary_target_writer_unittest.cc
@@ -42,7 +42,11 @@ TEST_F(NinjaBinaryTargetWriterTest, CSources) {
"target_output_name = bar\n"
"\n"
"build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
"obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
@@ -137,6 +141,8 @@ TEST_F(NinjaBinaryTargetWriterTest, Inputs) {
"\n"
"build obj/foo/bar.source1.o: cxx ../../foo/source1.cc | "
"../../foo/input1 ../../foo/input2\n"
+ " source_file_part = source1.cc\n"
+ " source_name_part = source1\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.source1.o\n";
std::string out_str = out.str();
@@ -172,8 +178,12 @@ TEST_F(NinjaBinaryTargetWriterTest, Inputs) {
"../../foo/input1 ../../foo/input2\n"
"build obj/foo/bar.source1.o: cxx ../../foo/source1.cc | "
"obj/foo/bar.inputs.stamp\n"
+ " source_file_part = source1.cc\n"
+ " source_name_part = source1\n"
"build obj/foo/bar.source2.o: cxx ../../foo/source2.cc | "
"obj/foo/bar.inputs.stamp\n"
+ " source_file_part = source2.cc\n"
+ " source_name_part = source2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.source1.o "
"obj/foo/bar.source2.o\n";
diff --git a/gn/src/gn/ninja_build_writer.cc b/gn/src/gn/ninja_build_writer.cc
index 908c5df15e9..e39e5591a40 100644
--- a/gn/src/gn/ninja_build_writer.cc
+++ b/gn/src/gn/ninja_build_writer.cc
@@ -29,6 +29,7 @@
#include "gn/switches.h"
#include "gn/target.h"
#include "gn/trace.h"
+#include "util/atomic_write.h"
#include "util/build_config.h"
#include "util/exe_path.h"
@@ -110,7 +111,7 @@ base::CommandLine GetSelfInvocationCommandLine(
i->first != switches::kDotfile && i->first != switches::kArgs) {
std::string escaped_value =
EscapeString(FilePathToUTF8(i->second), escape_shell, nullptr);
- cmdline.AppendSwitchASCII(i->first, escaped_value);
+ cmdline.AppendSwitch(i->first, escaped_value);
}
}
@@ -261,16 +262,16 @@ bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
if (!gen.Run(err))
return false;
- // Unconditionally write the build.ninja. Ninja's build-out-of-date checking
- // will re-run GN when any build input is newer than build.ninja, so any time
- // the build is updated, build.ninja's timestamp needs to updated also, even
- // if the contents haven't been changed.
+ // Unconditionally write the build.ninja. Ninja's build-out-of-date
+ // checking will re-run GN when any build input is newer than build.ninja, so
+ // any time the build is updated, build.ninja's timestamp needs to updated
+ // also, even if the contents haven't been changed.
base::FilePath ninja_file_name(build_settings->GetFullPath(
SourceFile(build_settings->build_dir().value() + "build.ninja")));
base::CreateDirectory(ninja_file_name.DirName());
std::string ninja_contents = file.str();
- if (base::WriteFile(ninja_file_name, ninja_contents.data(),
- static_cast<int>(ninja_contents.size())) !=
+ if (util::WriteFileAtomically(ninja_file_name, ninja_contents.data(),
+ static_cast<int>(ninja_contents.size())) !=
static_cast<int>(ninja_contents.size()))
return false;
@@ -278,14 +279,43 @@ bool NinjaBuildWriter::RunAndWriteFile(const BuildSettings* build_settings,
base::FilePath dep_file_name(build_settings->GetFullPath(
SourceFile(build_settings->build_dir().value() + "build.ninja.d")));
std::string dep_contents = depfile.str();
- if (base::WriteFile(dep_file_name, dep_contents.data(),
- static_cast<int>(dep_contents.size())) !=
+ if (util::WriteFileAtomically(dep_file_name, dep_contents.data(),
+ static_cast<int>(dep_contents.size())) !=
static_cast<int>(dep_contents.size()))
return false;
+ // Finally, write the empty build.ninja.stamp file. This is the output
+ // expected by the first of the two ninja rules used to accomplish
+ // regeneration.
+
+ base::FilePath stamp_file_name(build_settings->GetFullPath(
+ SourceFile(build_settings->build_dir().value() + "build.ninja.stamp")));
+ std::string stamp_contents;
+ if (util::WriteFileAtomically(stamp_file_name, stamp_contents.data(),
+ static_cast<int>(stamp_contents.size())) !=
+ static_cast<int>(stamp_contents.size()))
+ return false;
+
return true;
}
+// static
+std::string NinjaBuildWriter::ExtractRegenerationCommands(
+ std::istream& build_ninja_in) {
+ std::ostringstream out;
+ int num_blank_lines = 0;
+ for (std::string line; std::getline(build_ninja_in, line);) {
+ out << line << '\n';
+ if (line.empty())
+ ++num_blank_lines;
+ // Warning: Significant magic number. Represents the number of blank lines
+ // in the ninja rules written by NinjaBuildWriter::WriteNinjaRules below.
+ if (num_blank_lines == 4)
+ return out.str();
+ }
+ return std::string{};
+}
+
void NinjaBuildWriter::WriteNinjaRules() {
out_ << "ninja_required_version = "
<< build_settings_->ninja_required_version().Describe() << "\n\n";
@@ -295,16 +325,25 @@ void NinjaBuildWriter::WriteNinjaRules() {
out_ << " pool = console\n";
out_ << " description = Regenerating ninja files\n\n";
- // This rule will regenerate the ninja files when any input file has changed.
- out_ << "build build.ninja: gn\n"
+ // A comment is left in the build.ninja explaining the two statement setup to
+ // avoid confusion, since build.ninja is written earlier than the ninja rules
+ // might make someone think.
+ out_ << "# The 'gn' rule also writes build.ninja, unbeknownst to ninja. The\n"
+ << "# build.ninja edge is separate to prevent ninja from deleting it\n"
+ << "# (due to depfile usage) if interrupted. gn uses atomic writes to\n"
+ << "# ensure that build.ninja is always valid even if interrupted.\n"
+ << "build build.ninja.stamp: gn\n"
<< " generator = 1\n"
- << " depfile = build.ninja.d\n";
+ << " depfile = build.ninja.d\n"
+ << "\n"
+ << "build build.ninja: phony build.ninja.stamp\n"
+ << " generator = 1\n";
- // Input build files. These go in the ".d" file. If we write them as
- // dependencies in the .ninja file itself, ninja will expect the files to
- // exist and will error if they don't. When files are listed in a depfile,
- // missing files are ignored.
- dep_out_ << "build.ninja:";
+ // Input build files. These go in the ".d" file. If we write them
+ // as dependencies in the .ninja file itself, ninja will expect
+ // the files to exist and will error if they don't. When files are
+ // listed in a depfile, missing files are ignored.
+ dep_out_ << "build.ninja.stamp:";
// Other files read by the build.
std::vector<base::FilePath> other_files = g_scheduler->GetGenDependencies();
@@ -349,8 +388,10 @@ void NinjaBuildWriter::WriteAllPools() {
}
for (const Target* target : all_targets_) {
- if (target->output_type() == Target::ACTION) {
- const LabelPtrPair<Pool>& pool = target->action_values().pool();
+ if (target->IsBinary() ||
+ target->output_type() == Target::ACTION ||
+ target->output_type() == Target::ACTION_FOREACH) {
+ const LabelPtrPair<Pool>& pool = target->pool();
if (pool.ptr)
used_pools.insert(pool.ptr);
}
@@ -446,6 +487,10 @@ Phony rules
short name. Use "ninja doom_melon" to compile the
"//tools/fruit:doom_melon" executable.
+ Note that for Apple platforms, create_bundle targets with a product_type
+ of "com.apple.product-type.application" are considered as executable
+ for this rule (as they define application bundles).
+
5. The short names of all targets if there is only one target with that
short name.
@@ -509,6 +554,13 @@ bool NinjaBuildWriter::WritePhonyAndAllRules(Err* err) {
exes_counts.last_seen = target;
}
+ if (target->output_type() == Target::CREATE_BUNDLE &&
+ target->bundle_data().is_application()) {
+ Counts& exes_counts = exes[short_name];
+ exes_counts.count++;
+ exes_counts.last_seen = target;
+ }
+
// Find targets in "important" directories.
const std::string& dir_string = label.dir().value();
if (dir_string.size() == 2 && dir_string[0] == '/' &&
diff --git a/gn/src/gn/ninja_build_writer.h b/gn/src/gn/ninja_build_writer.h
index e77f7401c98..c21769a572c 100644
--- a/gn/src/gn/ninja_build_writer.h
+++ b/gn/src/gn/ninja_build_writer.h
@@ -48,9 +48,36 @@ class NinjaBuildWriter {
const Builder& builder,
Err* err);
+ // Extracts from an existing build.ninja file's contents the commands
+ // necessary to run GN and regenerate build.ninja.
+ //
+ // The regeneration rules live at the top of the build.ninja file and their
+ // specific contents are an internal detail of NinjaBuildWriter. Used by
+ // commands::PrepareForRegeneration.
+ //
+ // On error, returns an empty string.
+ static std::string ExtractRegenerationCommands(std::istream& build_ninja_in);
+
bool Run(Err* err);
private:
+ // WriteNinjaRules writes the rules that ninja uses to regenerate its own
+ // build files, used whenever a build input file has changed.
+ //
+ // Ninja file regeneration is accomplished by two separate build statements.
+ // This is necessary to work around ninja's behavior of deleting all output
+ // files of a build edge if the edge uses a depfile and is interrupted before
+ // it can complete. Previously, interrupting regeneration would cause ninja to
+ // delete build.ninja, losing any flags/build settings passed to gen
+ // previously and requiring the user to manually 'gen' again.
+ //
+ // The workaround involves misleading ninja about when the build.ninja file is
+ // actually written. The first build statement runs the actual 'gen
+ // --regeneration' command, writing "build.ninja" (and .d and .stamp) and
+ // lists the "build.ninja.d" depfile to automatically trigger regeneration as
+ // needed, but does not list "build.ninja" as an output. The second
+ // statement's stated output is "build.ninja", but it simply uses the phony
+ // rule to refer to the first statement.
void WriteNinjaRules();
void WriteAllPools();
bool WriteSubninjas(Err* err);
diff --git a/gn/src/gn/ninja_build_writer_unittest.cc b/gn/src/gn/ninja_build_writer_unittest.cc
index fab082f2af9..ba6af4f0476 100644
--- a/gn/src/gn/ninja_build_writer_unittest.cc
+++ b/gn/src/gn/ninja_build_writer_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <fstream>
#include <sstream>
#include "base/command_line.h"
@@ -53,7 +54,7 @@ TEST_F(NinjaBuildWriterTest, GetSelfInvocationCommandLine) {
// (from //out/Debug to //).
setup.build_settings()->SetRootPath(root_realpath);
cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
- EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueString(switches::kRoot));
EXPECT_FALSE(cmd_out.HasSwitch(switches::kDotfile));
// If --root is . and --dotfile is foo/.gn, then --dotfile also needs
@@ -61,9 +62,9 @@ TEST_F(NinjaBuildWriterTest, GetSelfInvocationCommandLine) {
setup.build_settings()->SetRootPath(root_realpath);
setup.build_settings()->set_dotfile_name(gn_realpath);
cmd_out = GetSelfInvocationCommandLine(setup.build_settings());
- EXPECT_EQ("../..", cmd_out.GetSwitchValueASCII(switches::kRoot));
+ EXPECT_EQ("../..", cmd_out.GetSwitchValueString(switches::kRoot));
EXPECT_EQ("../../testdot.gn",
- cmd_out.GetSwitchValueASCII(switches::kDotfile));
+ cmd_out.GetSwitchValueString(switches::kDotfile));
}
TEST_F(NinjaBuildWriterTest, TwoTargets) {
@@ -113,7 +114,7 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
target_baz.action_values().outputs() = SubstitutionList::MakeForTest(
"//out/Debug/out5.out", "//out/Debug/out6.out");
target_baz.SetToolchain(&other_toolchain);
- target_baz.action_values().set_pool(
+ target_baz.set_pool(
LabelPtrPair<Pool>(&another_regular_pool));
ASSERT_TRUE(target_baz.OnResolved(&err));
@@ -143,10 +144,13 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
ASSERT_TRUE(writer.Run(&err));
const char expected_rule_gn[] = "rule gn\n";
- const char expected_build_ninja[] =
- "build build.ninja: gn\n"
+ const char expected_build_ninja_stamp[] =
+ "build build.ninja.stamp: gn\n"
" generator = 1\n"
" depfile = build.ninja.d\n";
+ const char expected_build_ninja[] =
+ "build build.ninja: phony build.ninja.stamp\n"
+ " generator = 1\n";
const char expected_other_pool[] =
"pool other_toolchain_another_depth_pool\n"
" depth = 7\n"
@@ -172,6 +176,7 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
<< "Expected to find: " << expected << "\n" \
<< "Within: " << out_str
EXPECT_SNIPPET(expected_rule_gn);
+ EXPECT_SNIPPET(expected_build_ninja_stamp);
EXPECT_SNIPPET(expected_build_ninja);
EXPECT_SNIPPET(expected_other_pool);
EXPECT_SNIPPET(expected_toolchain);
@@ -184,6 +189,88 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
EXPECT_EQ(std::string::npos, out_str.find("pool console"));
}
+TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands) {
+ TestWithScope setup;
+ Err err;
+
+ Target target_foo(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target_foo.set_output_type(Target::ACTION);
+ target_foo.action_values().set_script(SourceFile("//foo/script.py"));
+ target_foo.action_values().outputs() = SubstitutionList::MakeForTest(
+ "//out/Debug/out1.out", "//out/Debug/out2.out");
+ target_foo.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target_foo.OnResolved(&err));
+
+ // The console pool must be in the default toolchain.
+ Pool console_pool(setup.settings(), Label(SourceDir("//"), "console",
+ setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ console_pool.set_depth(1);
+
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+ used_toolchains[setup.settings()] = setup.toolchain();
+
+ std::vector<const Target*> targets = {&target_foo};
+
+ std::stringstream ninja_out;
+ std::ostringstream depfile_out;
+
+ NinjaBuildWriter writer(setup.build_settings(), used_toolchains, targets,
+ setup.toolchain(), targets, ninja_out, depfile_out);
+ ASSERT_TRUE(writer.Run(&err));
+
+ const char expected_rule_gn[] = "rule gn\n";
+ const char expected_build_ninja_stamp[] = "build build.ninja.stamp: gn\n";
+ const char expected_build_ninja[] =
+ "build build.ninja: phony build.ninja.stamp\n";
+ const char expected_target[] = "build bar:";
+ const char expected_root_target[] = "build all: phony $\n";
+ const char expected_default[] = "default all\n";
+ std::string ninja_out_str = ninja_out.str();
+#define EXPECT_SNIPPET(str, expected) \
+ EXPECT_NE(std::string::npos, str.find(expected)) \
+ << "Expected to find: " << expected << "\n" \
+ << "Within: " << str
+#define EXPECT_NO_SNIPPET(str, unexpected) \
+ EXPECT_EQ(std::string::npos, str.find(unexpected)) \
+ << "Found unexpected: " << unexpected << "\n" \
+ << "Within: " << str
+ EXPECT_SNIPPET(ninja_out_str, expected_rule_gn);
+ EXPECT_SNIPPET(ninja_out_str, expected_build_ninja_stamp);
+ EXPECT_SNIPPET(ninja_out_str, expected_build_ninja);
+ EXPECT_SNIPPET(ninja_out_str, expected_target);
+ EXPECT_SNIPPET(ninja_out_str, expected_root_target);
+ EXPECT_SNIPPET(ninja_out_str, expected_default);
+
+ std::string commands =
+ NinjaBuildWriter::ExtractRegenerationCommands(ninja_out);
+ EXPECT_SNIPPET(commands, expected_rule_gn);
+ EXPECT_SNIPPET(commands, expected_build_ninja_stamp);
+ EXPECT_SNIPPET(commands, expected_build_ninja);
+ EXPECT_NO_SNIPPET(commands, expected_target);
+ EXPECT_NO_SNIPPET(commands, expected_root_target);
+ EXPECT_NO_SNIPPET(commands, expected_default);
+
+#undef EXPECT_SNIPPET
+#undef EXPECT_NO_SNIPPET
+}
+
+TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_DefaultStream) {
+ std::ifstream ninja_in;
+ EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
+}
+
+TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_StreamError) {
+ std::ifstream ninja_in("/does/not/exist");
+ EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
+}
+
+TEST_F(NinjaBuildWriterTest, ExtractRegenerationCommands_IncompleteNinja) {
+ std::stringstream ninja_in;
+ ninja_in << "foo\nbar\nbaz\nbif\n";
+ EXPECT_EQ(NinjaBuildWriter::ExtractRegenerationCommands(ninja_in), "");
+}
+
TEST_F(NinjaBuildWriterTest, SpaceInDepfile) {
TestWithScope setup;
Err err;
@@ -208,7 +295,7 @@ TEST_F(NinjaBuildWriterTest, SpaceInDepfile) {
ASSERT_TRUE(writer.Run(&err));
EXPECT_EQ(depfile_out.str(),
- "build.ninja: ../../path\\ with\\ space/BUILD.gn");
+ "build.ninja.stamp: ../../path\\ with\\ space/BUILD.gn");
}
TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
diff --git a/gn/src/gn/ninja_c_binary_target_writer.cc b/gn/src/gn/ninja_c_binary_target_writer.cc
index 5b518b3edd1..1ef0475f910 100644
--- a/gn/src/gn/ninja_c_binary_target_writer.cc
+++ b/gn/src/gn/ninja_c_binary_target_writer.cc
@@ -21,6 +21,7 @@
#include "gn/general_tool.h"
#include "gn/ninja_target_command_util.h"
#include "gn/ninja_utils.h"
+#include "gn/pool.h"
#include "gn/scheduler.h"
#include "gn/settings.h"
#include "gn/string_utils.h"
@@ -224,42 +225,8 @@ void NinjaCBinaryTargetWriter::WriteCompilerVars(
const std::vector<ModuleDep>& module_dep_info) {
const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
- // Defines.
- if (subst.used.count(&CSubstitutionDefines)) {
- out_ << CSubstitutionDefines.ninja_name << " =";
- RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
- target_, &ConfigValues::defines,
- DefineWriter(), out_);
- out_ << std::endl;
- }
-
- // Framework search path.
- if (subst.used.count(&CSubstitutionFrameworkDirs)) {
- const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
-
- out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
- PathOutput framework_dirs_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
- FrameworkDirsWriter(framework_dirs_output,
- tool->framework_dir_switch()),
- out_);
- out_ << std::endl;
- }
-
- // Include directories.
- if (subst.used.count(&CSubstitutionIncludeDirs)) {
- out_ << CSubstitutionIncludeDirs.ninja_name << " =";
- PathOutput include_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
- IncludeWriter(include_path_output), out_);
- out_ << std::endl;
- }
+ WriteCCompilerVars(subst, /*indent=*/false,
+ /*respect_source_types_used=*/true);
if (!module_dep_info.empty()) {
// TODO(scottmg): Currently clang modules only working for C++.
@@ -272,89 +239,6 @@ void NinjaCBinaryTargetWriter::WriteCompilerVars(
}
}
- bool has_precompiled_headers =
- target_->config_values().has_precompiled_headers();
-
- EscapeOptions opts = GetFlagOptions();
- if (target_->source_types_used().Get(SourceFile::SOURCE_S) ||
- target_->source_types_used().Get(SourceFile::SOURCE_ASM)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionAsmFlags, false, Tool::kToolNone,
- &ConfigValues::asmflags, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
- target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
- target_->source_types_used().Get(SourceFile::SOURCE_M) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
- false, Tool::kToolNone, &ConfigValues::cflags, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_C)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
- has_precompiled_headers, CTool::kCToolCc,
- &ConfigValues::cflags_c, opts, path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
- target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsCc, has_precompiled_headers,
- CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
- out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_M)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsObjC, has_precompiled_headers,
- CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionCFlagsObjCc, has_precompiled_headers,
- CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
- path_output_, out_);
- }
- if (target_->source_types_used().SwiftSourceUsed()) {
- if (subst.used.count(&CSubstitutionSwiftModuleName)) {
- out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
- EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
- out_ << std::endl;
- }
-
- if (subst.used.count(&CSubstitutionSwiftBridgeHeader)) {
- out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
- if (!target_->swift_values().bridge_header().is_null()) {
- path_output_.WriteFile(out_, target_->swift_values().bridge_header());
- } else {
- out_ << R"("")";
- }
- out_ << std::endl;
- }
-
- if (subst.used.count(&CSubstitutionSwiftModuleDirs)) {
- // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
- // are generated in the same directory).
- UniqueVector<SourceDir> swiftmodule_dirs;
- for (const Target* dep : target_->swift_values().modules())
- swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
-
- out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
- PathOutput swiftmodule_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
- for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
- swiftmodule_path_writer(swiftmodule_dir, out_);
- }
- out_ << std::endl;
- }
-
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
- &ConfigValues::swiftflags, opts, path_output_, out_);
- }
-
WriteSharedVars(subst);
}
@@ -601,6 +485,7 @@ void NinjaCBinaryTargetWriter::WriteSources(
WriteCompilerBuildLine({source}, deps, order_only_deps, tool_name,
tool_outputs);
+ WritePool(out_);
}
// It's theoretically possible for a compiler to produce more than one
@@ -681,14 +566,14 @@ void NinjaCBinaryTargetWriter::WriteSwiftSources(
WriteCompilerBuildLine(target_->sources(), input_deps,
swift_order_only_deps.vector(), tool->name(),
- {swiftmodule_output_file});
+ {swiftmodule_output_file}, false);
if (!additional_outputs.empty()) {
out_ << std::endl;
WriteCompilerBuildLine(
{swiftmodule_output_file.AsSourceFile(settings_->build_settings())},
input_deps, swift_order_only_deps.vector(),
- GeneralTool::kGeneralToolStamp, additional_outputs);
+ GeneralTool::kGeneralToolStamp, additional_outputs, false);
}
}
@@ -775,11 +660,12 @@ void NinjaCBinaryTargetWriter::WriteLinkerStuff(
std::copy(input_deps.begin(), input_deps.end(),
std::back_inserter(implicit_deps));
- // Any C++ target which depends on a Rust .rlib has to depend on its
- // entire tree of transitive rlibs.
+ // Any C++ target which depends on a Rust .rlib has to depend on its entire
+ // tree of transitive rlibs found inside the linking target (which excludes
+ // rlibs only depended on inside a shared library dependency).
std::vector<OutputFile> transitive_rustlibs;
if (target_->IsFinal()) {
- for (const auto* dep : target_->rust_transitive_libs().GetOrdered()) {
+ for (const auto* dep : target_->inherited_libraries().GetOrdered()) {
if (dep->output_type() == Target::RUST_LIBRARY) {
transitive_rustlibs.push_back(dep->dependency_output_file());
implicit_deps.push_back(dep->dependency_output_file());
@@ -794,7 +680,7 @@ void NinjaCBinaryTargetWriter::WriteLinkerStuff(
swiftmodules.push_back(dep->swift_values().module_output_file());
implicit_deps.push_back(dep->swift_values().module_output_file());
}
- if (target_->swift_values().builds_module()) {
+ if (target_->builds_swift_module()) {
swiftmodules.push_back(target_->swift_values().module_output_file());
implicit_deps.push_back(target_->swift_values().module_output_file());
}
@@ -850,6 +736,7 @@ void NinjaCBinaryTargetWriter::WriteLinkerStuff(
WriteOutputSubstitutions();
WriteLibsList("solibs", solibs);
WriteLibsList("rlibs", transitive_rustlibs);
+ WritePool(out_);
}
void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
diff --git a/gn/src/gn/ninja_c_binary_target_writer_unittest.cc b/gn/src/gn/ninja_c_binary_target_writer_unittest.cc
index 61bc517f966..38df42b0491 100644
--- a/gn/src/gn/ninja_c_binary_target_writer_unittest.cc
+++ b/gn/src/gn/ninja_c_binary_target_writer_unittest.cc
@@ -10,6 +10,7 @@
#include "gn/config.h"
#include "gn/ninja_target_command_util.h"
+#include "gn/pool.h"
#include "gn/scheduler.h"
#include "gn/target.h"
#include "gn/test_with_scheduler.h"
@@ -53,7 +54,11 @@ TEST_F(NinjaCBinaryTargetWriterTest, SourceSet) {
"target_output_name = bar\n"
"\n"
"build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
"obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
@@ -204,6 +209,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, StaticLibrary) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"\n"
"build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
" arflags = --asdf\n"
@@ -250,6 +257,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"\n"
"build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
"obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
@@ -279,6 +288,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"\n"
"build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
"|| obj/foo/libbaz.a\n"
@@ -330,8 +341,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
"\n"
"build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
" || obj/foo/action.stamp\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
" || obj/foo/action.stamp\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
// The order-only dependency here is stricly unnecessary since the
@@ -391,16 +406,18 @@ TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
"target_out_dir = obj/foo\n"
"target_output_name = gen_obj\n"
"\n"
- "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
+ "build obj/BUILD_DIR/gen_obj.generated.o: cxx generated.cc"
" || obj/foo/generate.stamp\n"
+ " source_file_part = generated.cc\n"
+ " source_name_part = generated\n"
"\n"
- "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/gen_obj.generated.o"
+ "build obj/foo/gen_obj.stamp: stamp obj/BUILD_DIR/gen_obj.generated.o"
// The order-only dependency here is strictly unnecessary since the
// sources list this as an order-only dep.
" || obj/foo/generate.stamp\n";
std::string obj_str = obj_out.str();
- EXPECT_EQ(obj_expected, obj_str);
+ EXPECT_EQ(std::string(obj_expected), obj_str);
// A shared library depends on gen_obj, having corresponding header for
// generated obj.
@@ -426,7 +443,7 @@ TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
"target_output_name = libgen_lib\n"
"\n"
"\n"
- "build ./libgen_lib.so: solink obj/out/Debug/gen_obj.generated.o"
+ "build ./libgen_lib.so: solink obj/BUILD_DIR/gen_obj.generated.o"
// The order-only dependency here is strictly unnecessary since
// obj/out/Debug/gen_obj.generated.o has dependency to
// obj/foo/gen_obj.stamp
@@ -467,6 +484,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
"target_output_name = final_target\n"
"\n"
"build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
"\n"
"build ./final_target: link obj/foo/final_target.main.o"
" ./libgen_lib.so\n"
@@ -613,7 +632,11 @@ TEST_F(NinjaCBinaryTargetWriterTest, EmptyOutputExtension) {
"target_output_name = shlib\n"
"\n"
"build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build ./shlib: solink obj/foo/shlib.input1.o "
"obj/foo/shlib.input2.o\n"
@@ -667,6 +690,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
"target_output_name = inter\n"
"\n"
"build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
+ " source_file_part = inter.cc\n"
+ " source_name_part = inter\n"
"\n"
"build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
"./data_target\n";
@@ -700,6 +725,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
"target_output_name = exe\n"
"\n"
"build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ " source_file_part = final.cc\n"
+ " source_name_part = final\n"
"\n"
"build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
"obj/foo/inter.stamp\n"
@@ -739,6 +766,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ " source_file_part = sources.cc\n"
+ " source_name_part = sources\n"
"\n"
"build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
" ldflags = /DEF:../../foo/bar.def\n"
@@ -776,6 +805,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ " source_file_part = sources.cc\n"
+ " source_name_part = sources\n"
"\n"
"build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
" ldflags =\n"
@@ -811,6 +842,8 @@ TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
"target_output_name = exe\n"
"\n"
"build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ " source_file_part = final.cc\n"
+ " source_name_part = final\n"
"\n"
"build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
" ldflags =\n"
@@ -888,8 +921,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
"\n"
"build withpch/obj/foo/no_pch_target.input1.o: "
"withpch_cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build withpch/obj/foo/no_pch_target.input2.o: "
"withpch_cc ../../foo/input2.c\n"
+ " source_file_part = input2.c\n"
+ " source_name_part = input2\n"
"\n"
"build withpch/obj/foo/no_pch_target.stamp: "
"withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
@@ -930,20 +967,28 @@ TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
// Compile the precompiled source files with /Yc.
"build withpch/obj/build/pch_target.precompile.c.o: "
"withpch_cc ../../build/precompile.cc\n"
+ " source_file_part = precompile.cc\n"
+ " source_name_part = precompile\n"
" cflags_c = ${cflags_c} /Ycbuild/precompile.h\n"
"\n"
"build withpch/obj/build/pch_target.precompile.cc.o: "
"withpch_cxx ../../build/precompile.cc\n"
+ " source_file_part = precompile.cc\n"
+ " source_name_part = precompile\n"
" cflags_cc = ${cflags_cc} /Ycbuild/precompile.h\n"
"\n"
"build withpch/obj/foo/pch_target.input1.o: "
"withpch_cxx ../../foo/input1.cc | "
// Explicit dependency on the PCH build step.
"withpch/obj/build/pch_target.precompile.cc.o\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build withpch/obj/foo/pch_target.input2.o: "
"withpch_cc ../../foo/input2.c | "
// Explicit dependency on the PCH build step.
"withpch/obj/build/pch_target.precompile.c.o\n"
+ " source_file_part = input2.c\n"
+ " source_name_part = input2\n"
"\n"
"build withpch/obj/foo/pch_target.stamp: withpch_stamp "
"withpch/obj/foo/pch_target.input1.o "
@@ -1022,8 +1067,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
"\n"
"build withpch/obj/foo/no_pch_target.input1.o: "
"withpch_cxx ../../foo/input1.cc\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build withpch/obj/foo/no_pch_target.input2.o: "
"withpch_cc ../../foo/input2.c\n"
+ " source_file_part = input2.c\n"
+ " source_name_part = input2\n"
"\n"
"build withpch/obj/foo/no_pch_target.stamp: "
"withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
@@ -1062,20 +1111,28 @@ TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
// Compile the precompiled sources with -x <lang>.
"build withpch/obj/build/pch_target.precompile.h-c.gch: "
"withpch_cc ../../build/precompile.h\n"
+ " source_file_part = precompile.h\n"
+ " source_name_part = precompile\n"
" cflags_c = -std=c99 -x c-header\n"
"\n"
"build withpch/obj/build/pch_target.precompile.h-cc.gch: "
"withpch_cxx ../../build/precompile.h\n"
+ " source_file_part = precompile.h\n"
+ " source_name_part = precompile\n"
" cflags_cc = -x c++-header\n"
"\n"
"build withpch/obj/foo/pch_target.input1.o: "
"withpch_cxx ../../foo/input1.cc | "
// Explicit dependency on the PCH build step.
"withpch/obj/build/pch_target.precompile.h-cc.gch\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build withpch/obj/foo/pch_target.input2.o: "
"withpch_cc ../../foo/input2.c | "
// Explicit dependency on the PCH build step.
"withpch/obj/build/pch_target.precompile.h-c.gch\n"
+ " source_file_part = input2.c\n"
+ " source_name_part = input2\n"
"\n"
"build withpch/obj/foo/pch_target.stamp: "
"withpch_stamp withpch/obj/foo/pch_target.input1.o "
@@ -1140,8 +1197,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
"\n"
"build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
" | ../../foo/input.data\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
" | ../../foo/input.data\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
"obj/foo/bar.input2.o\n";
@@ -1211,8 +1272,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
" ../../foo/input1.data ../../foo/input2.data\n"
"build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
" | obj/foo/bar.inputs.stamp\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
" | obj/foo/bar.inputs.stamp\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
"obj/foo/bar.input2.o\n";
@@ -1261,8 +1326,12 @@ TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
" ../../foo/input1.data ../../foo/input2.data ../../foo/input3.data\n"
"build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
" | obj/foo/bar.inputs.stamp\n"
+ " source_file_part = input1.cc\n"
+ " source_name_part = input1\n"
"build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
" | obj/foo/bar.inputs.stamp\n"
+ " source_file_part = input2.cc\n"
+ " source_name_part = input2\n"
"\n"
"build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
"obj/foo/bar.input2.o\n";
@@ -1272,232 +1341,883 @@ TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
}
// Test linking of Rust dependencies into C targets.
-TEST_F(NinjaCBinaryTargetWriterTest, RustDeps) {
+TEST_F(NinjaCBinaryTargetWriterTest, RustStaticLib) {
Err err;
TestWithScope setup;
- {
- Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
- library_target.set_output_type(Target::STATIC_LIBRARY);
- library_target.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- library_target.sources().push_back(lib);
- library_target.source_types_used().Set(SourceFile::SOURCE_RS);
- library_target.rust_values().set_crate_root(lib);
- library_target.rust_values().crate_name() = "foo";
- library_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(library_target.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//bar/bar.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.private_deps().push_back(LabelTargetPair(&library_target));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
+ Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ library_target.set_output_type(Target::STATIC_LIBRARY);
+ library_target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ library_target.sources().push_back(lib);
+ library_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ library_target.rust_values().set_crate_root(lib);
+ library_target.rust_values().crate_name() = "foo";
+ library_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(library_target.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//bar/bar.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&library_target));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
- "\n"
- "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
- " ldflags =\n"
- " libs =\n"
- " frameworks =\n"
- " swiftmodules =\n"
- " output_extension = \n"
- " output_dir = \n";
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
+ " source_file_part = bar.cc\n"
+ " source_name_part = bar\n"
+ "\n"
+ "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
- {
- Target rlib_target(setup.settings(), Label(SourceDir("//baz/"), "lib"));
- rlib_target.set_output_type(Target::RUST_LIBRARY);
- rlib_target.visibility().SetPublic();
- SourceFile bazlib("//baz/lib.rs");
- rlib_target.sources().push_back(bazlib);
- rlib_target.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib_target.rust_values().set_crate_root(bazlib);
- rlib_target.rust_values().crate_name() = "lib";
- rlib_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib_target.OnResolved(&err));
-
- Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
- library_target.set_output_type(Target::STATIC_LIBRARY);
- library_target.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- library_target.sources().push_back(lib);
- library_target.source_types_used().Set(SourceFile::SOURCE_RS);
- library_target.rust_values().set_crate_root(lib);
- library_target.rust_values().crate_name() = "foo";
- library_target.public_deps().push_back(LabelTargetPair(&rlib_target));
- library_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(library_target.OnResolved(&err));
-
- Target rlib_target2(setup.settings(), Label(SourceDir("//qux/"), "lib2"));
- rlib_target2.set_output_type(Target::RUST_LIBRARY);
- rlib_target2.visibility().SetPublic();
- SourceFile quxlib("//qux/lib.rs");
- rlib_target2.sources().push_back(quxlib);
- rlib_target2.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib_target2.rust_values().set_crate_root(quxlib);
- rlib_target2.rust_values().crate_name() = "lib2";
- rlib_target2.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib_target2.OnResolved(&err));
-
- Target rlib_target3(setup.settings(), Label(SourceDir("//quxqux/"), "lib3"));
- rlib_target3.set_output_type(Target::RUST_LIBRARY);
- rlib_target3.visibility().SetPublic();
- SourceFile quxquxlib("//quxqux/lib.rs");
- rlib_target3.sources().push_back(quxquxlib);
- rlib_target3.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib_target3.rust_values().set_crate_root(quxlib);
- rlib_target3.rust_values().crate_name() = "lib3";
- rlib_target3.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib_target3.OnResolved(&err));
-
- Target procmacro(setup.settings(),
- Label(SourceDir("//quuxmacro/"), "procmacro"));
- procmacro.set_output_type(Target::RUST_PROC_MACRO);
- procmacro.visibility().SetPublic();
- SourceFile procmacrolib("//procmacro/lib.rs");
- procmacro.sources().push_back(procmacrolib);
- procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
- procmacro.public_deps().push_back(LabelTargetPair(&rlib_target2));
- procmacro.public_deps().push_back(LabelTargetPair(&rlib_target3));
- procmacro.rust_values().set_crate_root(procmacrolib);
- procmacro.rust_values().crate_name() = "procmacro";
- procmacro.SetToolchain(setup.toolchain());
- ASSERT_TRUE(procmacro.OnResolved(&err));
-
- Target rlib_target4(setup.settings(), Label(SourceDir("//quux/"), "lib4"));
- rlib_target4.set_output_type(Target::RUST_LIBRARY);
- rlib_target4.visibility().SetPublic();
- SourceFile quuxlib("//quux/lib.rs");
- rlib_target4.sources().push_back(quuxlib);
- rlib_target4.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib_target4.public_deps().push_back(LabelTargetPair(&rlib_target2));
- // Transitive proc macros should not impact C++ targets; we're
- // adding one to ensure the ninja instructions below are unaffected.
- rlib_target4.public_deps().push_back(LabelTargetPair(&procmacro));
- rlib_target4.rust_values().set_crate_root(quuxlib);
- rlib_target4.rust_values().crate_name() = "lib4";
- rlib_target4.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib_target4.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//bar/bar.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.private_deps().push_back(LabelTargetPair(&library_target));
- target.private_deps().push_back(LabelTargetPair(&rlib_target4));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
+// Test linking of Rust dependencies into C targets.
+TEST_F(NinjaCBinaryTargetWriterTest, RlibInLibrary) {
+ Err err;
+ TestWithScope setup;
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
+ // This source_set() is depended on by an rlib, which is a private dep of a
+ // static lib.
+ Target priv_sset_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//priv_sset_in_staticlib/"), "priv_sset_in_staticlib"));
+ priv_sset_in_staticlib.set_output_type(Target::SOURCE_SET);
+ priv_sset_in_staticlib.visibility().SetPublic();
+ priv_sset_in_staticlib.sources().push_back(
+ SourceFile("//priv_sset_in_staticlib/lib.cc"));
+ priv_sset_in_staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ priv_sset_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_sset_in_staticlib.OnResolved(&err));
+
+ // This source_set() is depended on by an rlib, which is a public dep of a
+ // static lib.
+ Target pub_sset_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//pub_sset_in_staticlib/"), "pub_sset_in_staticlib"));
+ pub_sset_in_staticlib.set_output_type(Target::SOURCE_SET);
+ pub_sset_in_staticlib.visibility().SetPublic();
+ pub_sset_in_staticlib.sources().push_back(
+ SourceFile("//pub_sset_in_staticlib/lib.cc"));
+ pub_sset_in_staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pub_sset_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_sset_in_staticlib.OnResolved(&err));
+
+ // This source_set() is depended on by an rlib, which is a private dep of a
+ // shared lib.
+ Target priv_sset_in_dylib(
+ setup.settings(),
+ Label(SourceDir("//priv_sset_in_dylib/"), "priv_sset_in_dylib"));
+ priv_sset_in_dylib.set_output_type(Target::SOURCE_SET);
+ priv_sset_in_dylib.visibility().SetPublic();
+ priv_sset_in_dylib.sources().push_back(
+ SourceFile("//priv_sset_in_dylib/lib.cc"));
+ priv_sset_in_dylib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ priv_sset_in_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_sset_in_dylib.OnResolved(&err));
+
+ // This source_set() is depended on by an rlib, which is a public dep of a
+ // shared lib.
+ Target pub_sset_in_dylib(
+ setup.settings(),
+ Label(SourceDir("//pub_sset_in_dylib"), "pub_sset_in_dylib"));
+ pub_sset_in_dylib.set_output_type(Target::SOURCE_SET);
+ pub_sset_in_dylib.visibility().SetPublic();
+ pub_sset_in_dylib.sources().push_back(
+ SourceFile("//pub_sset_in_dylib/lib.cc"));
+ pub_sset_in_dylib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pub_sset_in_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_sset_in_dylib.OnResolved(&err));
+
+ Target priv_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//priv_in_staticlib/"), "priv_in_staticlib"));
+ priv_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ priv_in_staticlib.visibility().SetPublic();
+ SourceFile priv_in_staticlib_root("//priv_in_staticlib/lib.rs");
+ priv_in_staticlib.sources().push_back(priv_in_staticlib_root);
+ priv_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_staticlib.rust_values().set_crate_root(priv_in_staticlib_root);
+ priv_in_staticlib.rust_values().crate_name() = "priv_in_staticlib";
+ priv_in_staticlib.SetToolchain(setup.toolchain());
+ priv_in_staticlib.private_deps().push_back(
+ LabelTargetPair(&priv_sset_in_staticlib));
+ ASSERT_TRUE(priv_in_staticlib.OnResolved(&err));
+
+ Target pub_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//pub_in_staticlib/"), "pub_in_staticlib"));
+ pub_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_staticlib.visibility().SetPublic();
+ SourceFile pub_in_staticlib_root("//pub_in_staticlib/lib.rs");
+ pub_in_staticlib.sources().push_back(pub_in_staticlib_root);
+ pub_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_staticlib.rust_values().set_crate_root(pub_in_staticlib_root);
+ pub_in_staticlib.rust_values().crate_name() = "pub_in_staticlib";
+ pub_in_staticlib.SetToolchain(setup.toolchain());
+ pub_in_staticlib.private_deps().push_back(
+ LabelTargetPair(&pub_sset_in_staticlib));
+ ASSERT_TRUE(pub_in_staticlib.OnResolved(&err));
+
+ Target priv_in_dylib(setup.settings(),
+ Label(SourceDir("//priv_in_dylib/"), "priv_in_dylib"));
+ priv_in_dylib.set_output_type(Target::RUST_LIBRARY);
+ priv_in_dylib.visibility().SetPublic();
+ SourceFile priv_in_dylib_root("//priv_in_dylib/lib.rs");
+ priv_in_dylib.sources().push_back(priv_in_dylib_root);
+ priv_in_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_dylib.rust_values().set_crate_root(priv_in_dylib_root);
+ priv_in_dylib.rust_values().crate_name() = "priv_in_dylib";
+ priv_in_dylib.SetToolchain(setup.toolchain());
+ priv_in_dylib.private_deps().push_back(LabelTargetPair(&priv_sset_in_dylib));
+ ASSERT_TRUE(priv_in_dylib.OnResolved(&err));
+
+ Target pub_in_dylib(setup.settings(),
+ Label(SourceDir("//pub_in_dylib/"), "pub_in_dylib"));
+ pub_in_dylib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_dylib.visibility().SetPublic();
+ SourceFile pub_in_dylib_root("//pub_in_dylib/lib.rs");
+ pub_in_dylib.sources().push_back(pub_in_dylib_root);
+ pub_in_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_dylib.rust_values().set_crate_root(pub_in_dylib_root);
+ pub_in_dylib.rust_values().crate_name() = "pub_in_dylib";
+ pub_in_dylib.SetToolchain(setup.toolchain());
+ pub_in_dylib.private_deps().push_back(LabelTargetPair(&pub_sset_in_dylib));
+ ASSERT_TRUE(pub_in_dylib.OnResolved(&err));
+
+ Target staticlib(setup.settings(),
+ Label(SourceDir("//staticlib/"), "staticlib"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//staticlib/lib.cc"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.public_deps().push_back(LabelTargetPair(&pub_in_staticlib));
+ staticlib.private_deps().push_back(LabelTargetPair(&priv_in_staticlib));
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
+ Target dylib(setup.settings(), Label(SourceDir("//dylib/"), "dylib"));
+ dylib.set_output_type(Target::SHARED_LIBRARY);
+ dylib.visibility().SetPublic();
+ SourceFile dylib_root("//dylib/lib.rs");
+ dylib.sources().push_back(dylib_root);
+ dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ dylib.rust_values().set_crate_root(dylib_root);
+ dylib.rust_values().crate_name() = "dylib";
+ dylib.public_deps().push_back(LabelTargetPair(&pub_in_dylib));
+ dylib.private_deps().push_back(LabelTargetPair(&priv_in_dylib));
+ dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dylib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "exe"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&staticlib));
+ target.private_deps().push_back(LabelTargetPair(&dylib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
- "\n"
- "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a | "
- "obj/baz/lib.rlib obj/quux/lib4.rlib obj/qux/lib2.rlib\n"
- " ldflags =\n"
- " libs =\n"
- " frameworks =\n"
- " swiftmodules =\n"
- " output_extension = \n"
- " output_dir = \n"
- " rlibs = obj/baz/lib.rlib obj/quux/lib4.rlib obj/qux/lib2.rlib\n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
- {
- Target procmacro(setup.settings(), Label(SourceDir("//baz/"), "macro"));
- procmacro.set_output_type(Target::LOADABLE_MODULE);
- procmacro.visibility().SetPublic();
- SourceFile bazlib("//baz/lib.rs");
- procmacro.sources().push_back(bazlib);
- procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
- procmacro.rust_values().set_crate_root(bazlib);
- procmacro.rust_values().crate_name() = "macro";
- procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
- procmacro.SetToolchain(setup.toolchain());
- ASSERT_TRUE(procmacro.OnResolved(&err));
-
- Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
- library_target.set_output_type(Target::STATIC_LIBRARY);
- library_target.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- library_target.sources().push_back(lib);
- library_target.source_types_used().Set(SourceFile::SOURCE_RS);
- library_target.rust_values().set_crate_root(lib);
- library_target.rust_values().crate_name() = "foo";
- library_target.public_deps().push_back(LabelTargetPair(&procmacro));
- library_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(library_target.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
- target.set_output_type(Target::EXECUTABLE);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//bar/bar.cc"));
- target.source_types_used().Set(SourceFile::SOURCE_CPP);
- target.private_deps().push_back(LabelTargetPair(&library_target));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/exe/exe.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./exe: link obj/exe/exe.main.o "
+ "obj/pub_sset_in_staticlib/pub_sset_in_staticlib.lib.o "
+ "obj/priv_sset_in_staticlib/priv_sset_in_staticlib.lib.o "
+ "obj/staticlib/libstaticlib.a "
+ "obj/dylib/libdylib.so | "
+ "obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib || "
+ "obj/pub_sset_in_staticlib/pub_sset_in_staticlib.stamp "
+ "obj/priv_sset_in_staticlib/priv_sset_in_staticlib.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib\n";
- std::ostringstream out;
- NinjaCBinaryTargetWriter writer(&target, out);
- writer.Run();
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
- "\n"
- "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
- " ldflags =\n"
- " libs =\n"
- " frameworks =\n"
- " swiftmodules =\n"
- " output_extension = \n"
- " output_dir = \n";
+// Test linking of Rust dependencies into C targets. Proc-macro dependencies are
+// not inherited by the targets that depend on them, even from public_deps,
+// since they are not built into those targets, but instead used to build them.
+TEST_F(NinjaCBinaryTargetWriterTest, RlibsWithProcMacros) {
+ Err err;
+ TestWithScope setup;
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
- }
+ Target pub_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//pub_in_staticlib/"), "pub_in_staticlib"));
+ pub_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_staticlib.visibility().SetPublic();
+ SourceFile pub_in_staticlib_root("//pub_in_staticlib/lib.rs");
+ pub_in_staticlib.sources().push_back(pub_in_staticlib_root);
+ pub_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_staticlib.rust_values().set_crate_root(pub_in_staticlib_root);
+ pub_in_staticlib.rust_values().crate_name() = "pub_in_staticlib";
+ pub_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_in_staticlib.OnResolved(&err));
+
+ Target priv_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//priv_in_staticlib/"), "priv_in_staticlib"));
+ priv_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ priv_in_staticlib.visibility().SetPublic();
+ SourceFile priv_in_staticlib_root("//priv_in_staticlib/lib.rs");
+ priv_in_staticlib.sources().push_back(priv_in_staticlib_root);
+ priv_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_staticlib.rust_values().set_crate_root(priv_in_staticlib_root);
+ priv_in_staticlib.rust_values().crate_name() = "priv_in_staticlib";
+ priv_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_in_staticlib.OnResolved(&err));
+
+ Target staticlib(setup.settings(),
+ Label(SourceDir("//staticlib/"), "staticlib"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//staticlib/lib.cc"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.public_deps().push_back(LabelTargetPair(&pub_in_staticlib));
+ staticlib.private_deps().push_back(LabelTargetPair(&priv_in_staticlib));
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
+ Target priv_in_procmacro(
+ setup.settings(),
+ Label(SourceDir("//priv_in_procmacro/"), "priv_in_procmacro"));
+ priv_in_procmacro.set_output_type(Target::RUST_LIBRARY);
+ priv_in_procmacro.visibility().SetPublic();
+ SourceFile priv_in_procmacro_root("//priv_in_procmacro/lib.rs");
+ priv_in_procmacro.sources().push_back(priv_in_procmacro_root);
+ priv_in_procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_procmacro.rust_values().set_crate_root(priv_in_procmacro_root);
+ priv_in_procmacro.rust_values().crate_name() = "priv_in_procmacro";
+ priv_in_procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_in_procmacro.OnResolved(&err));
+
+ // Public deps in a proc-macro are not inherited, since the proc-macro is not
+ // compiled into targets that depend on it.
+ Target pub_in_procmacro(
+ setup.settings(),
+ Label(SourceDir("//pub_in_procmacro/"), "pub_in_procmacro"));
+ pub_in_procmacro.set_output_type(Target::RUST_LIBRARY);
+ pub_in_procmacro.visibility().SetPublic();
+ SourceFile pub_in_procmacro_root("//pub_in_procmacro/lib.rs");
+ pub_in_procmacro.sources().push_back(pub_in_procmacro_root);
+ pub_in_procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_procmacro.rust_values().set_crate_root(pub_in_procmacro_root);
+ pub_in_procmacro.rust_values().crate_name() = "pub_in_procmacro";
+ pub_in_procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_in_procmacro.OnResolved(&err));
+
+ Target pub_in_procmacro_and_rlib(
+ setup.settings(), Label(SourceDir("//pub_in_procmacro_and_rlib/"),
+ "pub_in_procmacro_and_rlib"));
+ pub_in_procmacro_and_rlib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_procmacro_and_rlib.visibility().SetPublic();
+ SourceFile pub_in_procmacro_and_rlib_root(
+ "//pub_in_procmacro_and_rlib/lib.rs");
+ pub_in_procmacro_and_rlib.sources().push_back(pub_in_procmacro_and_rlib_root);
+ pub_in_procmacro_and_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_procmacro_and_rlib.rust_values().set_crate_root(
+ pub_in_procmacro_and_rlib_root);
+ pub_in_procmacro_and_rlib.rust_values().crate_name() = "lib2";
+ pub_in_procmacro_and_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_in_procmacro_and_rlib.OnResolved(&err));
+
+ Target procmacro(setup.settings(),
+ Label(SourceDir("//procmacro/"), "procmacro"));
+ procmacro.set_output_type(Target::RUST_PROC_MACRO);
+ procmacro.visibility().SetPublic();
+ SourceFile procmacrolib("//procmacro/lib.rs");
+ procmacro.sources().push_back(procmacrolib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.public_deps().push_back(LabelTargetPair(&pub_in_procmacro));
+ procmacro.public_deps().push_back(LabelTargetPair(&priv_in_procmacro));
+ procmacro.public_deps().push_back(
+ LabelTargetPair(&pub_in_procmacro_and_rlib));
+ procmacro.rust_values().set_crate_root(procmacrolib);
+ procmacro.rust_values().crate_name() = "procmacro";
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//rlib/"), "rlib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile rlib_root("//rlib/lib.rs");
+ rlib.sources().push_back(rlib_root);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.public_deps().push_back(LabelTargetPair(&pub_in_procmacro_and_rlib));
+ // Transitive proc macros should not impact C++ targets; we're
+ // adding one to ensure the ninja instructions below are unaffected.
+ rlib.public_deps().push_back(LabelTargetPair(&procmacro));
+ rlib.rust_values().set_crate_root(rlib_root);
+ rlib.rust_values().crate_name() = "rlib";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "exe"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&staticlib));
+ target.private_deps().push_back(LabelTargetPair(&rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/exe/exe.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./exe: link obj/exe/exe.main.o "
+ "obj/staticlib/libstaticlib.a | "
+ "obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib "
+ "obj/rlib/librlib.rlib "
+ "obj/pub_in_procmacro_and_rlib/libpub_in_procmacro_and_rlib.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib "
+ "obj/rlib/librlib.rlib "
+ "obj/pub_in_procmacro_and_rlib/libpub_in_procmacro_and_rlib.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+// Test linking of Rust dependencies into C targets.
+TEST_F(NinjaCBinaryTargetWriterTest, ProcMacroInRustStaticLib) {
+ Err err;
+ TestWithScope setup;
+
+ Target procmacro(setup.settings(), Label(SourceDir("//baz/"), "macro"));
+ procmacro.set_output_type(Target::LOADABLE_MODULE);
+ procmacro.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ procmacro.sources().push_back(bazlib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.rust_values().set_crate_root(bazlib);
+ procmacro.rust_values().crate_name() = "macro";
+ procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ Target library_target(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ library_target.set_output_type(Target::STATIC_LIBRARY);
+ library_target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ library_target.sources().push_back(lib);
+ library_target.source_types_used().Set(SourceFile::SOURCE_RS);
+ library_target.rust_values().set_crate_root(lib);
+ library_target.rust_values().crate_name() = "foo";
+ library_target.public_deps().push_back(LabelTargetPair(&procmacro));
+ library_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(library_target.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//bar/bar.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&library_target));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/bar/bar.bar.o: cxx ../../bar/bar.cc\n"
+ " source_file_part = bar.cc\n"
+ " source_name_part = bar\n"
+ "\n"
+ "build ./bar: link obj/bar/bar.bar.o obj/foo/libfoo.a\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, RustDepsOverDynamicLinking) {
+ Err err;
+ TestWithScope setup;
+
+ Target rlib3(setup.settings(), Label(SourceDir("//baz/"), "baz"));
+ rlib3.set_output_type(Target::RUST_LIBRARY);
+ rlib3.visibility().SetPublic();
+ SourceFile lib3("//baz/lib.rs");
+ rlib3.sources().push_back(lib3);
+ rlib3.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib3.rust_values().set_crate_root(lib3);
+ rlib3.rust_values().crate_name() = "baz";
+ rlib3.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib3.OnResolved(&err));
+
+ Target rlib2(setup.settings(), Label(SourceDir("//bar/"), "bar"));
+ rlib2.set_output_type(Target::RUST_LIBRARY);
+ rlib2.visibility().SetPublic();
+ SourceFile lib2("//bar/lib.rs");
+ rlib2.sources().push_back(lib2);
+ rlib2.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib2.rust_values().set_crate_root(lib2);
+ rlib2.rust_values().crate_name() = "bar";
+ rlib2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib2.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//foo/"), "foo"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ rlib.sources().push_back(lib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(lib);
+ rlib.rust_values().crate_name() = "foo";
+ rlib.public_deps().push_back(LabelTargetPair(&rlib2));
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target cdylib(setup.settings(), Label(SourceDir("//sh/"), "mylib"));
+ cdylib.set_output_type(Target::SHARED_LIBRARY);
+ cdylib.visibility().SetPublic();
+ SourceFile barlib("//sh/lib.rs");
+ cdylib.sources().push_back(barlib);
+ cdylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ cdylib.rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+ cdylib.rust_values().set_crate_root(barlib);
+ cdylib.rust_values().crate_name() = "mylib";
+ cdylib.private_deps().push_back(LabelTargetPair(&rlib));
+ cdylib.public_deps().push_back(LabelTargetPair(&rlib3));
+ cdylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(cdylib.OnResolved(&err));
+
+ Target nearrlib(setup.settings(), Label(SourceDir("//near/"), "near"));
+ nearrlib.set_output_type(Target::RUST_LIBRARY);
+ nearrlib.visibility().SetPublic();
+ SourceFile nearlib("//near/lib.rs");
+ nearrlib.sources().push_back(nearlib);
+ nearrlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ nearrlib.rust_values().set_crate_root(nearlib);
+ nearrlib.rust_values().crate_name() = "near";
+ nearrlib.public_deps().push_back(LabelTargetPair(&cdylib));
+ nearrlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(nearrlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "binary"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&nearrlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = binary\n"
+ "\n"
+ "build obj/exe/binary.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./binary: link obj/exe/binary.main.o obj/sh/libmylib.so | "
+ "obj/near/libnear.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/near/libnear.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, LinkingWithRustLibraryDepsOnCdylib) {
+ Err err;
+ TestWithScope setup;
+
+ // A non-rust shared library.
+ Target cc_shlib(setup.settings(), Label(SourceDir("//cc_shlib"), "cc_shlib"));
+ cc_shlib.set_output_type(Target::SHARED_LIBRARY);
+ cc_shlib.set_output_name("cc_shlib");
+ cc_shlib.SetToolchain(setup.toolchain());
+ cc_shlib.visibility().SetPublic();
+ ASSERT_TRUE(cc_shlib.OnResolved(&err));
+
+ // A Rust CDYLIB shared library that will be in deps.
+ Target rust_shlib(setup.settings(),
+ Label(SourceDir("//rust_shlib/"), "rust_shlib"));
+ rust_shlib.set_output_type(Target::SHARED_LIBRARY);
+ rust_shlib.visibility().SetPublic();
+ SourceFile rust_shlib_rs("//rust_shlib/lib.rs");
+ rust_shlib.sources().push_back(rust_shlib_rs);
+ rust_shlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rust_shlib.rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+ rust_shlib.rust_values().set_crate_root(rust_shlib_rs);
+ rust_shlib.rust_values().crate_name() = "rust_shlib";
+ rust_shlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rust_shlib.OnResolved(&err));
+
+ // A Rust DYLIB shared library that will be in public_deps.
+ Target pub_rust_shlib(setup.settings(), Label(SourceDir("//pub_rust_shlib/"),
+ "pub_rust_shlib"));
+ pub_rust_shlib.set_output_type(Target::SHARED_LIBRARY);
+ pub_rust_shlib.visibility().SetPublic();
+ SourceFile pub_rust_shlib_rs("//pub_rust_shlib/lib.rs");
+ pub_rust_shlib.sources().push_back(pub_rust_shlib_rs);
+ pub_rust_shlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_rust_shlib.rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+ pub_rust_shlib.rust_values().set_crate_root(pub_rust_shlib_rs);
+ pub_rust_shlib.rust_values().crate_name() = "pub_rust_shlib";
+ pub_rust_shlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_rust_shlib.OnResolved(&err));
+
+ // An rlib that depends on both shared libraries.
+ Target rlib(setup.settings(), Label(SourceDir("//rlib/"), "rlib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile rlib_rs("//rlib/lib.rs");
+ rlib.sources().push_back(rlib_rs);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(rlib_rs);
+ rlib.rust_values().crate_name() = "rlib";
+ rlib.private_deps().push_back(LabelTargetPair(&rust_shlib));
+ rlib.private_deps().push_back(LabelTargetPair(&cc_shlib));
+ rlib.public_deps().push_back(LabelTargetPair(&pub_rust_shlib));
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "binary"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = binary\n"
+ "\n"
+ "build obj/exe/binary.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./binary: link obj/exe/binary.main.o "
+ "obj/pub_rust_shlib/libpub_rust_shlib.so obj/rust_shlib/librust_shlib.so "
+ "./libcc_shlib.so | "
+ "obj/rlib/librlib.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/rlib/librlib.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, LinkingWithRustLibraryDepsOnDylib) {
+ Err err;
+ TestWithScope setup;
+
+ // A non-rust shared library.
+ Target cc_shlib(setup.settings(), Label(SourceDir("//cc_shlib"), "cc_shlib"));
+ cc_shlib.set_output_type(Target::SHARED_LIBRARY);
+ cc_shlib.set_output_name("cc_shlib");
+ cc_shlib.SetToolchain(setup.toolchain());
+ cc_shlib.visibility().SetPublic();
+ ASSERT_TRUE(cc_shlib.OnResolved(&err));
+
+ // A Rust DYLIB shared library that will be in deps.
+ Target rust_shlib(setup.settings(),
+ Label(SourceDir("//rust_shlib/"), "rust_shlib"));
+ rust_shlib.set_output_type(Target::SHARED_LIBRARY);
+ rust_shlib.visibility().SetPublic();
+ SourceFile rust_shlib_rs("//rust_shlib/lib.rs");
+ rust_shlib.sources().push_back(rust_shlib_rs);
+ rust_shlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rust_shlib.rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+ rust_shlib.rust_values().set_crate_root(rust_shlib_rs);
+ rust_shlib.rust_values().crate_name() = "rust_shlib";
+ rust_shlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rust_shlib.OnResolved(&err));
+
+ // A Rust DYLIB shared library that will be in public_deps.
+ Target pub_rust_shlib(setup.settings(), Label(SourceDir("//pub_rust_shlib/"),
+ "pub_rust_shlib"));
+ pub_rust_shlib.set_output_type(Target::SHARED_LIBRARY);
+ pub_rust_shlib.visibility().SetPublic();
+ SourceFile pub_rust_shlib_rs("//pub_rust_shlib/lib.rs");
+ pub_rust_shlib.sources().push_back(pub_rust_shlib_rs);
+ pub_rust_shlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_rust_shlib.rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+ pub_rust_shlib.rust_values().set_crate_root(pub_rust_shlib_rs);
+ pub_rust_shlib.rust_values().crate_name() = "pub_rust_shlib";
+ pub_rust_shlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_rust_shlib.OnResolved(&err));
+
+ // An rlib that depends on both shared libraries.
+ Target rlib(setup.settings(), Label(SourceDir("//rlib/"), "rlib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile rlib_rs("//rlib/lib.rs");
+ rlib.sources().push_back(rlib_rs);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(rlib_rs);
+ rlib.rust_values().crate_name() = "rlib";
+ rlib.private_deps().push_back(LabelTargetPair(&rust_shlib));
+ rlib.private_deps().push_back(LabelTargetPair(&cc_shlib));
+ rlib.public_deps().push_back(LabelTargetPair(&pub_rust_shlib));
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "binary"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = binary\n"
+ "\n"
+ "build obj/exe/binary.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./binary: link obj/exe/binary.main.o "
+ "obj/pub_rust_shlib/libpub_rust_shlib.so obj/rust_shlib/librust_shlib.so "
+ "./libcc_shlib.so | "
+ "obj/rlib/librlib.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/rlib/librlib.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
+// Verify dependencies of a shared library and a rust library are inherited
+// independently.
+TEST_F(NinjaCBinaryTargetWriterTest, RustLibAfterSharedLib) {
+ Err err;
+ TestWithScope setup;
+
+ Target static1(setup.settings(),
+ Label(SourceDir("//static1/"), "staticlib1"));
+ static1.set_output_type(Target::STATIC_LIBRARY);
+ static1.visibility().SetPublic();
+ static1.sources().push_back(SourceFile("//static1/c.cc"));
+ static1.source_types_used().Set(SourceFile::SOURCE_CPP);
+ static1.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(static1.OnResolved(&err));
+
+ Target static2(setup.settings(),
+ Label(SourceDir("//static2/"), "staticlib2"));
+ static2.set_output_type(Target::STATIC_LIBRARY);
+ static2.visibility().SetPublic();
+ static2.sources().push_back(SourceFile("//static2/c.cc"));
+ static2.source_types_used().Set(SourceFile::SOURCE_CPP);
+ static2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(static2.OnResolved(&err));
+
+ Target static3(setup.settings(),
+ Label(SourceDir("//static3/"), "staticlib3"));
+ static3.set_output_type(Target::STATIC_LIBRARY);
+ static3.visibility().SetPublic();
+ static3.sources().push_back(SourceFile("//static3/c.cc"));
+ static3.source_types_used().Set(SourceFile::SOURCE_CPP);
+ static3.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(static3.OnResolved(&err));
+
+ Target shared1(setup.settings(),
+ Label(SourceDir("//shared1"), "mysharedlib1"));
+ shared1.set_output_type(Target::SHARED_LIBRARY);
+ shared1.set_output_name("mysharedlib1");
+ shared1.set_output_prefix_override("");
+ shared1.SetToolchain(setup.toolchain());
+ shared1.visibility().SetPublic();
+ shared1.private_deps().push_back(LabelTargetPair(&static1));
+ ASSERT_TRUE(shared1.OnResolved(&err));
+
+ Target rlib2(setup.settings(), Label(SourceDir("//rlib2/"), "myrlib2"));
+ rlib2.set_output_type(Target::RUST_LIBRARY);
+ rlib2.visibility().SetPublic();
+ SourceFile lib2("//rlib2/lib.rs");
+ rlib2.sources().push_back(lib2);
+ rlib2.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib2.rust_values().set_crate_root(lib2);
+ rlib2.rust_values().crate_name() = "foo";
+ rlib2.private_deps().push_back(LabelTargetPair(&static2));
+ rlib2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib2.OnResolved(&err));
+
+ Target shared3(setup.settings(),
+ Label(SourceDir("//shared3"), "mysharedlib3"));
+ shared3.set_output_type(Target::SHARED_LIBRARY);
+ shared3.set_output_name("mysharedlib3");
+ shared3.set_output_prefix_override("");
+ shared3.SetToolchain(setup.toolchain());
+ shared3.visibility().SetPublic();
+ shared3.private_deps().push_back(LabelTargetPair(&static3));
+ ASSERT_TRUE(shared3.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "binary"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//exe/main.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.private_deps().push_back(LabelTargetPair(&shared1));
+ target.private_deps().push_back(LabelTargetPair(&rlib2));
+ target.private_deps().push_back(LabelTargetPair(&shared3));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = binary\n"
+ "\n"
+ "build obj/exe/binary.main.o: cxx ../../exe/main.cc\n"
+ " source_file_part = main.cc\n"
+ " source_name_part = main\n"
+ "\n"
+ "build ./binary: link obj/exe/binary.main.o "
+ "./mysharedlib1.so ./mysharedlib3.so "
+ "obj/static2/libstaticlib2.a | obj/rlib2/libmyrlib2.rlib\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " rlibs = obj/rlib2/libmyrlib2.rlib\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
TEST_F(NinjaCBinaryTargetWriterTest, ModuleMapInStaticLibrary) {
@@ -1531,7 +2251,11 @@ TEST_F(NinjaCBinaryTargetWriterTest, ModuleMapInStaticLibrary) {
"target_output_name = libbar\n"
"\n"
"build obj/foo/libbar.bar.o: cxx ../../foo/bar.cc | obj/foo/libbar.bar.pcm\n"
+ " source_file_part = bar.cc\n"
+ " source_name_part = bar\n"
"build obj/foo/libbar.bar.pcm: cxx_module ../../foo/bar.modulemap\n"
+ " source_file_part = bar.modulemap\n"
+ " source_name_part = bar\n"
"\n"
"build obj/foo/libbar.a: alink obj/foo/libbar.bar.o\n"
" arflags =\n"
@@ -1655,10 +2379,10 @@ TEST_F(NinjaCBinaryTargetWriterTest, SwiftModule) {
"target_output_name = bar\n"
"\n"
"build obj/bar/Bar.swiftmodule: swift ../../bar/bar.swift"
- " || obj/foo/foo.stamp\n"
+ " || obj/bar/group.stamp obj/foo/foo.stamp\n"
"\n"
"build obj/bar/bar.o: stamp obj/bar/Bar.swiftmodule"
- " || obj/foo/foo.stamp\n"
+ " || obj/bar/group.stamp obj/foo/foo.stamp\n"
"\n"
"build obj/bar/bar.stamp: stamp obj/bar/bar.o "
"|| obj/bar/group.stamp obj/foo/foo.stamp\n";
@@ -1780,17 +2504,21 @@ TEST_F(NinjaCBinaryTargetWriterTest, DependOnModule) {
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
label = //blah$:a
root_out_dir = withmodules
target_out_dir = obj/blah
target_output_name = liba
build obj/blah/liba.a.pcm: cxx_module ../../blah/a.modulemap
+ source_file_part = a.modulemap
+ source_name_part = a
build obj/blah/liba.a.o: cxx ../../blah/a.cc | obj/blah/liba.a.pcm
+ source_file_part = a.cc
+ source_name_part = a
build obj/blah/liba.a: alink obj/blah/liba.a.o
arflags =
@@ -1821,17 +2549,21 @@ build obj/blah/liba.a: alink obj/blah/liba.a.o
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files
label = //stuff$:b
root_out_dir = withmodules
target_out_dir = obj/stuff
target_output_name = libb
build obj/stuff/libb.b.pcm: cxx_module ../../stuff/b.modulemap
+ source_file_part = b.modulemap
+ source_name_part = b
build obj/stuff/libb.b.o: cxx ../../stuff/b.cc | obj/stuff/libb.b.pcm
+ source_file_part = b.cc
+ source_name_part = b
build obj/stuff/libb.a: alink obj/stuff/libb.b.o
arflags =
@@ -1861,16 +2593,18 @@ build obj/stuff/libb.a: alink obj/stuff/libb.b.o
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/stuff/libc.c.pcm -fmodule-file=obj/blah/liba.a.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm
label = //things$:c
root_out_dir = withmodules
target_out_dir = obj/things
target_output_name = libc
build obj/stuff/libc.c.pcm: cxx_module ../../stuff/c.modulemap | obj/blah/liba.a.pcm
+ source_file_part = c.modulemap
+ source_name_part = c
build obj/things/libc.a: alink || obj/blah/liba.a
arflags =
@@ -1900,17 +2634,21 @@ build obj/things/libc.a: alink || obj/blah/liba.a
const char expected[] = R"(defines =
include_dirs =
-module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
-module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
cflags =
cflags_cc =
+module_deps = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
+module_deps_no_self = -Xclang -fmodules-embed-all-files -fmodule-file=obj/blah/liba.a.pcm -fmodule-file=obj/stuff/libb.b.pcm
label = //zap$:c
root_out_dir = withmodules
target_out_dir = obj/zap
target_output_name = c
build obj/zap/c.x.o: cxx ../../zap/x.cc | obj/blah/liba.a.pcm obj/stuff/libb.b.pcm
+ source_file_part = x.cc
+ source_name_part = x
build obj/zap/c.y.o: cxx ../../zap/y.cc | obj/blah/liba.a.pcm obj/stuff/libb.b.pcm
+ source_file_part = y.cc
+ source_name_part = y
build withmodules/c: link obj/zap/c.x.o obj/zap/c.y.o obj/blah/liba.a obj/stuff/libb.a
ldflags =
@@ -1963,6 +2701,8 @@ target_out_dir = obj/launchpad
target_output_name = main
build obj/launchpad/main.main.o: cxx ../../launchpad/main.cc
+ source_file_part = main.cc
+ source_name_part = main
build ./main: link obj/launchpad/main.main.o | ./Space$ Cadet.so.TOC
ldflags =
@@ -1981,3 +2721,48 @@ build ./main: link obj/launchpad/main.main.o | ./Space$ Cadet.so.TOC
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
+
+TEST_F(NinjaCBinaryTargetWriterTest, Pool) {
+ Err err;
+ TestWithScope setup;
+
+ Pool pool(setup.settings(),
+ Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ pool.set_depth(42);
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.sources().push_back(SourceFile("//foo/source.cc"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.set_pool(LabelPtrPair<Pool>(&pool));
+ target.visibility().SetPublic();
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.source.o: cxx ../../foo/source.cc\n"
+ " source_file_part = source.cc\n"
+ " source_name_part = source\n"
+ " pool = foo_pool\n"
+ "\n"
+ "build ./bar: link obj/foo/bar.source.o\n"
+ " ldflags =\n"
+ " libs =\n"
+ " frameworks =\n"
+ " swiftmodules =\n"
+ " output_extension = \n"
+ " output_dir = \n"
+ " pool = foo_pool\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
diff --git a/gn/src/gn/ninja_rust_binary_target_writer.cc b/gn/src/gn/ninja_rust_binary_target_writer.cc
index d894681608d..6b9c2dcf785 100644
--- a/gn/src/gn/ninja_rust_binary_target_writer.cc
+++ b/gn/src/gn/ninja_rust_binary_target_writer.cc
@@ -13,8 +13,8 @@
#include "gn/ninja_target_command_util.h"
#include "gn/ninja_utils.h"
#include "gn/rust_substitution_type.h"
-#include "gn/substitution_writer.h"
#include "gn/rust_values.h"
+#include "gn/substitution_writer.h"
#include "gn/target.h"
namespace {
@@ -113,8 +113,8 @@ void NinjaRustBinaryTargetWriter::Run() {
size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> input_deps = WriteInputsStampAndGetDep(
- num_stamp_uses);
+ std::vector<OutputFile> input_deps =
+ WriteInputsStampAndGetDep(num_stamp_uses);
WriteCompilerVars();
@@ -176,11 +176,23 @@ void NinjaRustBinaryTargetWriter::Run() {
}
}
- // Bubble up the full list of transitive rlib dependencies.
- std::vector<OutputFile> transitive_rustlibs;
- for (const auto* dep : target_->rust_transitive_libs().GetOrdered()) {
- if (dep->source_types_used().RustSourceUsed()) {
- transitive_rustlibs.push_back(dep->dependency_output_file());
+ // Collect the full transitive set of rust libraries that this target depends
+ // on, and the public flag represents if the target has direct access to the
+ // dependency through a chain of public_deps.
+ std::vector<ExternCrate> transitive_crates;
+ for (const auto& [dep, has_direct_access] :
+ target_->rust_transitive_inherited_libs().GetOrderedAndPublicFlag()) {
+ // We will tell rustc to look for crate metadata for any rust crate
+ // dependencies except cdylibs, as they have no metadata present.
+ if (dep->source_types_used().RustSourceUsed() &&
+ RustValues::IsRustLibrary(dep)) {
+ transitive_crates.push_back({dep, has_direct_access});
+ // If the current crate can directly acccess the `dep` crate, then the
+ // current crate needs an implicit dependency on `dep` so it will be
+ // rebuilt if `dep` changes.
+ if (has_direct_access) {
+ implicit_deps.push_back(dep->dependency_output_file());
+ }
}
}
@@ -196,9 +208,9 @@ void NinjaRustBinaryTargetWriter::Run() {
std::copy(classified_deps.non_linkable_deps.begin(),
classified_deps.non_linkable_deps.end(),
std::back_inserter(extern_deps));
- WriteExterns(extern_deps);
- WriteRustdeps(transitive_rustlibs, rustdeps, nonrustdeps);
+ WriteExternsAndDeps(extern_deps, transitive_crates, rustdeps, nonrustdeps);
WriteSourcesAndInputs();
+ WritePool(out_);
}
void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
@@ -207,13 +219,7 @@ void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
EscapeOptions opts = GetFlagOptions();
WriteCrateVars(target_, tool_, opts, out_);
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &kRustSubstitutionRustFlags, false, Tool::kToolNone,
- &ConfigValues::rustflags, opts, path_output_, out_);
-
- WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
- &kRustSubstitutionRustEnv, false, Tool::kToolNone,
- &ConfigValues::rustenv, opts, path_output_, out_);
+ WriteRustCompilerVars(subst, /*indent=*/false, /*always_write=*/true);
WriteSharedVars(subst);
}
@@ -237,7 +243,8 @@ void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
out_ << " sources =";
for (const auto& source : target_->sources()) {
out_ << " ";
- path_output_.WriteFile(out_, OutputFile(settings_->build_settings(), source));
+ path_output_.WriteFile(out_,
+ OutputFile(settings_->build_settings(), source));
}
for (const auto& data : target_->config_values().inputs()) {
out_ << " ";
@@ -246,67 +253,99 @@ void NinjaRustBinaryTargetWriter::WriteSourcesAndInputs() {
out_ << std::endl;
}
-void NinjaRustBinaryTargetWriter::WriteExterns(
- const std::vector<const Target*>& deps) {
+void NinjaRustBinaryTargetWriter::WriteExternsAndDeps(
+ const std::vector<const Target*>& deps,
+ const std::vector<ExternCrate>& transitive_rust_deps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps) {
+ // Writes a external LibFile which comes from user-specified externs, and may
+ // be either a string or a SourceFile.
+ auto write_extern_lib_file = [this](std::string_view crate_name,
+ LibFile lib_file) {
+ out_ << " --extern ";
+ out_ << crate_name;
+ out_ << "=";
+ if (lib_file.is_source_file()) {
+ path_output_.WriteFile(out_, lib_file.source_file());
+ } else {
+ EscapeOptions escape_opts_command;
+ escape_opts_command.mode = ESCAPE_NINJA_COMMAND;
+ EscapeStringToStream(out_, lib_file.value(), escape_opts_command);
+ }
+ };
+ // Writes an external OutputFile which comes from a dependency of the current
+ // target.
+ auto write_extern_target = [this](const Target& dep) {
+ std::string_view crate_name;
+ const auto& aliased_deps = target_->rust_values().aliased_deps();
+ if (auto it = aliased_deps.find(dep.label()); it != aliased_deps.end()) {
+ crate_name = it->second;
+ } else {
+ crate_name = dep.rust_values().crate_name();
+ }
+
+ out_ << " --extern ";
+ out_ << crate_name;
+ out_ << "=";
+ path_output_.WriteFile(out_, dep.dependency_output_file());
+ };
+
+ // Write accessible crates with `--extern` to add them to the extern prelude.
out_ << " externs =";
- for (const Target* target : deps) {
- if (target->output_type() == Target::RUST_LIBRARY ||
- target->output_type() == Target::RUST_PROC_MACRO ||
- (target->source_types_used().RustSourceUsed() &&
- target->rust_values().InferredCrateType(target) ==
- RustValues::CRATE_DYLIB)) {
- out_ << " --extern ";
- const auto& renamed_dep =
- target_->rust_values().aliased_deps().find(target->label());
- if (renamed_dep != target_->rust_values().aliased_deps().end()) {
- out_ << renamed_dep->second << "=";
- } else {
- out_ << std::string(target->rust_values().crate_name()) << "=";
+ // Tracking to avoid emitted the same lib twice. We track it instead of
+ // pre-emptively constructing a UniqueVector since we would have to also store
+ // the crate name, and in the future the public-ness.
+ std::unordered_set<OutputFile> emitted_rust_libs;
+ // TODO: We defer private dependencies to -Ldependency until --extern priv is
+ // stabilized.
+ UniqueVector<SourceDir> private_extern_dirs;
+
+ // Walk the transitive closure of all rust dependencies.
+ //
+ // For dependencies that are meant to be accessible we pass them to --extern
+ // in order to add them to the crate's extern prelude.
+ //
+ // For all transitive dependencies, we add them to `private_extern_dirs` in
+ // order to generate a -Ldependency switch that points to them. This ensures
+ // that rustc can find them if they are used by other dependencies. For
+ // example:
+ //
+ // A -> C --public--> D
+ // -> B --private-> D
+ //
+ // Here A has direct access to D, but B and C also make use of D, and they
+ // will only search the paths specified to -Ldependency, thus D needs to
+ // appear as both a --extern (for A) and -Ldependency (for B and C).
+ for (const auto& crate : transitive_rust_deps) {
+ const OutputFile& rust_lib = crate.target->dependency_output_file();
+ if (emitted_rust_libs.count(rust_lib) == 0) {
+ if (crate.has_direct_access) {
+ write_extern_target(*crate.target);
}
- path_output_.WriteFile(out_, target->dependency_output_file());
+ emitted_rust_libs.insert(rust_lib);
}
+ private_extern_dirs.push_back(
+ rust_lib.AsSourceFile(settings_->build_settings()).GetDir());
}
- EscapeOptions extern_escape_opts;
- extern_escape_opts.mode = ESCAPE_NINJA_COMMAND;
-
+ // Add explicitly specified externs from the GN target.
for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
const ConfigValues& cur = iter.cur();
- for (const auto& e : cur.externs()) {
- out_ << " --extern " << std::string(e.first) << "=";
- if (e.second.is_source_file()) {
- path_output_.WriteFile(out_, e.second.source_file());
- } else {
- EscapeStringToStream(out_, e.second.value(), extern_escape_opts);
- }
+ for (const auto& [crate_name, lib_file] : cur.externs()) {
+ write_extern_lib_file(crate_name, lib_file);
}
}
out_ << std::endl;
-}
-
-void NinjaRustBinaryTargetWriter::WriteRustdeps(
- const std::vector<OutputFile>& transitive_rustdeps,
- const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps) {
out_ << " rustdeps =";
- // Rust dependencies.
- UniqueVector<SourceDir> transitive_rustdep_dirs;
- for (const auto& rustdep : transitive_rustdeps) {
- // TODO switch to using --extern priv: after stabilization
- transitive_rustdep_dirs.push_back(
- rustdep.AsSourceFile(settings_->build_settings()).GetDir());
- }
- for (const auto& rustdepdir : transitive_rustdep_dirs) {
+ for (const SourceDir& dir : private_extern_dirs) {
+ // TODO: switch to using `--extern priv:name` after stabilization.
out_ << " -Ldependency=";
- path_output_.WriteDir(out_, rustdepdir, PathOutput::DIR_NO_LAST_SLASH);
+ path_output_.WriteDir(out_, dir, PathOutput::DIR_NO_LAST_SLASH);
}
- EscapeOptions lib_escape_opts;
- lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
-
// Non-Rust native dependencies.
UniqueVector<SourceDir> nonrustdep_dirs;
for (const auto& nonrustdep : nonrustdeps) {
diff --git a/gn/src/gn/ninja_rust_binary_target_writer.h b/gn/src/gn/ninja_rust_binary_target_writer.h
index 0319c86292a..69ec916454b 100644
--- a/gn/src/gn/ninja_rust_binary_target_writer.h
+++ b/gn/src/gn/ninja_rust_binary_target_writer.h
@@ -6,9 +6,9 @@
#define TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
#include "gn/ninja_binary_target_writer.h"
+#include "gn/output_file.h"
#include "gn/rust_tool.h"
-
-struct EscapeOptions;
+#include "gn/target.h"
// Writes a .ninja file for a binary target type (an executable, a shared
// library, or a static library).
@@ -20,13 +20,18 @@ class NinjaRustBinaryTargetWriter : public NinjaBinaryTargetWriter {
void Run() override;
private:
+ struct ExternCrate {
+ const Target* target;
+ bool has_direct_access;
+ };
+
void WriteCompilerVars();
void WriteSources(const OutputFile& input_dep,
const std::vector<OutputFile>& order_only_deps);
- void WriteExterns(const std::vector<const Target*>& deps);
- void WriteRustdeps(const std::vector<OutputFile>& transitive_rustdeps,
- const std::vector<OutputFile>& rustdeps,
- const std::vector<OutputFile>& nonrustdeps);
+ void WriteExternsAndDeps(const std::vector<const Target*>& deps,
+ const std::vector<ExternCrate>& transitive_rust_deps,
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps);
// Unlike C/C++, Rust compiles all sources of a crate in one command.
// Write a ninja variable `sources` that contains all sources and input files.
void WriteSourcesAndInputs();
diff --git a/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc b/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc
index b4b71d64ee2..41a9fdd5f74 100644
--- a/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/gn/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -5,6 +5,7 @@
#include "gn/ninja_rust_binary_target_writer.h"
#include "gn/config.h"
+#include "gn/pool.h"
#include "gn/rust_values.h"
#include "gn/scheduler.h"
#include "gn/target.h"
@@ -64,6 +65,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustExecutable) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
"../../foo/main.rs\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
" rustdeps =\n"
" ldflags = -fsanitize=address\n"
@@ -73,71 +76,164 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustExecutable) {
}
}
+// Accessible dependencies appear as --extern switches for rustc, so that the
+// target crate can make use of them whether transitive or not. Transitive
+// dependencies can be accessible if they are in the public_deps of a direct
+// dependency, or part of a chain of public_deps from a direct dependency. Any
+// dependencies used by other crate dependencies also must appear, but are
+// pointed to by -Ldependency as they are not available for use from the target
+// crate. In the future they may move to `--extern priv:` when explicit private
+// dependencies are stabilized.
TEST_F(NinjaRustBinaryTargetWriterTest, RlibDeps) {
Err err;
TestWithScope setup;
- Target rlib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
- rlib.set_output_type(Target::RUST_LIBRARY);
- rlib.visibility().SetPublic();
- SourceFile barlib("//bar/lib.rs");
- rlib.sources().push_back(SourceFile("//bar/mylib.rs"));
- rlib.sources().push_back(barlib);
- rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- rlib.rust_values().set_crate_root(barlib);
- rlib.rust_values().crate_name() = "mylib";
- rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(rlib.OnResolved(&err));
+ Target private_rlib(setup.settings(), Label(SourceDir("//baz/"), "privatelib"));
+ private_rlib.set_output_type(Target::RUST_LIBRARY);
+ private_rlib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ private_rlib.sources().push_back(SourceFile("//baz/privatelib.rs"));
+ private_rlib.sources().push_back(bazlib);
+ private_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ private_rlib.rust_values().set_crate_root(bazlib);
+ private_rlib.rust_values().crate_name() = "privatecrate";
+ private_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(private_rlib.OnResolved(&err));
{
std::ostringstream out;
- NinjaRustBinaryTargetWriter writer(&rlib, out);
+ NinjaRustBinaryTargetWriter writer(&private_rlib, out);
writer.Run();
const char expected[] =
- "crate_name = mylib\n"
+ "crate_name = privatecrate\n"
"crate_type = rlib\n"
"output_extension = .rlib\n"
"output_dir = \n"
"rustflags =\n"
"rustenv =\n"
"root_out_dir = .\n"
- "target_out_dir = obj/bar\n"
- "target_output_name = libmylib\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libprivatelib\n"
"\n"
- "build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
+ "build obj/baz/libprivatelib.rlib: rust_rlib ../../baz/lib.rs | "
+ "../../baz/privatelib.rs ../../baz/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
- " sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
+ " sources = ../../baz/privatelib.rs ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target far_public_rlib(setup.settings(),
+ Label(SourceDir("//far/"), "farlib"));
+ far_public_rlib.set_output_type(Target::RUST_LIBRARY);
+ far_public_rlib.visibility().SetPublic();
+ SourceFile farlib("//far/lib.rs");
+ far_public_rlib.sources().push_back(SourceFile("//far/farlib.rs"));
+ far_public_rlib.sources().push_back(farlib);
+ far_public_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ far_public_rlib.rust_values().set_crate_root(farlib);
+ far_public_rlib.rust_values().crate_name() = "farcrate";
+ far_public_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(far_public_rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&far_public_rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = farcrate\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/far\n"
+ "target_output_name = libfarlib\n"
+ "\n"
+ "build obj/far/libfarlib.rlib: rust_rlib ../../far/lib.rs | "
+ "../../far/farlib.rs ../../far/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../far/farlib.rs ../../far/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target public_rlib(setup.settings(), Label(SourceDir("//bar/"), "publiclib"));
+ public_rlib.set_output_type(Target::RUST_LIBRARY);
+ public_rlib.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ public_rlib.sources().push_back(SourceFile("//bar/publiclib.rs"));
+ public_rlib.sources().push_back(barlib);
+ public_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ public_rlib.rust_values().set_crate_root(barlib);
+ public_rlib.rust_values().crate_name() = "publiccrate";
+ public_rlib.public_deps().push_back(LabelTargetPair(&far_public_rlib));
+ public_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(public_rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&public_rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = publiccrate\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = libpubliclib\n"
+ "\n"
+ "build obj/bar/libpubliclib.rlib: rust_rlib ../../bar/lib.rs | "
+ "../../bar/publiclib.rs ../../bar/lib.rs obj/far/libfarlib.rlib\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs = --extern farcrate=obj/far/libfarlib.rlib\n"
+ " rustdeps = -Ldependency=obj/far\n"
+ " ldflags =\n"
+ " sources = ../../bar/publiclib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
+ Target rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
SourceFile lib("//foo/main.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.SetToolchain(setup.toolchain());
- another_rlib.public_deps().push_back(LabelTargetPair(&rlib));
- ASSERT_TRUE(another_rlib.OnResolved(&err));
+ rlib.sources().push_back(SourceFile("//foo/direct.rs"));
+ rlib.sources().push_back(lib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(lib);
+ rlib.rust_values().crate_name() = "direct";
+ rlib.SetToolchain(setup.toolchain());
+ rlib.public_deps().push_back(LabelTargetPair(&public_rlib));
+ rlib.private_deps().push_back(LabelTargetPair(&private_rlib));
+ ASSERT_TRUE(rlib.OnResolved(&err));
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ Target target(setup.settings(), Label(SourceDir("//main/"), "main"));
target.set_output_type(Target::EXECUTABLE);
target.visibility().SetPublic();
- SourceFile main("//foo/main.rs");
- target.sources().push_back(SourceFile("//foo/source.rs"));
+ SourceFile main("//main/main.rs");
+ target.sources().push_back(SourceFile("//main/source.rs"));
target.sources().push_back(main);
target.source_types_used().Set(SourceFile::SOURCE_RS);
target.rust_values().set_crate_root(main);
- target.rust_values().crate_name() = "foo_bar";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.rust_values().crate_name() = "main_crate";
+ target.private_deps().push_back(LabelTargetPair(&rlib));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -147,22 +243,29 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDeps) {
writer.Run();
const char expected[] =
- "crate_name = foo_bar\n"
+ "crate_name = main_crate\n"
"crate_type = bin\n"
"output_extension = \n"
"output_dir = \n"
"rustflags =\n"
"rustenv =\n"
"root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
+ "target_out_dir = obj/main\n"
+ "target_output_name = main\n"
"\n"
- "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib\n"
- " externs = --extern direct=obj/foo/libdirect.rlib\n"
- " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ "build ./main_crate: rust_bin ../../main/main.rs | "
+ "../../main/source.rs ../../main/main.rs obj/foo/libdirect.rlib "
+ "obj/bar/libpubliclib.rlib obj/far/libfarlib.rlib "
+ "obj/baz/libprivatelib.rlib\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
+ " externs = --extern direct=obj/foo/libdirect.rlib "
+ "--extern publiccrate=obj/bar/libpubliclib.rlib "
+ "--extern farcrate=obj/far/libfarlib.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar "
+ "-Ldependency=obj/far -Ldependency=obj/baz\n"
" ldflags =\n"
- " sources = ../../foo/source.rs ../../foo/main.rs\n";
+ " sources = ../../main/source.rs ../../main/main.rs\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
@@ -172,6 +275,86 @@ TEST_F(NinjaRustBinaryTargetWriterTest, DylibDeps) {
Err err;
TestWithScope setup;
+ Target private_inside_dylib(setup.settings(), Label(SourceDir("//faz/"), "private_inside"));
+ private_inside_dylib.set_output_type(Target::RUST_LIBRARY);
+ private_inside_dylib.visibility().SetPublic();
+ SourceFile fazlib("//faz/lib.rs");
+ private_inside_dylib.sources().push_back(SourceFile("//faz/private_inside.rs"));
+ private_inside_dylib.sources().push_back(fazlib);
+ private_inside_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ private_inside_dylib.rust_values().set_crate_root(fazlib);
+ private_inside_dylib.rust_values().crate_name() = "private_inside";
+ private_inside_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(private_inside_dylib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&private_inside_dylib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = private_inside\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/faz\n"
+ "target_output_name = libprivate_inside\n"
+ "\n"
+ "build obj/faz/libprivate_inside.rlib: rust_rlib ../../faz/lib.rs | "
+ "../../faz/private_inside.rs ../../faz/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../faz/private_inside.rs ../../faz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target inside_dylib(setup.settings(), Label(SourceDir("//baz/"), "inside"));
+ inside_dylib.set_output_type(Target::RUST_LIBRARY);
+ inside_dylib.visibility().SetPublic();
+ SourceFile bazlib("//baz/lib.rs");
+ inside_dylib.sources().push_back(SourceFile("//baz/inside.rs"));
+ inside_dylib.sources().push_back(bazlib);
+ inside_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ inside_dylib.rust_values().set_crate_root(bazlib);
+ inside_dylib.rust_values().crate_name() = "inside";
+ inside_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(inside_dylib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&inside_dylib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = inside\n"
+ "crate_type = rlib\n"
+ "output_extension = .rlib\n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/baz\n"
+ "target_output_name = libinside\n"
+ "\n"
+ "build obj/baz/libinside.rlib: rust_rlib ../../baz/lib.rs | "
+ "../../baz/inside.rs ../../baz/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../baz/inside.rs ../../baz/lib.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
Target dylib(setup.settings(), Label(SourceDir("//bar/"), "mylib"));
dylib.set_output_type(Target::SHARED_LIBRARY);
dylib.visibility().SetPublic();
@@ -179,9 +362,11 @@ TEST_F(NinjaRustBinaryTargetWriterTest, DylibDeps) {
dylib.sources().push_back(SourceFile("//bar/mylib.rs"));
dylib.sources().push_back(barlib);
dylib.source_types_used().Set(SourceFile::SOURCE_RS);
- dylib.rust_values().set_crate_type(RustValues::CRATE_DYLIB); // TODO
+ dylib.rust_values().set_crate_type(RustValues::CRATE_DYLIB);
dylib.rust_values().set_crate_root(barlib);
dylib.rust_values().crate_name() = "mylib";
+ dylib.public_deps().push_back(LabelTargetPair(&inside_dylib));
+ dylib.private_deps().push_back(LabelTargetPair(&private_inside_dylib));
dylib.SetToolchain(setup.toolchain());
ASSERT_TRUE(dylib.OnResolved(&err));
@@ -202,15 +387,32 @@ TEST_F(NinjaRustBinaryTargetWriterTest, DylibDeps) {
"target_output_name = libmylib\n"
"\n"
"build obj/bar/libmylib.so: rust_dylib ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs\n"
- " externs =\n"
- " rustdeps =\n"
+ "../../bar/mylib.rs ../../bar/lib.rs "
+ "obj/baz/libinside.rlib obj/faz/libprivate_inside.rlib\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs = --extern inside=obj/baz/libinside.rlib "
+ "--extern private_inside=obj/faz/libprivate_inside.rlib\n"
+ " rustdeps = -Ldependency=obj/baz -Ldependency=obj/faz\n"
" ldflags =\n"
" sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
+ Target private_dylib(setup.settings(), Label(SourceDir("//private_dylib/"), "private_dylib"));
+ private_dylib.set_output_type(Target::SHARED_LIBRARY);
+ private_dylib.visibility().SetPublic();
+ SourceFile private_dyliblib("//private_dylib/lib.rs");
+ private_dylib.sources().push_back(SourceFile("//private_dylib/mylib.rs"));
+ private_dylib.sources().push_back(private_dyliblib);
+ private_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ private_dylib.rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+ private_dylib.rust_values().set_crate_root(private_dyliblib);
+ private_dylib.rust_values().crate_name() = "private_dylib";
+ private_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(private_dylib.OnResolved(&err));
+
Target another_dylib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
another_dylib.set_output_type(Target::SHARED_LIBRARY);
another_dylib.visibility().SetPublic();
@@ -222,7 +424,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, DylibDeps) {
another_dylib.rust_values().set_crate_root(lib);
another_dylib.rust_values().crate_name() = "direct";
another_dylib.SetToolchain(setup.toolchain());
- another_dylib.private_deps().push_back(LabelTargetPair(&dylib));
+ another_dylib.public_deps().push_back(LabelTargetPair(&dylib));
+ another_dylib.private_deps().push_back(LabelTargetPair(&private_dylib));
ASSERT_TRUE(another_dylib.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
@@ -255,9 +458,16 @@ TEST_F(NinjaRustBinaryTargetWriterTest, DylibDeps) {
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.so\n"
- " externs = --extern direct=obj/foo/libdirect.so\n"
- " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ "../../foo/main.rs obj/foo/libdirect.so obj/bar/libmylib.so "
+ "obj/baz/libinside.rlib\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
+ " externs = --extern direct=obj/foo/libdirect.so "
+ "--extern mylib=obj/bar/libmylib.so "
+ "--extern inside=obj/baz/libinside.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar "
+ "-Ldependency=obj/baz -Ldependency=obj/faz "
+ "-Ldependency=obj/private_dylib\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
@@ -300,6 +510,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
"\n"
"build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
"../../bar/mylib.rs ../../bar/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
@@ -308,6 +520,13 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
+ // A group produces an order-only dependency in ninja:
+ // https://ninja-build.org/manual.html#ref_dependencies.
+ //
+ // If a crate D inside the group is visible to a crate C depending on the
+ // group, the crate C needs to be rebuilt when D is changed. The group
+ // dependency does not guarantee that it would, so we test that C has an
+ // indirect dependency on D through this group.
Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
group.set_output_type(Target::GROUP);
group.visibility().SetPublic();
@@ -333,6 +552,10 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
NinjaRustBinaryTargetWriter writer(&rlib, out);
writer.Run();
+ // libmymacro.so is inside the obj/baz/group, so would be built before
+ // libmylib.rlib. However it must also cause libmylib.rlib to be recompiled
+ // when changed, so we expect an implicit dependency (appearing after `|` on
+ // the build line) from libmylib.rlib to libmymacro.so.
const char expected[] =
"crate_name = mylib\n"
"crate_type = rlib\n"
@@ -347,6 +570,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
"build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
"../../bar/mylib.rs ../../bar/lib.rs obj/bar/libmymacro.so || "
"obj/baz/group.stamp\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs = --extern mymacro=obj/bar/libmymacro.so\n"
" rustdeps = -Ldependency=obj/bar\n"
" ldflags =\n"
@@ -385,8 +610,12 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RlibDepsAcrossGroups) {
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | "
- "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib\n"
- " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ "../../foo/source.rs ../../foo/main.rs "
+ "obj/bar/libmylib.rlib obj/bar/libmymacro.so\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib "
+ "--extern mymacro=obj/bar/libmymacro.so\n"
" rustdeps = -Ldependency=obj/bar\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
@@ -399,17 +628,42 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RenamedDeps) {
Err err;
TestWithScope setup;
- Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
- another_rlib.set_output_type(Target::RUST_LIBRARY);
- another_rlib.visibility().SetPublic();
- SourceFile lib("//foo/lib.rs");
- another_rlib.sources().push_back(SourceFile("//foo/direct.rs"));
- another_rlib.sources().push_back(lib);
- another_rlib.source_types_used().Set(SourceFile::SOURCE_RS);
- another_rlib.rust_values().set_crate_root(lib);
- another_rlib.rust_values().crate_name() = "direct";
- another_rlib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(another_rlib.OnResolved(&err));
+ Target transitive(setup.settings(), Label(SourceDir("//faz/"), "transitive"));
+ transitive.set_output_type(Target::RUST_LIBRARY);
+ transitive.visibility().SetPublic();
+ SourceFile transitive_lib("//faz/transitive/lib.rs");
+ transitive.sources().push_back(SourceFile("//faz/transitive/transitive.rs"));
+ transitive.sources().push_back(transitive_lib);
+ transitive.source_types_used().Set(SourceFile::SOURCE_RS);
+ transitive.rust_values().set_crate_root(transitive_lib);
+ transitive.rust_values().crate_name() = "transitive";
+ transitive.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(transitive.OnResolved(&err));
+
+ Target rlib(setup.settings(), Label(SourceDir("//baz/"), "mylib"));
+ rlib.set_output_type(Target::RUST_LIBRARY);
+ rlib.visibility().SetPublic();
+ SourceFile barlib("//baz/bar/lib.rs");
+ rlib.sources().push_back(SourceFile("//baz/bar/mylib.rs"));
+ rlib.sources().push_back(barlib);
+ rlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib.rust_values().set_crate_root(barlib);
+ rlib.rust_values().crate_name() = "mylib";
+ rlib.SetToolchain(setup.toolchain());
+ rlib.public_deps().push_back(LabelTargetPair(&transitive));
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target direct(setup.settings(), Label(SourceDir("//bar/"), "direct"));
+ direct.set_output_type(Target::RUST_LIBRARY);
+ direct.visibility().SetPublic();
+ SourceFile direct_lib("//bar/direct/lib.rs");
+ direct.sources().push_back(SourceFile("//bar/direct/direct.rs"));
+ direct.sources().push_back(direct_lib);
+ direct.source_types_used().Set(SourceFile::SOURCE_RS);
+ direct.rust_values().set_crate_root(direct_lib);
+ direct.rust_values().crate_name() = "direct";
+ direct.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(direct.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
target.set_output_type(Target::EXECUTABLE);
@@ -420,8 +674,13 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RenamedDeps) {
target.source_types_used().Set(SourceFile::SOURCE_RS);
target.rust_values().set_crate_root(main);
target.rust_values().crate_name() = "foo_bar";
- target.rust_values().aliased_deps()[another_rlib.label()] = "direct_renamed";
- target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ // A direct dependency is renamed.
+ target.rust_values().aliased_deps()[direct.label()] = "direct_renamed";
+ // A transitive public dependency, through `rlib`, is renamed.
+ target.rust_values().aliased_deps()[transitive.label()] =
+ "transitive_renamed";
+ target.private_deps().push_back(LabelTargetPair(&direct));
+ target.private_deps().push_back(LabelTargetPair(&rlib));
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -442,9 +701,15 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RenamedDeps) {
"target_output_name = bar\n"
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/foo/libdirect.rlib\n"
- " externs = --extern direct_renamed=obj/foo/libdirect.rlib\n"
- " rustdeps = -Ldependency=obj/foo\n"
+ "../../foo/main.rs obj/bar/libdirect.rlib obj/baz/libmylib.rlib "
+ "obj/faz/libtransitive.rlib\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
+ " externs = --extern direct_renamed=obj/bar/libdirect.rlib "
+ "--extern mylib=obj/baz/libmylib.rlib "
+ "--extern transitive_renamed=obj/faz/libtransitive.rlib\n"
+ " rustdeps = -Ldependency=obj/bar -Ldependency=obj/baz "
+ "-Ldependency=obj/faz\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
@@ -542,6 +807,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
"obj/bar/libmylib.rlib "
"obj/foo/libstatic.a ./libshared.so ./libshared_with_toc.so.TOC "
"|| obj/baz/sourceset.stamp\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs = --extern mylib=obj/bar/libmylib.rlib\n"
" rustdeps = -Ldependency=obj/bar "
"-Lnative=obj/baz -Lnative=obj/foo -Lnative=. "
@@ -584,6 +851,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/foo/libstatic.a\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
" rustdeps = -Lnative=obj/foo -Clink-arg=-Bdynamic "
"-Clink-arg=obj/foo/libstatic.a\n"
@@ -624,6 +893,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
"build obj/baz/libbaz.a: rust_staticlib ../../baz/lib.rs | "
"../../baz/lib.rs "
"obj/foo/libstatic.a\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs =\n"
" rustdeps = -Lnative=obj/foo -Clink-arg=-Bdynamic "
"-Clink-arg=obj/foo/libstatic.a\n"
@@ -634,6 +905,196 @@ TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
}
}
+TEST_F(NinjaRustBinaryTargetWriterTest, RlibInLibrary) {
+ Err err;
+ TestWithScope setup;
+
+ Target priv_sset_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//priv_sset_in_staticlib/"), "priv_sset_in_staticlib"));
+ priv_sset_in_staticlib.set_output_type(Target::SOURCE_SET);
+ priv_sset_in_staticlib.visibility().SetPublic();
+ priv_sset_in_staticlib.sources().push_back(
+ SourceFile("//priv_sset_in_staticlib/lib.cc"));
+ priv_sset_in_staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ priv_sset_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_sset_in_staticlib.OnResolved(&err));
+
+ Target pub_sset_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//pub_sset_in_staticlib/"), "pub_sset_in_staticlib"));
+ pub_sset_in_staticlib.set_output_type(Target::SOURCE_SET);
+ pub_sset_in_staticlib.visibility().SetPublic();
+ pub_sset_in_staticlib.sources().push_back(
+ SourceFile("//pub_sset_in_staticlib/lib.cc"));
+ pub_sset_in_staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pub_sset_in_staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_sset_in_staticlib.OnResolved(&err));
+
+ Target priv_sset_in_dylib(
+ setup.settings(),
+ Label(SourceDir("//priv_sset_in_dylib/"), "priv_sset_in_dylib"));
+ priv_sset_in_dylib.set_output_type(Target::SOURCE_SET);
+ priv_sset_in_dylib.visibility().SetPublic();
+ priv_sset_in_dylib.sources().push_back(
+ SourceFile("//priv_sset_in_dylib/lib.cc"));
+ priv_sset_in_dylib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ priv_sset_in_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(priv_sset_in_dylib.OnResolved(&err));
+
+ Target pub_sset_in_dylib(
+ setup.settings(),
+ Label(SourceDir("//pub_sset_in_dylib"), "pub_sset_in_dylib"));
+ pub_sset_in_dylib.set_output_type(Target::SOURCE_SET);
+ pub_sset_in_dylib.visibility().SetPublic();
+ pub_sset_in_dylib.sources().push_back(
+ SourceFile("//pub_sset_in_dylib/lib.cc"));
+ pub_sset_in_dylib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pub_sset_in_dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(pub_sset_in_dylib.OnResolved(&err));
+
+ Target priv_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//priv_in_staticlib/"), "priv_in_staticlib"));
+ priv_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ priv_in_staticlib.visibility().SetPublic();
+ SourceFile priv_in_staticlib_root("//priv_in_staticlib/lib.rs");
+ priv_in_staticlib.sources().push_back(priv_in_staticlib_root);
+ priv_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_staticlib.rust_values().set_crate_root(priv_in_staticlib_root);
+ priv_in_staticlib.rust_values().crate_name() = "priv_in_staticlib";
+ priv_in_staticlib.SetToolchain(setup.toolchain());
+ priv_in_staticlib.private_deps().push_back(
+ LabelTargetPair(&priv_sset_in_staticlib));
+ ASSERT_TRUE(priv_in_staticlib.OnResolved(&err));
+
+ Target pub_in_staticlib(
+ setup.settings(),
+ Label(SourceDir("//pub_in_staticlib/"), "pub_in_staticlib"));
+ pub_in_staticlib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_staticlib.visibility().SetPublic();
+ SourceFile pub_in_staticlib_root("//pub_in_staticlib/lib.rs");
+ pub_in_staticlib.sources().push_back(pub_in_staticlib_root);
+ pub_in_staticlib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_staticlib.rust_values().set_crate_root(pub_in_staticlib_root);
+ pub_in_staticlib.rust_values().crate_name() = "pub_in_staticlib";
+ pub_in_staticlib.SetToolchain(setup.toolchain());
+ pub_in_staticlib.private_deps().push_back(
+ LabelTargetPair(&pub_sset_in_staticlib));
+ ASSERT_TRUE(pub_in_staticlib.OnResolved(&err));
+
+ Target priv_in_dylib(setup.settings(),
+ Label(SourceDir("//priv_in_dylib/"), "priv_in_dylib"));
+ priv_in_dylib.set_output_type(Target::RUST_LIBRARY);
+ priv_in_dylib.visibility().SetPublic();
+ SourceFile priv_in_dylib_root("//priv_in_dylib/lib.rs");
+ priv_in_dylib.sources().push_back(priv_in_dylib_root);
+ priv_in_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ priv_in_dylib.rust_values().set_crate_root(priv_in_dylib_root);
+ priv_in_dylib.rust_values().crate_name() = "priv_in_dylib";
+ priv_in_dylib.SetToolchain(setup.toolchain());
+ priv_in_dylib.private_deps().push_back(LabelTargetPair(&priv_sset_in_dylib));
+ ASSERT_TRUE(priv_in_dylib.OnResolved(&err));
+
+ Target pub_in_dylib(setup.settings(),
+ Label(SourceDir("//pub_in_dylib/"), "pub_in_dylib"));
+ pub_in_dylib.set_output_type(Target::RUST_LIBRARY);
+ pub_in_dylib.visibility().SetPublic();
+ SourceFile pub_in_dylib_root("//pub_in_dylib/lib.rs");
+ pub_in_dylib.sources().push_back(pub_in_dylib_root);
+ pub_in_dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ pub_in_dylib.rust_values().set_crate_root(pub_in_dylib_root);
+ pub_in_dylib.rust_values().crate_name() = "pub_in_dylib";
+ pub_in_dylib.SetToolchain(setup.toolchain());
+ pub_in_dylib.private_deps().push_back(LabelTargetPair(&pub_sset_in_dylib));
+ ASSERT_TRUE(pub_in_dylib.OnResolved(&err));
+
+ Target staticlib(setup.settings(),
+ Label(SourceDir("//staticlib/"), "staticlib"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//staticlib/lib.cc"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.public_deps().push_back(LabelTargetPair(&pub_in_staticlib));
+ staticlib.private_deps().push_back(LabelTargetPair(&priv_in_staticlib));
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
+ Target dylib(setup.settings(), Label(SourceDir("//dylib/"), "dylib"));
+ dylib.set_output_type(Target::SHARED_LIBRARY);
+ dylib.visibility().SetPublic();
+ SourceFile dylib_root("//dylib/lib.rs");
+ dylib.sources().push_back(dylib_root);
+ dylib.source_types_used().Set(SourceFile::SOURCE_RS);
+ dylib.rust_values().set_crate_root(dylib_root);
+ dylib.rust_values().crate_name() = "dylib";
+ dylib.public_deps().push_back(LabelTargetPair(&pub_in_dylib));
+ dylib.private_deps().push_back(LabelTargetPair(&priv_in_dylib));
+ dylib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dylib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//exe/"), "exe"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//exe/main.rs");
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "exe";
+ target.private_deps().push_back(LabelTargetPair(&staticlib));
+ target.private_deps().push_back(LabelTargetPair(&dylib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = exe\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/exe\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build ./exe: rust_bin ../../exe/main.rs | "
+ "../../exe/main.rs "
+ "obj/pub_sset_in_staticlib/pub_sset_in_staticlib.lib.o "
+ "obj/priv_sset_in_staticlib/priv_sset_in_staticlib.lib.o "
+ "obj/staticlib/libstaticlib.a "
+ "obj/dylib/libdylib.so "
+ "obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "obj/priv_in_staticlib/libpriv_in_staticlib.rlib "
+ "obj/pub_in_dylib/libpub_in_dylib.rlib || "
+ "obj/pub_sset_in_staticlib/pub_sset_in_staticlib.stamp "
+ "obj/priv_sset_in_staticlib/priv_sset_in_staticlib.stamp\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
+ " externs = "
+ "--extern pub_in_staticlib=obj/pub_in_staticlib/libpub_in_staticlib.rlib "
+ "--extern dylib=obj/dylib/libdylib.so "
+ "--extern pub_in_dylib=obj/pub_in_dylib/libpub_in_dylib.rlib\n"
+ " rustdeps = -Ldependency=obj/pub_in_staticlib "
+ "-Ldependency=obj/priv_in_staticlib -Ldependency=obj/dylib "
+ "-Ldependency=obj/pub_in_dylib -Ldependency=obj/priv_in_dylib "
+ "-Lnative=obj/pub_sset_in_staticlib "
+ "-Lnative=obj/priv_sset_in_staticlib "
+ "-Lnative=obj/staticlib -Clink-arg=-Bdynamic "
+ "-Clink-arg=obj/pub_sset_in_staticlib/pub_sset_in_staticlib.lib.o "
+ "-Clink-arg=obj/priv_sset_in_staticlib/priv_sset_in_staticlib.lib.o "
+ "-Clink-arg=obj/staticlib/libstaticlib.a\n"
+ " ldflags =\n"
+ " sources = ../../exe/main.rs\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
+
TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {
Err err;
TestWithScope setup;
@@ -670,6 +1131,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {
"\n"
"build ./foo_bar.exe: rust_bin ../../foo/main.rs | ../../foo/input3.rs "
"../../foo/main.rs\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
@@ -716,6 +1179,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, LibsAndLibDirs) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/input.rs "
"../../foo/main.rs\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
" rustdeps = -Lnative=../../baz -lquux\n"
" ldflags =\n"
@@ -725,22 +1190,39 @@ TEST_F(NinjaRustBinaryTargetWriterTest, LibsAndLibDirs) {
}
}
+// Test that neither public nor private rust dependencies of a proc-macro are
+// transitively acquired as accessible dependencies by users of the macro. But
+// the macro itself is listed as an accessible dependency (via --extern).
TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
Err err;
TestWithScope setup;
- Target procmacrodep(setup.settings(),
- Label(SourceDir("//baz/"), "mymacrodep"));
- procmacrodep.set_output_type(Target::RUST_LIBRARY);
- procmacrodep.visibility().SetPublic();
- SourceFile bazlib("//baz/lib.rs");
- procmacrodep.sources().push_back(SourceFile("//baz/mylib.rs"));
- procmacrodep.sources().push_back(bazlib);
- procmacrodep.source_types_used().Set(SourceFile::SOURCE_RS);
- procmacrodep.rust_values().set_crate_root(bazlib);
- procmacrodep.rust_values().crate_name() = "mymacrodep";
- procmacrodep.SetToolchain(setup.toolchain());
- ASSERT_TRUE(procmacrodep.OnResolved(&err));
+ Target procmacropublicdep(
+ setup.settings(), Label(SourceDir("//baz/public/"), "mymacropublicdep"));
+ procmacropublicdep.set_output_type(Target::RUST_LIBRARY);
+ procmacropublicdep.visibility().SetPublic();
+ SourceFile publicbazlib("//baz/public/lib.rs");
+ procmacropublicdep.sources().push_back(SourceFile("//baz/public/mylib.rs"));
+ procmacropublicdep.sources().push_back(publicbazlib);
+ procmacropublicdep.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacropublicdep.rust_values().set_crate_root(publicbazlib);
+ procmacropublicdep.rust_values().crate_name() = "publicdep";
+ procmacropublicdep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacropublicdep.OnResolved(&err));
+
+ Target procmacroprivatedep(
+ setup.settings(),
+ Label(SourceDir("//baz/private/"), "mymacroprivatedep"));
+ procmacroprivatedep.set_output_type(Target::RUST_LIBRARY);
+ procmacroprivatedep.visibility().SetPublic();
+ SourceFile privatebazlib("//baz/private/lib.rs");
+ procmacroprivatedep.sources().push_back(SourceFile("//baz/private/mylib.rs"));
+ procmacroprivatedep.sources().push_back(privatebazlib);
+ procmacroprivatedep.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacroprivatedep.rust_values().set_crate_root(privatebazlib);
+ procmacroprivatedep.rust_values().crate_name() = "privatedep";
+ procmacroprivatedep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacroprivatedep.OnResolved(&err));
Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
procmacro.set_output_type(Target::RUST_PROC_MACRO);
@@ -754,7 +1236,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
// Add a dependency to the procmacro so we can be sure its output
// directory is not propagated downstream beyond the proc macro.
- procmacro.private_deps().push_back(LabelTargetPair(&procmacrodep));
+ procmacro.public_deps().push_back(LabelTargetPair(&procmacropublicdep));
+ procmacro.private_deps().push_back(LabelTargetPair(&procmacroprivatedep));
procmacro.SetToolchain(setup.toolchain());
ASSERT_TRUE(procmacro.OnResolved(&err));
@@ -775,9 +1258,16 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
"target_output_name = libmymacro\n"
"\n"
"build obj/bar/libmymacro.so: rust_macro ../../bar/lib.rs | "
- "../../bar/mylib.rs ../../bar/lib.rs obj/baz/libmymacrodep.rlib\n"
- " externs = --extern mymacrodep=obj/baz/libmymacrodep.rlib\n"
- " rustdeps = -Ldependency=obj/baz\n"
+ "../../bar/mylib.rs ../../bar/lib.rs "
+ "obj/baz/public/libmymacropublicdep.rlib "
+ "obj/baz/private/libmymacroprivatedep.rlib\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
+ " externs = "
+ "--extern publicdep=obj/baz/public/libmymacropublicdep.rlib "
+ "--extern privatedep=obj/baz/private/libmymacroprivatedep.rlib\n"
+ " rustdeps = -Ldependency=obj/baz/public "
+ "-Ldependency=obj/baz/private\n"
" ldflags =\n"
" sources = ../../bar/mylib.rs ../../bar/lib.rs\n";
std::string out_str = out.str();
@@ -815,6 +1305,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, RustProcMacro) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/bar/libmymacro.so\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs = --extern mymacro=obj/bar/libmymacro.so\n"
" rustdeps = -Ldependency=obj/bar\n"
" ldflags =\n"
@@ -858,6 +1350,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
"\n"
"build obj/bar/libmylib.rlib: rust_rlib ../../bar/lib.rs | "
"../../bar/mylib.rs ../../bar/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
@@ -902,8 +1396,11 @@ TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
"target_out_dir = obj/foo\n"
"target_output_name = bar\n"
"\n"
- "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
- "../../foo/main.rs obj/bar/libmylib.rlib || obj/baz/group.stamp\n"
+ "build ./foo_bar: rust_bin ../../foo/main.rs | "
+ "../../foo/source.rs ../../foo/main.rs obj/bar/libmylib.rlib || "
+ "obj/baz/group.stamp\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs = --extern mylib=obj/bar/libmylib.rlib\n"
" rustdeps = -Ldependency=obj/bar\n"
" ldflags =\n"
@@ -955,6 +1452,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, Externs) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs ../../foo/lib1.rlib\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs = --extern lib1=../../foo/lib1.rlib --extern "
"lib2=lib2.rlib\n"
" rustdeps =\n"
@@ -989,7 +1488,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, Inputs) {
writer.Run();
const char expected[] =
- "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json ../../foo/template.h\n"
+ "build obj/foo/bar.inputs.stamp: stamp ../../foo/config.json "
+ "../../foo/template.h\n"
"crate_name = foo_bar\n"
"crate_type = bin\n"
"output_extension = \n"
@@ -1003,6 +1503,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, Inputs) {
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs ../../foo/config.json ../../foo/template.h "
"|| obj/foo/bar.inputs.stamp\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
@@ -1044,6 +1546,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, CdylibDeps) {
"\n"
"build obj/bar/libmylib.so: rust_cdylib ../../bar/lib.rs | "
"../../bar/lib.rs\n"
+ " source_file_part = lib.rs\n"
+ " source_name_part = lib\n"
" externs =\n"
" rustdeps =\n"
" ldflags =\n"
@@ -1082,9 +1586,11 @@ TEST_F(NinjaRustBinaryTargetWriterTest, CdylibDeps) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
"../../foo/main.rs obj/bar/libmylib.so\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs =\n"
- " rustdeps = -Ldependency=obj/bar -Lnative=obj/bar "
- "-Clink-arg=-Bdynamic -Clink-arg=obj/bar/libmylib.so\n"
+ " rustdeps = -Lnative=obj/bar -Clink-arg=-Bdynamic "
+ "-Clink-arg=obj/bar/libmylib.so\n"
" ldflags =\n"
" sources = ../../foo/source.rs ../../foo/main.rs\n";
std::string out_str = out.str();
@@ -1157,6 +1663,8 @@ TEST_F(NinjaRustBinaryTargetWriterTest, TransitivePublicNonRustDeps) {
"\n"
"build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/main.rs "
"obj/bar/libmylib.rlib ./libshared.so ./libimplicit.so\n"
+ " source_file_part = main.rs\n"
+ " source_name_part = main\n"
" externs = --extern mylib=obj/bar/libmylib.rlib\n"
" rustdeps = -Ldependency=obj/bar -Lnative=. -Clink-arg=-Bdynamic "
"-Clink-arg=./libshared.so -Clink-arg=./libimplicit.so\n"
@@ -1166,3 +1674,138 @@ TEST_F(NinjaRustBinaryTargetWriterTest, TransitivePublicNonRustDeps) {
EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
}
}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, TransitiveRustDepsThroughSourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target rlib_pub(setup.settings(),
+ Label(SourceDir("//public/"), "behind_sourceset_public"));
+ rlib_pub.set_output_type(Target::RUST_LIBRARY);
+ rlib_pub.visibility().SetPublic();
+ SourceFile rlib_pub_root("//public/lib.rs");
+ rlib_pub.sources().push_back(
+ SourceFile("//public/behind_sourceset_public.rs"));
+ rlib_pub.sources().push_back(rlib_pub_root);
+ rlib_pub.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_pub.rust_values().set_crate_root(rlib_pub_root);
+ rlib_pub.rust_values().crate_name() = "behind_sourceset_public";
+ rlib_pub.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_pub.OnResolved(&err));
+
+ Target rlib_priv(setup.settings(),
+ Label(SourceDir("//private/"), "behind_sourceset_private"));
+ rlib_priv.set_output_type(Target::RUST_LIBRARY);
+ rlib_priv.visibility().SetPublic();
+ SourceFile rlib_priv_root("//private/lib.rs");
+ rlib_priv.sources().push_back(
+ SourceFile("//private/behind_sourceset_private.rs"));
+ rlib_priv.sources().push_back(rlib_priv_root);
+ rlib_priv.source_types_used().Set(SourceFile::SOURCE_RS);
+ rlib_priv.rust_values().set_crate_root(rlib_priv_root);
+ rlib_priv.rust_values().crate_name() = "behind_sourceset_private";
+ rlib_priv.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib_priv.OnResolved(&err));
+
+ Target sset(setup.settings(), Label(SourceDir("//sset/"), "bar"));
+ sset.set_output_type(Target::SOURCE_SET);
+ sset.visibility().SetPublic();
+ sset.sources().push_back(SourceFile("//sset/input1.cc"));
+ sset.source_types_used().Set(SourceFile::SOURCE_CPP);
+ sset.SetToolchain(setup.toolchain());
+ sset.public_deps().push_back(LabelTargetPair(&rlib_pub));
+ sset.private_deps().push_back(LabelTargetPair(&rlib_priv));
+ ASSERT_TRUE(sset.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//linked/"), "exe"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//linked/exe.rs");
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "exe";
+ target.private_deps().push_back(LabelTargetPair(&sset));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = exe\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/linked\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build ./exe: rust_bin ../../linked/exe.rs | ../../linked/exe.rs "
+ "obj/sset/bar.input1.o obj/public/libbehind_sourceset_public.rlib "
+ "obj/private/libbehind_sourceset_private.rlib || obj/sset/bar.stamp\n"
+ " source_file_part = exe.rs\n"
+ " source_name_part = exe\n"
+ " externs = --extern "
+ "behind_sourceset_public=obj/public/libbehind_sourceset_public.rlib\n"
+ " rustdeps = -Ldependency=obj/public -Ldependency=obj/private "
+ "-Lnative=obj/sset -Clink-arg=-Bdynamic "
+ "-Clink-arg=obj/sset/bar.input1.o\n"
+ " ldflags =\n"
+ " sources = ../../linked/exe.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, Pool) {
+ Err err;
+ TestWithScope setup;
+
+ Pool pool(setup.settings(),
+ Label(SourceDir("//foo/"), "pool", setup.toolchain()->label().dir(),
+ setup.toolchain()->label().name()));
+ pool.set_depth(42);
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ SourceFile main("//foo/source.rs");
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "bar";
+ target.set_output_type(Target::EXECUTABLE);
+ target.set_pool(LabelPtrPair<Pool>(&pool));
+ target.visibility().SetPublic();
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = bar\n"
+ "crate_type = bin\n"
+ "output_extension = \n"
+ "output_dir = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build ./bar: rust_bin ../../foo/source.rs | ../../foo/source.rs\n"
+ " source_file_part = source.rs\n"
+ " source_name_part = source\n"
+ " externs =\n"
+ " rustdeps =\n"
+ " ldflags =\n"
+ " sources = ../../foo/source.rs\n"
+ " pool = foo_pool\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+}
diff --git a/gn/src/gn/ninja_target_command_util.cc b/gn/src/gn/ninja_target_command_util.cc
index b542fb26fdd..a691ae35614 100644
--- a/gn/src/gn/ninja_target_command_util.cc
+++ b/gn/src/gn/ninja_target_command_util.cc
@@ -51,10 +51,13 @@ void WriteOneFlag(RecursiveWriterConfig config,
EscapeOptions flag_escape_options,
PathOutput& path_output,
std::ostream& out,
- bool write_substitution) {
+ bool write_substitution,
+ bool indent) {
if (!target->toolchain()->substitution_bits().used.count(subst_enum))
return;
+ if (indent)
+ out << " ";
if (write_substitution)
out << subst_enum->ninja_name << " =";
diff --git a/gn/src/gn/ninja_target_command_util.h b/gn/src/gn/ninja_target_command_util.h
index b0179a1cf13..93666fbaa3c 100644
--- a/gn/src/gn/ninja_target_command_util.h
+++ b/gn/src/gn/ninja_target_command_util.h
@@ -102,7 +102,8 @@ void WriteOneFlag(RecursiveWriterConfig config,
EscapeOptions flag_escape_options,
PathOutput& path_output,
std::ostream& out,
- bool write_substitution = true);
+ bool write_substitution = true,
+ bool indent = false);
// Fills |outputs| with the object or gch file for the precompiled header of the
// given type (flag type and tool type must match).
diff --git a/gn/src/gn/ninja_target_writer.cc b/gn/src/gn/ninja_target_writer.cc
index efcf9912ef8..0f2514b4f43 100644
--- a/gn/src/gn/ninja_target_writer.cc
+++ b/gn/src/gn/ninja_target_writer.cc
@@ -8,6 +8,7 @@
#include "base/files/file_util.h"
#include "base/strings/string_util.h"
+#include "gn/c_substitution_type.h"
#include "gn/config_values_extractors.h"
#include "gn/err.h"
#include "gn/escape.h"
@@ -20,8 +21,10 @@
#include "gn/ninja_create_bundle_target_writer.h"
#include "gn/ninja_generated_file_target_writer.h"
#include "gn/ninja_group_target_writer.h"
+#include "gn/ninja_target_command_util.h"
#include "gn/ninja_utils.h"
#include "gn/output_file.h"
+#include "gn/rust_substitution_type.h"
#include "gn/scheduler.h"
#include "gn/string_output_buffer.h"
#include "gn/string_utils.h"
@@ -188,6 +191,178 @@ void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
out_ << std::endl;
}
+void NinjaTargetWriter::WriteCCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool respect_source_used) {
+ // Defines.
+ if (bits.used.count(&CSubstitutionDefines)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionDefines.ninja_name << " =";
+ RecursiveTargetConfigToStream<std::string>(kRecursiveWriterSkipDuplicates,
+ target_, &ConfigValues::defines,
+ DefineWriter(), out_);
+ out_ << std::endl;
+ }
+
+ // Framework search path.
+ if (bits.used.count(&CSubstitutionFrameworkDirs)) {
+ const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink);
+
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionFrameworkDirs.ninja_name << " =";
+ PathOutput framework_dirs_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ kRecursiveWriterSkipDuplicates, target_, &ConfigValues::framework_dirs,
+ FrameworkDirsWriter(framework_dirs_output,
+ tool->framework_dir_switch()),
+ out_);
+ out_ << std::endl;
+ }
+
+ // Include directories.
+ if (bits.used.count(&CSubstitutionIncludeDirs)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionIncludeDirs.ninja_name << " =";
+ PathOutput include_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ RecursiveTargetConfigToStream<SourceDir>(
+ kRecursiveWriterSkipDuplicates, target_, &ConfigValues::include_dirs,
+ IncludeWriter(include_path_output), out_);
+ out_ << std::endl;
+ }
+
+ bool has_precompiled_headers =
+ target_->config_values().has_precompiled_headers();
+
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_S)
+ : bits.used.count(&CSubstitutionAsmFlags)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionAsmFlags, false, Tool::kToolNone,
+ &ConfigValues::asmflags, opts, path_output_, out_, true,
+ indent);
+ }
+ if (respect_source_used
+ ? (target_->source_types_used().Get(SourceFile::SOURCE_C) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_M) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MM) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
+ : bits.used.count(&CSubstitutionCFlags)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlags,
+ false, Tool::kToolNone, &ConfigValues::cflags, opts,
+ path_output_, out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_C)
+ : bits.used.count(&CSubstitutionCFlagsC)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_, &CSubstitutionCFlagsC,
+ has_precompiled_headers, CTool::kCToolCc,
+ &ConfigValues::cflags_c, opts, path_output_, out_, true,
+ indent);
+ }
+ if (respect_source_used
+ ? (target_->source_types_used().Get(SourceFile::SOURCE_CPP) ||
+ target_->source_types_used().Get(SourceFile::SOURCE_MODULEMAP))
+ : bits.used.count(&CSubstitutionCFlagsCc)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
+ out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_M)
+ : bits.used.count(&CSubstitutionCFlagsObjC)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
+ path_output_, out_, true, indent);
+ }
+ if (respect_source_used
+ ? target_->source_types_used().Get(SourceFile::SOURCE_MM)
+ : bits.used.count(&CSubstitutionCFlagsObjCc)) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
+ path_output_, out_, true, indent);
+ }
+ if (target_->source_types_used().SwiftSourceUsed() || !respect_source_used) {
+ if (bits.used.count(&CSubstitutionSwiftModuleName)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftModuleName.ninja_name << " = ";
+ EscapeStringToStream(out_, target_->swift_values().module_name(), opts);
+ out_ << std::endl;
+ }
+
+ if (bits.used.count(&CSubstitutionSwiftBridgeHeader)) {
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftBridgeHeader.ninja_name << " = ";
+ if (!target_->swift_values().bridge_header().is_null()) {
+ path_output_.WriteFile(out_, target_->swift_values().bridge_header());
+ } else {
+ out_ << R"("")";
+ }
+ out_ << std::endl;
+ }
+
+ if (bits.used.count(&CSubstitutionSwiftModuleDirs)) {
+ // Uniquify the list of swiftmodule dirs (in case multiple swiftmodules
+ // are generated in the same directory).
+ UniqueVector<SourceDir> swiftmodule_dirs;
+ for (const Target* dep : target_->swift_values().modules())
+ swiftmodule_dirs.push_back(dep->swift_values().module_output_dir());
+
+ if (indent)
+ out_ << " ";
+ out_ << CSubstitutionSwiftModuleDirs.ninja_name << " =";
+ PathOutput swiftmodule_path_output(
+ path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
+ IncludeWriter swiftmodule_path_writer(swiftmodule_path_output);
+ for (const SourceDir& swiftmodule_dir : swiftmodule_dirs) {
+ swiftmodule_path_writer(swiftmodule_dir, out_);
+ }
+ out_ << std::endl;
+ }
+
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &CSubstitutionSwiftFlags, false, CTool::kCToolSwift,
+ &ConfigValues::swiftflags, opts, path_output_, out_, true,
+ indent);
+ }
+}
+
+void NinjaTargetWriter::WriteRustCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool always_write) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+
+ if (bits.used.count(&kRustSubstitutionRustFlags) || always_write) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &kRustSubstitutionRustFlags, false, Tool::kToolNone,
+ &ConfigValues::rustflags, opts, path_output_, out_, true,
+ indent);
+ }
+
+ if (bits.used.count(&kRustSubstitutionRustEnv) || always_write) {
+ WriteOneFlag(kRecursiveWriterKeepDuplicates, target_,
+ &kRustSubstitutionRustEnv, false, Tool::kToolNone,
+ &ConfigValues::rustenv, opts, path_output_, out_, true,
+ indent);
+ }
+}
+
std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
const std::vector<const Target*>& additional_hard_deps,
size_t num_stamp_uses) const {
diff --git a/gn/src/gn/ninja_target_writer.h b/gn/src/gn/ninja_target_writer.h
index 7a339ab5e82..3d38a14f584 100644
--- a/gn/src/gn/ninja_target_writer.h
+++ b/gn/src/gn/ninja_target_writer.h
@@ -39,6 +39,23 @@ class NinjaTargetWriter {
// identified by the given bits will be written.
void WriteSharedVars(const SubstitutionBits& bits);
+ // Writes out the substitution values that are shared between C compiler tools
+ // and action tools. Only the substitutions identified by the given bits will
+ // be written.
+ // If respect_source_used is set, the generated substitution values will
+ // respect the types of source code used; otherwise they will respect the bits
+ // passed in.
+ void WriteCCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool respect_source_used);
+
+ // Writes out the substitution values that are shared between Rust tools
+ // and action tools. Only the substitutions identified by the given bits will
+ // be written, unless 'always_write' is specified.
+ void WriteRustCompilerVars(const SubstitutionBits& bits,
+ bool indent,
+ bool always_write);
+
// Writes to the output stream a stamp rule for input dependencies, and
// returns the file to be appended to source rules that encodes the
// order-only dependencies for the current target.
diff --git a/gn/src/gn/ninja_toolchain_writer.cc b/gn/src/gn/ninja_toolchain_writer.cc
index 7be771932c7..2314bae5d3d 100644
--- a/gn/src/gn/ninja_toolchain_writer.cc
+++ b/gn/src/gn/ninja_toolchain_writer.cc
@@ -9,6 +9,7 @@
#include "base/files/file_util.h"
#include "base/strings/stringize_macros.h"
#include "gn/build_settings.h"
+#include "gn/builtin_tool.h"
#include "gn/c_tool.h"
#include "gn/filesystem_utils.h"
#include "gn/general_tool.h"
@@ -43,8 +44,10 @@ void NinjaToolchainWriter::Run(
std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
for (const auto& tool : toolchain_->tools()) {
- if (tool.second->name() == GeneralTool::kGeneralToolAction)
+ if (tool.second->name() == GeneralTool::kGeneralToolAction ||
+ tool.second->AsBuiltin()) {
continue;
+ }
WriteToolRule(tool.second.get(), rule_prefix);
}
out_ << std::endl;
diff --git a/gn/src/gn/parse_tree.cc b/gn/src/gn/parse_tree.cc
index e9ca8fa6671..28d5313483b 100644
--- a/gn/src/gn/parse_tree.cc
+++ b/gn/src/gn/parse_tree.cc
@@ -258,10 +258,12 @@ void ParseNode::AddCommentsJSONNodes(base::Value* out_value) const {
std::unique_ptr<ParseNode> ParseNode::BuildFromJSON(const base::Value& value) {
const std::string& str_type = value.FindKey(kJsonNodeType)->GetString();
-#define RETURN_IF_MATCHES_NAME(t) \
- if (str_type == t::kDumpNodeName) { \
- return t::NewFromJSON(value); \
- }
+#define RETURN_IF_MATCHES_NAME(t) \
+ do { \
+ if (str_type == t::kDumpNodeName) { \
+ return t::NewFromJSON(value); \
+ } \
+ } while(0)
RETURN_IF_MATCHES_NAME(AccessorNode);
RETURN_IF_MATCHES_NAME(BinaryOpNode);
@@ -332,7 +334,8 @@ base::Value AccessorNode::GetJSONNode() const {
const base::Value* child = value.FindKey(kJsonNodeChild); \
if (!child || !child->is_list()) { \
return nullptr; \
- }
+ } \
+ (void)(0) // this is to supress extra semicolon warning.
// static
std::unique_ptr<AccessorNode> AccessorNode::NewFromJSON(
diff --git a/gn/src/gn/parser.cc b/gn/src/gn/parser.cc
index a4ad2446dd9..96739a5a51a 100644
--- a/gn/src/gn/parser.cc
+++ b/gn/src/gn/parser.cc
@@ -181,19 +181,20 @@ Scopes
All execution happens in the context of a scope which holds the current state
(like variables). With the exception of loops and conditions, '{' introduces
- a new scope that has a parent reference to the old scope.
+ a new scope.
- Variable reads recursively search all nested scopes until the variable is
- found or there are no more scopes. Variable writes always go into the current
- scope. This means that after the closing '}' (again excepting loops and
- conditions), all local variables will be restored to the previous values.
- This also means that "foo = foo" can do useful work by copying a variable
- into the current scope that was defined in a containing scope.
+ Most scopes have a parent reference to the old scope. Variable reads
+ recursively search all parent scopes until the variable is found or there are
+ no more scopes. Variable writes always go into the current scope. This means
+ that after the closing '}' (again excepting loops and conditions), all local
+ variables will be restored to the previous values. This also means that "foo
+ = foo" can do useful work by copying a variable into the current scope that
+ was defined in a containing scope.
- Scopes can also be assigned to variables. Such scopes can be created by
- functions like exec_script, when invoking a template (the template code
- refers to the variables set by the invoking code by the implicitly-created
- "invoker" scope), or explicitly like:
+ Scopes can be assigned to variables. Examples of such scopes are the
+ implicitly-created "invoker" when invoking a template (which refers to the
+ variables set by the invoking code), scopes created by functions like
+ exec_script, and scopes explicitly created like
empty_scope = {}
myvalues = {
@@ -201,10 +202,14 @@ Scopes
bar = "something"
}
- Inside such a scope definition can be any GN code including conditionals and
- function calls. After the close of the scope, it will contain all variables
- explicitly set by the code contained inside it. After this, the values can be
- read, modified, or added to:
+ In the case of explicitly created scopes and scopes created by functions like
+ exec_script, there is no reference to the parent scope. Such scopes are fully
+ self-contained and do not "inherit" values from their defining scope.
+
+ Inside an explicit scope definition can be any GN code including conditionals
+ and function calls. After the close of the scope, it will contain all
+ variables explicitly set by the code contained inside it. After this, the
+ values can be read, modified, or added to:
myvalues.foo += 2
empty_scope.new_thing = [ 1, 2, 3 ]
diff --git a/gn/src/gn/pointer_set.h b/gn/src/gn/pointer_set.h
index 6dc18b4c509..2bec930b04a 100644
--- a/gn/src/gn/pointer_set.h
+++ b/gn/src/gn/pointer_set.h
@@ -6,6 +6,8 @@
#define SRC_GN_POINTER_SET_H_
#include <functional>
+#include <vector>
+
#include "gn/hash_table_base.h"
// PointerSet<T> is a fast implemention of a set of non-owning and non-null
@@ -187,6 +189,16 @@ class PointerSet : public HashTableBase<PointerSetNode> {
return true;
}
+ // Convert this to a vector, more convenient and slightly faster than using
+ // std::vector<T*>(set.begin(), set.end()).
+ std::vector<T*> ToVector() const {
+ std::vector<T*> result(this->size());
+ auto it_result = result.begin();
+ for (auto it = this->begin(); it.valid(); ++it)
+ *it_result++ = *it;
+ return result;
+ }
+
private:
// Lookup node matching a given pointer. If result->valid() is true
// then the pointer was found, otherwise, this is the location of
diff --git a/gn/src/gn/pointer_set_unittest.cc b/gn/src/gn/pointer_set_unittest.cc
index 0bb4d6a81e8..e71a97d7107 100644
--- a/gn/src/gn/pointer_set_unittest.cc
+++ b/gn/src/gn/pointer_set_unittest.cc
@@ -5,6 +5,8 @@
#include "gn/pointer_set.h"
#include "util/test/test.h"
+#include <algorithm>
+
struct Foo {
int x;
};
@@ -165,3 +167,15 @@ TEST(PointerSet, IntersectionWith) {
EXPECT_EQ(set1, set);
EXPECT_EQ(set2, set);
}
+
+TEST(PointerSet, ToVector) {
+ TestPointerSet set(kFullList.begin(), kFullList.end());
+ auto vector = set.ToVector();
+ EXPECT_EQ(vector.size(), kFullList.size());
+
+ // NOTE: Order of items in the result is not guaranteed
+ // so just check whether items are available in it.
+ EXPECT_NE(std::find(vector.begin(), vector.end(), kFoo1), vector.end());
+ EXPECT_NE(std::find(vector.begin(), vector.end(), kFoo2), vector.end());
+ EXPECT_NE(std::find(vector.begin(), vector.end(), kFoo3), vector.end());
+} \ No newline at end of file
diff --git a/gn/src/gn/resolved_target_deps.h b/gn/src/gn/resolved_target_deps.h
new file mode 100644
index 00000000000..57e961f656b
--- /dev/null
+++ b/gn/src/gn/resolved_target_deps.h
@@ -0,0 +1,90 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_RESOLVED_TARGET_DEPS_H_
+#define TOOLS_GN_RESOLVED_TARGET_DEPS_H_
+
+#include "base/containers/span.h"
+#include "gn/label_ptr.h"
+
+#include <memory>
+
+// A class used to record the dependencies of a given Target in
+// a way that is much more efficient to iterate over than having three
+// separate LabelTargetVector instances. Technically equivalent to
+// DepsIterator, but profiling shows that this class is much faster
+// to use during graph-traversal heavy operations.
+//
+// Usage is:
+// 1) Create instance, passing const references to the LabelTargetVector
+// instances for the private, public and data deps for the target.
+//
+// 2) Use private_deps(), public_deps(), data_deps(), linked_deps()
+// and all_deps() to retrieve spans that cover various subsets of
+// interests. These can be used directly in for-range loops as in:
+//
+// for (const Target* target : resolved.linked_deps()) {
+// ..
+// }
+//
+class ResolvedTargetDeps {
+ public:
+ ResolvedTargetDeps() = default;
+
+ ResolvedTargetDeps(const LabelTargetVector& public_deps,
+ const LabelTargetVector& private_deps,
+ const LabelTargetVector& data_deps)
+ : public_count_(static_cast<uint32_t>(public_deps.size())),
+ private_count_(static_cast<uint32_t>(private_deps.size())),
+ data_count_(static_cast<uint32_t>(data_deps.size())),
+ deps_(Allocate(public_deps, private_deps, data_deps)) {}
+
+ size_t size() const { return private_count_ + public_count_ + data_count_; }
+
+ base::span<const Target*> public_deps() const {
+ return {deps_.get(), public_count_};
+ }
+
+ base::span<const Target*> private_deps() const {
+ return {deps_.get() + public_count_, private_count_};
+ }
+
+ base::span<const Target*> data_deps() const {
+ return {deps_.get() + private_count_ + public_count_, data_count_};
+ }
+
+ base::span<const Target*> linked_deps() const {
+ return {deps_.get(), private_count_ + public_count_};
+ }
+
+ base::span<const Target*> all_deps() const {
+ return {deps_.get(), private_count_ + public_count_ + data_count_};
+ }
+
+ static std::unique_ptr<const Target*[]> Allocate(
+ const LabelTargetVector& public_deps,
+ const LabelTargetVector& private_deps,
+ const LabelTargetVector& data_deps) {
+ size_t total_size =
+ private_deps.size() + public_deps.size() + data_deps.size();
+ auto result = std::make_unique<const Target*[]>(total_size);
+ const Target** ptr = result.get();
+ for (const auto& pair : public_deps)
+ *ptr++ = pair.ptr;
+ for (const auto& pair : private_deps)
+ *ptr++ = pair.ptr;
+ for (const auto& pair : data_deps)
+ *ptr++ = pair.ptr;
+ return result;
+ }
+
+ private:
+ uint32_t public_count_ = 0;
+ uint32_t private_count_ = 0;
+ uint32_t data_count_ = 0;
+ // Store the pointers in the following order: public, private, data.
+ std::unique_ptr<const Target*[]> deps_;
+};
+
+#endif // TOOLS_GN_RESOLVED_TARGET_DEPS_H_
diff --git a/gn/src/gn/resolved_target_deps_unittest.cc b/gn/src/gn/resolved_target_deps_unittest.cc
new file mode 100644
index 00000000000..74d623d091d
--- /dev/null
+++ b/gn/src/gn/resolved_target_deps_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gn/resolved_target_deps.h"
+#include "gn/test_with_scope.h"
+#include "util/test/test.h"
+
+TEST(ResolvedTargetDeps, DefaultConstruction) {
+ ResolvedTargetDeps deps;
+ EXPECT_EQ(0u, deps.size());
+ EXPECT_TRUE(deps.public_deps().empty());
+ EXPECT_TRUE(deps.private_deps().empty());
+ EXPECT_TRUE(deps.data_deps().empty());
+ EXPECT_TRUE(deps.linked_deps().empty());
+ EXPECT_TRUE(deps.all_deps().empty());
+}
+
+TEST(ResolvedTargetDeps, Construction) {
+ TestWithScope setup;
+ TestTarget a(setup, "//foo:a", Target::STATIC_LIBRARY);
+ TestTarget b(setup, "//foo:b", Target::SOURCE_SET);
+ TestTarget c(setup, "//foo:c", Target::SOURCE_SET);
+ TestTarget d(setup, "//foo:d", Target::SOURCE_SET);
+ TestTarget e(setup, "//foo:e", Target::EXECUTABLE);
+
+ LabelTargetVector public_vec;
+ LabelTargetVector private_vec;
+ LabelTargetVector data_vec;
+
+ public_vec.emplace_back(&a);
+ public_vec.emplace_back(&b);
+ private_vec.emplace_back(&c);
+ private_vec.emplace_back(&d);
+ data_vec.emplace_back(&e);
+
+ ResolvedTargetDeps deps(public_vec, private_vec, data_vec);
+ EXPECT_EQ(5u, deps.size());
+
+ EXPECT_EQ(2u, deps.public_deps().size());
+ EXPECT_EQ(&a, deps.public_deps()[0]);
+ EXPECT_EQ(&b, deps.public_deps()[1]);
+
+ EXPECT_EQ(2u, deps.private_deps().size());
+ EXPECT_EQ(&c, deps.private_deps()[0]);
+ EXPECT_EQ(&d, deps.private_deps()[1]);
+
+ EXPECT_EQ(1u, deps.data_deps().size());
+ EXPECT_EQ(&e, deps.data_deps()[0]);
+
+ EXPECT_EQ(4u, deps.linked_deps().size());
+ EXPECT_EQ(&a, deps.linked_deps()[0]);
+ EXPECT_EQ(&b, deps.linked_deps()[1]);
+ EXPECT_EQ(&c, deps.linked_deps()[2]);
+ EXPECT_EQ(&d, deps.linked_deps()[3]);
+
+ EXPECT_EQ(5u, deps.all_deps().size());
+ EXPECT_EQ(&a, deps.all_deps()[0]);
+ EXPECT_EQ(&b, deps.all_deps()[1]);
+ EXPECT_EQ(&c, deps.all_deps()[2]);
+ EXPECT_EQ(&d, deps.all_deps()[3]);
+ EXPECT_EQ(&e, deps.all_deps()[4]);
+}
diff --git a/gn/src/gn/runtime_deps.cc b/gn/src/gn/runtime_deps.cc
index 25eb2efd695..9ca016d9a29 100644
--- a/gn/src/gn/runtime_deps.cc
+++ b/gn/src/gn/runtime_deps.cc
@@ -133,7 +133,7 @@ bool CollectRuntimeDepsFromFlag(const BuildSettings* build_settings,
RuntimeDepsVector* files_to_write,
Err* err) {
std::string deps_target_list_file =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
switches::kRuntimeDepsListFile);
if (deps_target_list_file.empty())
diff --git a/gn/src/gn/rust_project_writer.cc b/gn/src/gn/rust_project_writer.cc
index c11f967b087..8bcd4950973 100644
--- a/gn/src/gn/rust_project_writer.cc
+++ b/gn/src/gn/rust_project_writer.cc
@@ -10,6 +10,7 @@
#include <tuple>
#include "base/json/string_escape.h"
+#include "base/strings/string_split.h"
#include "gn/builder.h"
#include "gn/deps_iterator.h"
#include "gn/ninja_target_command_util.h"
@@ -28,9 +29,7 @@
// Current structure of rust-project.json output file
//
// {
-// "roots": [
-// "some/source/root" // each crate's source root
-// ],
+// "sysroot": "path/to/rust/sysroot",
// "crates": [
// {
// "deps": [
@@ -39,7 +38,14 @@
// "name": "alloc" // extern name of dependency
// },
// ],
-// "edition": "2018", // edition of crate
+// "source": [
+// "include_dirs": [
+// "some/source/root",
+// "some/gen/dir",
+// ],
+// "exclude_dirs": []
+// },
+// "edition": "2021", // edition of crate
// "cfg": [
// "unix", // "atomic" value config options
// "rust_panic=\"abort\""", // key="value" config options
@@ -156,99 +162,9 @@ std::vector<std::string> FindAllArgValuesAfterPrefix(
return values;
}
-// TODO(bwb) Parse sysroot structure from toml files. This is fragile and
-// might break if upstream changes the dependency structure.
-const std::string_view sysroot_crates[] = {"std",
- "core",
- "alloc",
- "panic_unwind",
- "proc_macro",
- "test",
- "panic_abort",
- "unwind"};
-
-// Multiple sysroot crates have dependenices on each other. This provides a
-// mechanism for specifying that in an extendible manner.
-const std::unordered_map<std::string_view, std::vector<std::string_view>>
- sysroot_deps_map = {{"alloc", {"core"}},
- {"std", {"alloc", "core", "panic_abort", "unwind"}}};
-
-// Add each of the crates a sysroot has, including their dependencies.
-void AddSysrootCrate(const BuildSettings* build_settings,
- std::string_view crate,
- std::string_view current_sysroot,
- SysrootCrateIndexMap& sysroot_crate_lookup,
- CrateList& crate_list) {
- if (sysroot_crate_lookup.find(crate) != sysroot_crate_lookup.end()) {
- // If this sysroot crate is already in the lookup, we don't add it again.
- return;
- }
-
- // Add any crates that this sysroot crate depends on.
- auto deps_lookup = sysroot_deps_map.find(crate);
- if (deps_lookup != sysroot_deps_map.end()) {
- auto deps = (*deps_lookup).second;
- for (auto dep : deps) {
- AddSysrootCrate(build_settings, dep, current_sysroot,
- sysroot_crate_lookup, crate_list);
- }
- }
-
- size_t crate_index = crate_list.size();
- sysroot_crate_lookup.insert(std::make_pair(crate, crate_index));
-
- base::FilePath rebased_out_dir =
- build_settings->GetFullPath(build_settings->build_dir());
- auto crate_path =
- FilePathToUTF8(rebased_out_dir) + std::string(current_sysroot) +
- "/lib/rustlib/src/rust/library/" + std::string(crate) + "/src/lib.rs";
-
- Crate sysroot_crate =
- Crate(SourceFile(crate_path), crate_index, std::string(crate), "2018");
-
- sysroot_crate.AddConfigItem("debug_assertions");
-
- if (deps_lookup != sysroot_deps_map.end()) {
- auto deps = (*deps_lookup).second;
- for (auto dep : deps) {
- auto idx = sysroot_crate_lookup[dep];
- sysroot_crate.AddDependency(idx, std::string(dep));
- }
- }
-
- crate_list.push_back(sysroot_crate);
-}
-
-// Add the given sysroot to the project, if it hasn't already been added.
-void AddSysroot(const BuildSettings* build_settings,
- std::string_view sysroot,
- SysrootIndexMap& sysroot_lookup,
- CrateList& crate_list) {
- // If this sysroot is already in the lookup, we don't add it again.
- if (sysroot_lookup.find(sysroot) != sysroot_lookup.end()) {
- return;
- }
-
- // Otherwise, add all of its crates
- for (auto crate : sysroot_crates) {
- AddSysrootCrate(build_settings, crate, sysroot, sysroot_lookup[sysroot],
- crate_list);
- }
-}
-
-void AddSysrootDependencyToCrate(Crate* crate,
- const SysrootCrateIndexMap& sysroot,
- std::string_view crate_name) {
- if (const auto crate_idx = sysroot.find(crate_name);
- crate_idx != sysroot.end()) {
- crate->AddDependency(crate_idx->second, std::string(crate_name));
- }
-}
-
void AddTarget(const BuildSettings* build_settings,
const Target* target,
TargetIndexMap& lookup,
- SysrootIndexMap& sysroot_lookup,
CrateList& crate_list) {
if (lookup.find(target) != lookup.end()) {
// If target is already in the lookup, we don't add it again.
@@ -257,21 +173,11 @@ void AddTarget(const BuildSettings* build_settings,
auto compiler_args = ExtractCompilerArgs(target);
auto compiler_target = FindArgValue("--target", compiler_args);
-
- // Check what sysroot this target needs. Add it to the crate list if it
- // hasn't already been added.
- auto rust_tool =
- target->toolchain()->GetToolForTargetFinalOutputAsRust(target);
- auto current_sysroot = rust_tool->GetSysroot();
- if (current_sysroot != "" && sysroot_lookup.count(current_sysroot) == 0) {
- AddSysroot(build_settings, current_sysroot, sysroot_lookup, crate_list);
- }
-
auto crate_deps = GetRustDeps(target);
// Add all dependencies of this crate, before this crate.
for (const auto& dep : crate_deps) {
- AddTarget(build_settings, dep, lookup, sysroot_lookup, crate_list);
+ AddTarget(build_settings, dep, lookup, crate_list);
}
// The index of a crate is its position (0-based) in the list of crates.
@@ -289,8 +195,10 @@ void AddTarget(const BuildSettings* build_settings,
edition = FindArgValue("--edition", compiler_args);
}
- Crate crate =
- Crate(crate_root, crate_id, crate_label, edition.value_or("2015"));
+ auto gen_dir = GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN);
+
+ Crate crate = Crate(crate_root, gen_dir, crate_id, crate_label,
+ edition.value_or("2015"));
crate.SetCompilerArgs(compiler_args);
if (compiler_target.has_value())
@@ -306,17 +214,25 @@ void AddTarget(const BuildSettings* build_settings,
crate.AddConfigItem(cfg);
}
- // Add the sysroot dependencies, if there is one.
- if (current_sysroot != "") {
- const auto& sysroot = sysroot_lookup[current_sysroot];
- AddSysrootDependencyToCrate(&crate, sysroot, "core");
- AddSysrootDependencyToCrate(&crate, sysroot, "alloc");
- AddSysrootDependencyToCrate(&crate, sysroot, "std");
-
- // Proc macros have the proc_macro crate as a direct dependency
- if (std::string_view(rust_tool->name()) ==
- std::string_view(RustTool::kRsToolMacro)) {
- AddSysrootDependencyToCrate(&crate, sysroot, "proc_macro");
+ // If it's a proc macro, record its output location so IDEs can invoke it.
+ auto rust_tool =
+ target->toolchain()->GetToolForTargetFinalOutputAsRust(target);
+ if (std::string_view(rust_tool->name()) ==
+ std::string_view(RustTool::kRsToolMacro)) {
+ auto outputs = target->computed_outputs();
+ if (outputs.size() > 0) {
+ crate.SetIsProcMacro(outputs[0]);
+ }
+ }
+
+ // Note any environment variables. These may be used by proc macros
+ // invoked by the current crate (so we want to record these for all crates,
+ // not just proc macro crates)
+ for (const auto& env_var : target->config_values().rustenv()) {
+ std::vector<std::string> parts = base::SplitString(
+ env_var, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+ if (parts.size() >= 2) {
+ crate.AddRustenv(parts[0], parts[1]);
}
}
@@ -331,25 +247,21 @@ void AddTarget(const BuildSettings* build_settings,
void WriteCrates(const BuildSettings* build_settings,
CrateList& crate_list,
+ std::optional<std::string>& sysroot,
std::ostream& rust_project) {
- // produce a de-duplicated set of source roots:
- std::set<std::string> roots;
- for (auto& crate : crate_list) {
- roots.insert(
- FilePathToUTF8(build_settings->GetFullPath(crate.root().GetDir())));
- }
-
rust_project << "{" NEWLINE;
- rust_project << " \"roots\": [";
- bool first_root = true;
- for (auto& root : roots) {
- if (!first_root)
- rust_project << ",";
- first_root = false;
- rust_project << NEWLINE " \"" << root << "\"";
+ // If a sysroot was found, then that can be used to tell rust-analyzer where
+ // to find the sysroot (and associated tools like the
+ // 'rust-analyzer-proc-macro-srv` proc-macro server that matches the abi used
+ // by 'rustc'
+ if (sysroot.has_value()) {
+ base::FilePath rebased_out_dir =
+ build_settings->GetFullPath(build_settings->build_dir());
+ auto sysroot_path = FilePathToUTF8(rebased_out_dir) + sysroot.value();
+ rust_project << " \"sysroot\": \"" << sysroot_path << "\"," NEWLINE;
}
- rust_project << NEWLINE " ]," NEWLINE;
+
rust_project << " \"crates\": [";
bool first_crate = true;
for (auto& crate : crate_list) {
@@ -363,7 +275,25 @@ void WriteCrates(const BuildSettings* build_settings,
rust_project << NEWLINE << " {" NEWLINE
<< " \"crate_id\": " << crate.index() << "," NEWLINE
<< " \"root_module\": \"" << crate_module << "\"," NEWLINE
- << " \"label\": \"" << crate.label() << "\"," NEWLINE;
+ << " \"label\": \"" << crate.label() << "\"," NEWLINE
+ << " \"source\": {" NEWLINE
+ << " \"include_dirs\": [" NEWLINE
+ << " \""
+ << FilePathToUTF8(
+ build_settings->GetFullPath(crate.root().GetDir()))
+ << "\"";
+ auto gen_dir = crate.gen_dir();
+ if (gen_dir.has_value()) {
+ auto gen_dir_path = FilePathToUTF8(
+ build_settings->GetFullPath(gen_dir->AsSourceDir(build_settings)));
+ rust_project << "," NEWLINE << " \"" << gen_dir_path
+ << "\"" NEWLINE;
+ } else {
+ rust_project << NEWLINE;
+ }
+ rust_project << " ]," NEWLINE
+ << " \"exclude_dirs\": []" NEWLINE
+ << " }," NEWLINE;
auto compiler_target = crate.CompilerTarget();
if (compiler_target.has_value()) {
@@ -404,6 +334,15 @@ void WriteCrates(const BuildSettings* build_settings,
rust_project << " \"edition\": \"" << crate.edition() << "\"," NEWLINE;
+ auto proc_macro_target = crate.proc_macro_path();
+ if (proc_macro_target.has_value()) {
+ rust_project << " \"is_proc_macro\": true," NEWLINE;
+ auto so_location = FilePathToUTF8(build_settings->GetFullPath(
+ proc_macro_target->AsSourceFile(build_settings)));
+ rust_project << " \"proc_macro_dylib_path\": \"" << so_location
+ << "\"," NEWLINE;
+ }
+
rust_project << " \"cfg\": [";
bool first_cfg = true;
for (const auto& cfg : crate.configs()) {
@@ -418,8 +357,29 @@ void WriteCrates(const BuildSettings* build_settings,
rust_project << " \"" << escaped_config << "\"";
}
rust_project << NEWLINE;
- rust_project << " ]" NEWLINE; // end cfgs
+ rust_project << " ]"; // end cfgs
+
+ if (!crate.rustenv().empty()) {
+ rust_project << "," NEWLINE;
+ rust_project << " \"env\": {";
+ bool first_env = true;
+ for (const auto& env : crate.rustenv()) {
+ if (!first_env)
+ rust_project << ",";
+ first_env = false;
+ std::string escaped_key, escaped_val;
+ base::EscapeJSONString(env.first, false, &escaped_key);
+ base::EscapeJSONString(env.second, false, &escaped_val);
+ rust_project << NEWLINE;
+ rust_project << " \"" << escaped_key << "\": \"" << escaped_val
+ << "\"";
+ }
+ rust_project << NEWLINE;
+ rust_project << " }" NEWLINE; // end env vars
+ } else {
+ rust_project << NEWLINE;
+ }
rust_project << " }"; // end crate
}
rust_project << NEWLINE " ]" NEWLINE; // end crate list
@@ -430,16 +390,25 @@ void RustProjectWriter::RenderJSON(const BuildSettings* build_settings,
std::vector<const Target*>& all_targets,
std::ostream& rust_project) {
TargetIndexMap lookup;
- SysrootIndexMap sysroot_lookup;
CrateList crate_list;
+ std::optional<std::string> rust_sysroot;
// All the crates defined in the project.
for (const auto* target : all_targets) {
if (!target->IsBinary() || !target->source_types_used().RustSourceUsed())
continue;
- AddTarget(build_settings, target, lookup, sysroot_lookup, crate_list);
+ AddTarget(build_settings, target, lookup, crate_list);
+
+ // If a sysroot hasn't been found, see if we can find one using this target.
+ if (!rust_sysroot.has_value()) {
+ auto rust_tool =
+ target->toolchain()->GetToolForTargetFinalOutputAsRust(target);
+ auto sysroot = rust_tool->GetSysroot();
+ if (sysroot != "")
+ rust_sysroot = sysroot;
+ }
}
- WriteCrates(build_settings, crate_list, rust_project);
+ WriteCrates(build_settings, crate_list, rust_sysroot, rust_project);
}
diff --git a/gn/src/gn/rust_project_writer_helpers.h b/gn/src/gn/rust_project_writer_helpers.h
index a63ded1f520..31efd403f03 100644
--- a/gn/src/gn/rust_project_writer_helpers.h
+++ b/gn/src/gn/rust_project_writer_helpers.h
@@ -14,6 +14,7 @@
#include <unordered_map>
#include <vector>
+#include "base/containers/flat_map.h"
#include "build_settings.h"
#include "gn/source_file.h"
#include "gn/target.h"
@@ -34,16 +35,26 @@ using DependencyList = std::vector<Dependency>;
class Crate {
public:
Crate(SourceFile root,
+ std::optional<OutputFile> gen_dir,
CrateIndex index,
std::string label,
std::string edition)
- : root_(root), index_(index), label_(label), edition_(edition) {}
+ : root_(root),
+ gen_dir_(gen_dir),
+ index_(index),
+ label_(label),
+ edition_(edition) {}
~Crate() = default;
// Add a config item to the crate.
void AddConfigItem(std::string cfg_item) { configs_.push_back(cfg_item); }
+ // Add a key-value environment variable pair used when building this crate.
+ void AddRustenv(std::string key, std::string value) {
+ rustenv_.emplace(key, value);
+ }
+
// Add another crate as a dependency of this one.
void AddDependency(CrateIndex index, std::string name) {
deps_.push_back(std::make_pair(index, name));
@@ -55,11 +66,19 @@ class Crate {
// Set the compiler target ("e.g. x86_64-linux-kernel")
void SetCompilerTarget(std::string target) { compiler_target_ = target; }
+ // Set that this is a proc macro with the path to the output .so/dylib/dll
+ void SetIsProcMacro(OutputFile proc_macro_dynamic_library) {
+ proc_macro_dynamic_library_ = proc_macro_dynamic_library;
+ }
+
// Returns the root file for the crate.
SourceFile& root() { return root_; }
+ // Returns the root file for the crate.
+ std::optional<OutputFile>& gen_dir() { return gen_dir_; }
+
// Returns the crate index.
- CrateIndex index() { return index_; };
+ CrateIndex index() { return index_; }
// Returns the displayable crate label.
const std::string& label() { return label_; }
@@ -81,8 +100,18 @@ class Crate {
return compiler_target_;
}
+ // Returns whether this crate builds a proc macro .so
+ const std::optional<OutputFile>& proc_macro_path() {
+ return proc_macro_dynamic_library_;
+ }
+
+ // Returns environment variables applied to this, which may be necessary
+ // for correct functioning of environment variables
+ const base::flat_map<std::string, std::string>& rustenv() { return rustenv_; }
+
private:
SourceFile root_;
+ std::optional<OutputFile> gen_dir_;
CrateIndex index_;
std::string label_;
std::string edition_;
@@ -90,29 +119,17 @@ class Crate {
DependencyList deps_;
std::optional<std::string> compiler_target_;
std::vector<std::string> compiler_args_;
+ std::optional<OutputFile> proc_macro_dynamic_library_;
+ base::flat_map<std::string, std::string> rustenv_;
};
using CrateList = std::vector<Crate>;
-// Mapping of a sysroot crate (path) to it's index in the crates list.
-using SysrootCrateIndexMap = std::unordered_map<std::string_view, CrateIndex>;
-
-// Mapping of a sysroot (path) to the mapping of each of the sysroot crates to
-// their index in the crates list.
-using SysrootIndexMap =
- std::unordered_map<std::string_view, SysrootCrateIndexMap>;
-
-// Add all of the crates for a sysroot (path) to the rust_project ostream.
-// Add the given sysroot to the project, if it hasn't already been added.
-void AddSysroot(const BuildSettings* build_settings,
- std::string_view sysroot,
- SysrootIndexMap& sysroot_lookup,
- CrateList& crate_list);
-
// Write the entire rust-project.json file contents into the given stream, based
// on the the given crates list.
void WriteCrates(const BuildSettings* build_settings,
CrateList& crate_list,
+ std::optional<std::string>& sysroot,
std::ostream& rust_project);
// Assemble the compiler arguments for the given GN Target.
diff --git a/gn/src/gn/rust_project_writer_helpers_unittest.cc b/gn/src/gn/rust_project_writer_helpers_unittest.cc
index 7ff3b487533..14d5c3547b0 100644
--- a/gn/src/gn/rust_project_writer_helpers_unittest.cc
+++ b/gn/src/gn/rust_project_writer_helpers_unittest.cc
@@ -26,11 +26,13 @@ using RustProjectWriterHelper = TestWithScheduler;
TEST_F(RustProjectWriterHelper, WriteCrates) {
TestWithScope setup;
+ std::optional<std::string> sysroot;
+
CrateList crates;
- Crate dep =
- Crate(SourceFile("/root/tortoise/lib.rs"), 0, "//tortoise:bar", "2015");
- Crate target =
- Crate(SourceFile("/root/hare/lib.rs"), 1, "//hare:bar", "2015");
+ Crate dep = Crate(SourceFile("/root/tortoise/lib.rs"), std::nullopt, 0,
+ "//tortoise:bar", "2015");
+ Crate target = Crate(SourceFile("/root/hare/lib.rs"),
+ OutputFile("gendir/hare/"), 1, "//hare:bar", "2015");
target.AddDependency(0, "tortoise");
target.AddConfigItem("unix");
target.AddConfigItem("feature=\"test\"");
@@ -39,22 +41,24 @@ TEST_F(RustProjectWriterHelper, WriteCrates) {
crates.push_back(target);
std::ostringstream stream;
- WriteCrates(setup.build_settings(), crates, stream);
+ WriteCrates(setup.build_settings(), crates, sysroot, stream);
std::string out = stream.str();
#if defined(OS_WIN)
base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"/root/hare/\",\n"
- " \"/root/tortoise/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"/root/tortoise/lib.rs\",\n"
" \"label\": \"//tortoise:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"/root/tortoise/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
@@ -65,6 +69,13 @@ TEST_F(RustProjectWriterHelper, WriteCrates) {
" \"crate_id\": 1,\n"
" \"root_module\": \"/root/hare/lib.rs\",\n"
" \"label\": \"//hare:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"/root/hare/\",\n"
+ " \"out/Debug/gendir/hare/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" {\n"
" \"crate\": 0,\n"
@@ -87,13 +98,11 @@ TEST_F(RustProjectWriterHelper, SysrootDepsAreCorrect) {
TestWithScope setup;
setup.build_settings()->SetRootPath(UTF8ToFilePath("/root"));
- SysrootIndexMap sysroot_lookup;
+ std::optional<std::string> sysroot = "sysroot";
CrateList crates;
- AddSysroot(setup.build_settings(), "sysroot", sysroot_lookup, crates);
-
std::ostringstream stream;
- WriteCrates(setup.build_settings(), crates, stream);
+ WriteCrates(setup.build_settings(), crates, sysroot, stream);
std::string out = stream.str();
#if defined(OS_WIN)
base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
@@ -101,125 +110,8 @@ TEST_F(RustProjectWriterHelper, SysrootDepsAreCorrect) {
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/alloc/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/core/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_abort/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_unwind/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/proc_macro/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/std/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/test/src/\",\n"
- " \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/unwind/src/\"\n"
- " ],\n"
+ " \"sysroot\": \"/root/out/Debug/sysroot\",\n"
" \"crates\": [\n"
- " {\n"
- " \"crate_id\": 0,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/core/src/lib.rs\",\n"
- " \"label\": \"core\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 1,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/alloc/src/lib.rs\",\n"
- " \"label\": \"alloc\",\n"
- " \"deps\": [\n"
- " {\n"
- " \"crate\": 0,\n"
- " \"name\": \"core\"\n"
- " }\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 2,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_abort/src/lib.rs\",\n"
- " \"label\": \"panic_abort\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 3,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/unwind/src/lib.rs\",\n"
- " \"label\": \"unwind\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 4,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/std/src/lib.rs\",\n"
- " \"label\": \"std\",\n"
- " \"deps\": [\n"
- " {\n"
- " \"crate\": 1,\n"
- " \"name\": \"alloc\"\n"
- " },\n"
- " {\n"
- " \"crate\": 0,\n"
- " \"name\": \"core\"\n"
- " },\n"
- " {\n"
- " \"crate\": 2,\n"
- " \"name\": \"panic_abort\"\n"
- " },\n"
- " {\n"
- " \"crate\": 3,\n"
- " \"name\": \"unwind\"\n"
- " }\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 5,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/panic_unwind/src/lib.rs\",\n"
- " \"label\": \"panic_unwind\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 6,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/proc_macro/src/lib.rs\",\n"
- " \"label\": \"proc_macro\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " },\n"
- " {\n"
- " \"crate_id\": 7,\n"
- " \"root_module\": \"/root/out/Debug/sysroot/lib/rustlib/src/rust/library/test/src/lib.rs\",\n"
- " \"label\": \"test\",\n"
- " \"deps\": [\n"
- " ],\n"
- " \"edition\": \"2018\",\n"
- " \"cfg\": [\n"
- " \"debug_assertions\"\n"
- " ]\n"
- " }\n"
" ]\n"
"}\n";
diff --git a/gn/src/gn/rust_project_writer_unittest.cc b/gn/src/gn/rust_project_writer_unittest.cc
index 675f9a6a387..81b24b0d690 100644
--- a/gn/src/gn/rust_project_writer_unittest.cc
+++ b/gn/src/gn/rust_project_writer_unittest.cc
@@ -51,14 +51,18 @@ TEST_F(RustProjectJSONWriter, OneRustTarget) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"path/foo/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"path/foo/lib.rs\",\n"
" \"label\": \"//foo:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"path/foo/\",\n"
+ " \"path/out/Debug/gen/foo/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n"
" \"deps\": [\n"
" ],\n"
@@ -88,6 +92,7 @@ TEST_F(RustProjectJSONWriter, RustTargetDep) {
dep.rust_values().set_crate_root(tlib);
dep.rust_values().crate_name() = "tortoise";
dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
target.set_output_type(Target::RUST_LIBRARY);
@@ -111,15 +116,18 @@ TEST_F(RustProjectJSONWriter, RustTargetDep) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"hare/\",\n"
- " \"tortoise/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"tortoise/lib.rs\",\n"
" \"label\": \"//tortoise:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"tortoise/\",\n"
+ " \"out/Debug/gen/tortoise/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
@@ -132,6 +140,13 @@ TEST_F(RustProjectJSONWriter, RustTargetDep) {
" \"crate_id\": 1,\n"
" \"root_module\": \"hare/lib.rs\",\n"
" \"label\": \"//hare:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"hare/\",\n"
+ " \"out/Debug/gen/hare/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" {\n"
" \"crate\": 0,\n"
@@ -163,6 +178,7 @@ TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
dep.rust_values().set_crate_root(tlib);
dep.rust_values().crate_name() = "tortoise";
dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
Target dep2(setup.settings(), Label(SourceDir("//achilles/"), "bar"));
dep2.set_output_type(Target::RUST_LIBRARY);
@@ -173,6 +189,7 @@ TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
dep2.rust_values().set_crate_root(alib);
dep2.rust_values().crate_name() = "achilles";
dep2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep2.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
target.set_output_type(Target::RUST_LIBRARY);
@@ -197,16 +214,18 @@ TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"achilles/\",\n"
- " \"hare/\",\n"
- " \"tortoise/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"tortoise/lib.rs\",\n"
" \"label\": \"//tortoise:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"tortoise/\",\n"
+ " \"out/Debug/gen/tortoise/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
@@ -219,6 +238,13 @@ TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
" \"crate_id\": 1,\n"
" \"root_module\": \"achilles/lib.rs\",\n"
" \"label\": \"//achilles:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"achilles/\",\n"
+ " \"out/Debug/gen/achilles/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
@@ -231,6 +257,13 @@ TEST_F(RustProjectJSONWriter, RustTargetDepTwo) {
" \"crate_id\": 2,\n"
" \"root_module\": \"hare/lib.rs\",\n"
" \"label\": \"//hare:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"hare/\",\n"
+ " \"out/Debug/gen/hare/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" {\n"
" \"crate\": 0,\n"
@@ -267,18 +300,21 @@ TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
dep.rust_values().set_crate_root(tlib);
dep.rust_values().crate_name() = "tortoise";
dep.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep.OnResolved(&err));
Target dep2(setup.settings(), Label(SourceDir("//achilles/"), "bar"));
dep2.set_output_type(Target::STATIC_LIBRARY);
dep2.visibility().SetPublic();
SourceFile alib("//achilles/lib.o");
dep2.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep2.OnResolved(&err));
Target dep3(setup.settings(), Label(SourceDir("//achilles/"), "group"));
dep3.set_output_type(Target::GROUP);
dep3.visibility().SetPublic();
dep3.public_deps().push_back(LabelTargetPair(&dep));
dep3.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep3.OnResolved(&err));
Target dep4(setup.settings(), Label(SourceDir("//tortoise/"), "macro"));
dep4.set_output_type(Target::RUST_PROC_MACRO);
@@ -289,6 +325,7 @@ TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
dep4.rust_values().set_crate_root(tmlib);
dep4.rust_values().crate_name() = "tortoise_macro";
dep4.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(dep4.OnResolved(&err));
Target target(setup.settings(), Label(SourceDir("//hare/"), "bar"));
target.set_output_type(Target::RUST_LIBRARY);
@@ -314,16 +351,18 @@ TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"hare/\",\n"
- " \"tortoise/\",\n"
- " \"tortoise/macro/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"tortoise/lib.rs\",\n"
" \"label\": \"//tortoise:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"tortoise/\",\n"
+ " \"out/Debug/gen/tortoise/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
@@ -336,9 +375,19 @@ TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
" \"crate_id\": 1,\n"
" \"root_module\": \"tortoise/macro/lib.rs\",\n"
" \"label\": \"//tortoise:macro\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"tortoise/macro/\",\n"
+ " \"out/Debug/gen/tortoise/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" ],\n"
" \"edition\": \"2015\",\n"
+ " \"is_proc_macro\": true,\n"
+ " \"proc_macro_dylib_path\": "
+ "\"out/Debug/obj/tortoise/libmacro.so\",\n"
" \"cfg\": [\n"
" \"test\",\n"
" \"debug_assertions\"\n"
@@ -348,6 +397,13 @@ TEST_F(RustProjectJSONWriter, RustTargetGetDepRustOnly) {
" \"crate_id\": 2,\n"
" \"root_module\": \"hare/lib.rs\",\n"
" \"label\": \"//hare:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"hare/\",\n"
+ " \"out/Debug/gen/hare/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"deps\": [\n"
" {\n"
" \"crate\": 0,\n"
@@ -398,14 +454,18 @@ TEST_F(RustProjectJSONWriter, OneRustTargetWithRustcTargetSet) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"path/foo/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"path/foo/lib.rs\",\n"
" \"label\": \"//foo:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"path/foo/\",\n"
+ " \"path/out/Debug/gen/foo/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"target\": \"x86-64_unknown\",\n"
" \"compiler_args\": [\"--target\", \"x86-64_unknown\"],\n"
" \"deps\": [\n"
@@ -449,14 +509,18 @@ TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSet) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"path/foo/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"path/foo/lib.rs\",\n"
" \"label\": \"//foo:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"path/foo/\",\n"
+ " \"path/out/Debug/gen/foo/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"compiler_args\": [\"--edition=2018\"],\n"
" \"deps\": [\n"
" ],\n"
@@ -500,14 +564,18 @@ TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSetAlternate) {
#endif
const char expected_json[] =
"{\n"
- " \"roots\": [\n"
- " \"path/foo/\"\n"
- " ],\n"
" \"crates\": [\n"
" {\n"
" \"crate_id\": 0,\n"
" \"root_module\": \"path/foo/lib.rs\",\n"
" \"label\": \"//foo:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"path/foo/\",\n"
+ " \"path/out/Debug/gen/foo/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
" \"compiler_args\": [\"--edition\", \"2018\"],\n"
" \"deps\": [\n"
" ],\n"
@@ -521,4 +589,67 @@ TEST_F(RustProjectJSONWriter, OneRustTargetWithEditionSetAlternate) {
"}\n";
ExpectEqOrShowDiff(expected_json, out);
-} \ No newline at end of file
+}
+
+TEST_F(RustProjectJSONWriter, OneRustProcMacroTarget) {
+ Err err;
+ TestWithScope setup;
+ setup.build_settings()->SetRootPath(UTF8ToFilePath("path"));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::RUST_PROC_MACRO);
+ target.visibility().SetPublic();
+ SourceFile lib("//foo/lib.rs");
+ target.sources().push_back(lib);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.rust_values().set_crate_root(lib);
+ target.config_values().rustenv().push_back("TEST_ENV_VAR=baz");
+ target.config_values().rustenv().push_back("TEST_ENV_VAR2=baz2");
+ target.rust_values().crate_name() = "foo";
+ target.config_values().rustflags().push_back("--cfg=feature=\"foo_enabled\"");
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream stream;
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ RustProjectWriter::RenderJSON(setup.build_settings(), targets, stream);
+ std::string out = stream.str();
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"crates\": [\n"
+ " {\n"
+ " \"crate_id\": 0,\n"
+ " \"root_module\": \"path/foo/lib.rs\",\n"
+ " \"label\": \"//foo:bar\",\n"
+ " \"source\": {\n"
+ " \"include_dirs\": [\n"
+ " \"path/foo/\",\n"
+ " \"path/out/Debug/gen/foo/\"\n"
+ " ],\n"
+ " \"exclude_dirs\": []\n"
+ " },\n"
+ " \"compiler_args\": [\"--cfg=feature=\\\"foo_enabled\\\"\"],\n"
+ " \"deps\": [\n"
+ " ],\n"
+ " \"edition\": \"2015\",\n"
+ " \"is_proc_macro\": true,\n"
+ " \"proc_macro_dylib_path\": \"path/out/Debug/obj/foo/libbar.so\",\n"
+ " \"cfg\": [\n"
+ " \"test\",\n"
+ " \"debug_assertions\",\n"
+ " \"feature=\\\"foo_enabled\\\"\"\n"
+ " ],\n"
+ " \"env\": {\n"
+ " \"TEST_ENV_VAR\": \"baz\",\n"
+ " \"TEST_ENV_VAR2\": \"baz2\"\n"
+ " }\n"
+ " }\n"
+ " ]\n"
+ "}\n";
+
+ ExpectEqOrShowDiff(expected_json, out);
+}
diff --git a/gn/src/gn/rust_substitution_type.cc b/gn/src/gn/rust_substitution_type.cc
index 69e6cf6a1db..5423d0dc7bf 100644
--- a/gn/src/gn/rust_substitution_type.cc
+++ b/gn/src/gn/rust_substitution_type.cc
@@ -42,6 +42,12 @@ bool IsValidRustSubstitution(const Substitution* type) {
type == &kRustSubstitutionSources;
}
+// Rust substitution types which we also make available to action targets.
+bool IsValidRustScriptArgsSubstitution(const Substitution* type) {
+ return type == &kRustSubstitutionRustEnv ||
+ type == &kRustSubstitutionRustFlags;
+}
+
bool IsValidRustLinkerSubstitution(const Substitution* type) {
return IsValidRustSubstitution(type) ||
type == &CSubstitutionLdFlags;
diff --git a/gn/src/gn/rust_substitution_type.h b/gn/src/gn/rust_substitution_type.h
index 36ba9538436..5a6ac6cece5 100644
--- a/gn/src/gn/rust_substitution_type.h
+++ b/gn/src/gn/rust_substitution_type.h
@@ -23,6 +23,7 @@ extern const Substitution kRustSubstitutionRustFlags;
extern const Substitution kRustSubstitutionSources;
bool IsValidRustSubstitution(const Substitution* type);
+bool IsValidRustScriptArgsSubstitution(const Substitution* type);
bool IsValidRustLinkerSubstitution(const Substitution* type);
#endif // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/gn/src/gn/scope.cc b/gn/src/gn/scope.cc
index 05b09957ca9..d07426ad19b 100644
--- a/gn/src/gn/scope.cc
+++ b/gn/src/gn/scope.cc
@@ -39,6 +39,13 @@ Scope::ProgrammaticProvider::~ProgrammaticProvider() {
scope_->RemoveProvider(this);
}
+std::string Scope::TemplateInvocationEntry::Describe() const {
+ std::string ret = template_name;
+ ret += "(\"" + target_name + "\") ";
+ ret += location.Describe(false);
+ return ret;
+}
+
Scope::Scope(const Settings* settings)
: const_containing_(nullptr),
mutable_containing_(nullptr),
@@ -252,6 +259,17 @@ bool Scope::CheckForUnusedVars(Err* err) const {
"You set the variable \"" + std::string(pair.first) +
"\" here and it was unused before it went\nout of scope.";
+
+ // Gather the template invocations that led up to this scope.
+ auto entries = GetTemplateInvocationEntries();
+ if (entries.size() != 0) {
+
+ help.append("\n\nVia these template invocations:\n");
+ for (const auto& entry : entries) {
+ help.append(" " + entry.Describe() + "\n");
+ }
+ }
+
const BinaryOpNode* binary = pair.second.value.origin()->AsBinaryOp();
if (binary && binary->op().type() == Token::EQUAL) {
// Make a nicer error message for normal var sets.
@@ -557,6 +575,41 @@ void Scope::RemoveProvider(ProgrammaticProvider* p) {
programmatic_providers_.erase(p);
}
+void Scope::SetTemplateInvocationEntry(std::string template_name,
+ std::string target_name,
+ Location location) {
+ template_invocation_entry_ = std::make_unique<TemplateInvocationEntry>(
+ TemplateInvocationEntry{std::move(template_name), std::move(target_name),
+ std::move(location)});
+}
+
+const Scope::TemplateInvocationEntry* Scope::FindTemplateInvocationEntry() const {
+ if (template_invocation_entry_)
+ return template_invocation_entry_.get();
+ if (const Scope* scope = containing())
+ return scope->FindTemplateInvocationEntry();
+ return nullptr;
+}
+
+void Scope::AppendTemplateInvocationEntries(
+ std::vector<TemplateInvocationEntry>* out) const {
+
+ const Value* invoker = GetValue("invoker");
+ if (invoker && invoker->type() == Value::SCOPE)
+ invoker->scope_value()->AppendTemplateInvocationEntries(out);
+
+ const TemplateInvocationEntry* entry = FindTemplateInvocationEntry();
+ if (entry)
+ out->push_back(*entry);
+}
+
+std::vector<Scope::TemplateInvocationEntry>
+Scope::GetTemplateInvocationEntries() const {
+ std::vector<Scope::TemplateInvocationEntry> result;
+ AppendTemplateInvocationEntries(&result);
+ return result;
+}
+
// static
bool Scope::RecordMapValuesEqual(const RecordMap& a, const RecordMap& b) {
if (a.size() != b.size())
diff --git a/gn/src/gn/scope.h b/gn/src/gn/scope.h
index 5412a41ace4..eb38202a723 100644
--- a/gn/src/gn/scope.h
+++ b/gn/src/gn/scope.h
@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include <set>
+#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
@@ -15,6 +16,7 @@
#include "base/memory/ref_counted.h"
#include "gn/err.h"
+#include "gn/location.h"
#include "gn/pattern.h"
#include "gn/source_dir.h"
#include "gn/source_file.h"
@@ -94,6 +96,21 @@ class Scope {
std::set<std::string> excluded_values;
};
+ // Details about a Scope's creation as a template invocation
+ struct TemplateInvocationEntry {
+ // Produce a printable string that describes the template invocation:
+ //
+ // 'template_name("target_name") //some/BUILD.gn:<line>'
+ //
+ // The target name is only the string that's passed to the template as the
+ // name, it's not a complete GN label.
+ std::string Describe() const;
+
+ std::string template_name;
+ std::string target_name;
+ Location location;
+ };
+
// Creates an empty toplevel scope.
explicit Scope(const Settings* settings);
@@ -319,6 +336,15 @@ class Scope {
void SetProperty(const void* key, void* value);
void* GetProperty(const void* key, const Scope** found_on_scope) const;
+ // Track template invocations for printing or debugging.
+ void SetTemplateInvocationEntry(std::string template_name,
+ std::string target_name,
+ Location location);
+
+ // Return a vector containing the current stack of template invocations that
+ // lead up to this scope.
+ std::vector<TemplateInvocationEntry> GetTemplateInvocationEntries() const;
+
private:
friend class ProgrammaticProvider;
@@ -339,6 +365,13 @@ class Scope {
// of the values may be different).
static bool RecordMapValuesEqual(const RecordMap& a, const RecordMap& b);
+ // Walk up the containing scopes and any "invoker" Value scopes to gather any
+ // previous template invocations.
+ void AppendTemplateInvocationEntries(std::vector<TemplateInvocationEntry>* out) const;
+
+ // Walk up the containing scopes to find a TemplateInvocationEntry.
+ const TemplateInvocationEntry* FindTemplateInvocationEntry() const;
+
// Scopes can have no containing scope (both null), a mutable containing
// scope, or a const containing scope. The reason is that when we're doing
// a new target, we want to refer to the base_config scope which will be read
@@ -356,6 +389,9 @@ class Scope {
RecordMap values_;
+ // If this is a template scope, track the template invocation.
+ std::unique_ptr<TemplateInvocationEntry> template_invocation_entry_;
+
// Note that this can't use string pieces since the names are constructed from
// Values which might be deallocated before this goes out of scope.
using NamedScopeMap = std::unordered_map<std::string, std::unique_ptr<Scope>>;
diff --git a/gn/src/gn/settings.cc b/gn/src/gn/settings.cc
index 154e0af5eb2..c9a36c959ac 100644
--- a/gn/src/gn/settings.cc
+++ b/gn/src/gn/settings.cc
@@ -27,3 +27,12 @@ Settings::Settings(const BuildSettings* build_settings,
if (!toolchain_output_dir_.is_null())
toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
}
+
+bool Settings::ShouldShowToolchain(
+ std::initializer_list<const Label*> labels) const {
+ for (auto label : labels) {
+ if (label->GetToolchainLabel() != default_toolchain_label_)
+ return true;
+ }
+ return false;
+}
diff --git a/gn/src/gn/settings.h b/gn/src/gn/settings.h
index 9b83b60fe8a..79d19049da6 100644
--- a/gn/src/gn/settings.h
+++ b/gn/src/gn/settings.h
@@ -86,6 +86,11 @@ class Settings {
greedy_target_generation_ = gtg;
}
+ // Returns true if any Label in the list provided is not in the default
+ // toolchain, and therefore any error message that involves this list of
+ // Labels should print the toolchain for each Label.
+ bool ShouldShowToolchain(std::initializer_list<const Label*> labels) const;
+
private:
const BuildSettings* build_settings_;
diff --git a/gn/src/gn/setup.cc b/gn/src/gn/setup.cc
index b6fdee2b4d9..139a919f44e 100644
--- a/gn/src/gn/setup.cc
+++ b/gn/src/gn/setup.cc
@@ -23,6 +23,7 @@
#include "gn/exec_process.h"
#include "gn/filesystem_utils.h"
#include "gn/input_file.h"
+#include "gn/label_pattern.h"
#include "gn/parse_tree.h"
#include "gn/parser.h"
#include "gn/source_dir.h"
@@ -116,6 +117,28 @@ Variables
"//build/my_config.gni",
]
+ export_compile_commands [optional]
+ A list of label patterns for which to generate a Clang compilation
+ database (see "gn help label_pattern" for the string format).
+
+ When specified, GN will generate a compile_commands.json file in the root
+ of the build directory containing information on how to compile each
+ source file reachable from any label matching any pattern in the list.
+ This is used for Clang-based tooling and some editor integration. See
+ https://clang.llvm.org/docs/JSONCompilationDatabase.html
+
+ The switch --add-export-compile-commands to "gn gen" (see "gn help gen")
+ appends to this value which provides a per-user way to customize it.
+
+ The deprecated switch --export-compile-commands to "gn gen" (see "gn help
+ gen") adds to the export target list using a different format.
+
+ Example:
+ export_compile_commands = [
+ "//base/*",
+ "//tools:doom_melon",
+ ]
+
root [optional]
Label of the root build target. The GN build will start by loading the
build file containing this target name. This defaults to "//:" which will
@@ -126,12 +149,14 @@ Variables
help --root-target").
script_executable [optional]
- Path to specific Python executable or other interpreter to use in
- action targets and exec_script calls. By default GN searches the
- PATH for Python to execute these scripts.
+ By default, GN runs the scripts used in action targets and exec_script
+ calls using the Python interpreter found in PATH. This value specifies the
+ Python executable or other interpreter to use instead.
- If set to the empty string, the path specified in action targets
- and exec_script calls will be executed directly.
+ If set to the empty string, the scripts will be executed directly.
+
+ The command-line switch --script-executable will override this value (see
+ "gn help --script-executable")
secondary_source [optional]
Label of an alternate directory tree to find input files. When searching
@@ -507,7 +532,7 @@ bool Setup::FillArguments(const base::CommandLine& cmdline, Err* err) {
base::FilePath build_arg_file =
build_settings_.GetFullPath(GetBuildArgFile());
- auto switch_value = cmdline.GetSwitchValueASCII(switches::kArgs);
+ auto switch_value = cmdline.GetSwitchValueString(switches::kArgs);
if (cmdline.HasSwitch(switches::kArgs) ||
(gen_empty_args_ && !PathExists(build_arg_file))) {
if (!FillArgsFromCommandLine(
@@ -781,8 +806,19 @@ bool Setup::FillPythonPath(const base::CommandLine& cmdline, Err* err) {
if (!value->VerifyTypeIs(Value::STRING, err)) {
return false;
}
- build_settings_.set_python_path(
- ProcessFileExtensions(UTF8ToFilePath(value->string_value())));
+ // Note that an empty string value is valid, and means that the scripts
+ // invoked by actions will be run directly.
+ base::FilePath python_path;
+ if (!value->string_value().empty()) {
+ python_path =
+ ProcessFileExtensions(UTF8ToFilePath(value->string_value()));
+ if (python_path.empty()) {
+ *err = Err(Location(), "Could not find \"" + value->string_value() +
+ "\" from dotfile in PATH.");
+ return false;
+ }
+ }
+ build_settings_.set_python_path(python_path);
} else {
#if defined(OS_WIN)
base::FilePath python_path =
@@ -893,7 +929,7 @@ bool Setup::FillOtherConfig(const base::CommandLine& cmdline, Err* err) {
// Root build file.
if (cmdline.HasSwitch(switches::kRootTarget)) {
- auto switch_value = cmdline.GetSwitchValueASCII(switches::kRootTarget);
+ auto switch_value = cmdline.GetSwitchValueString(switches::kRootTarget);
Value root_value(nullptr, switch_value);
root_target_label = Label::Resolve(current_dir, std::string_view(), Label(),
root_value, err);
@@ -1024,5 +1060,43 @@ bool Setup::FillOtherConfig(const base::CommandLine& cmdline, Err* err) {
build_settings_.set_arg_file_template_path(path);
}
+ // No stamp files.
+ const Value* no_stamp_files_value =
+ dotfile_scope_.GetValue("no_stamp_files", true);
+ if (no_stamp_files_value) {
+ if (!no_stamp_files_value->VerifyTypeIs(Value::BOOLEAN, err)) {
+ return false;
+ }
+ build_settings_.set_no_stamp_files(no_stamp_files_value->boolean_value());
+ CHECK(!build_settings_.no_stamp_files())
+ << "no_stamp_files does not work yet!";
+ }
+
+ // Export compile commands.
+ const Value* export_cc_value =
+ dotfile_scope_.GetValue("export_compile_commands", true);
+ if (export_cc_value) {
+ if (!ExtractListOfLabelPatterns(&build_settings_, *export_cc_value,
+ SourceDir("//"), &export_compile_commands_,
+ err)) {
+ return false;
+ }
+ }
+
+ // Append any additional export compile command patterns from the cmdline.
+ for (const std::string& cur :
+ cmdline.GetSwitchValueStrings(switches::kAddExportCompileCommands)) {
+ LabelPattern pat = LabelPattern::GetPattern(
+ SourceDir("//"), build_settings_.root_path_utf8(), Value(nullptr, cur),
+ err);
+ if (err->has_error()) {
+ err->AppendSubErr(Err(
+ Location(),
+ "for the command-line switch --add-export-compile-commands=" + cur));
+ return false;
+ }
+ export_compile_commands_.push_back(std::move(pat));
+ }
+
return true;
}
diff --git a/gn/src/gn/setup.h b/gn/src/gn/setup.h
index 2809a7d3db7..b3abb1be0d6 100644
--- a/gn/src/gn/setup.h
+++ b/gn/src/gn/setup.h
@@ -113,6 +113,12 @@ class Setup {
return no_check_patterns_.get();
}
+ // This is a combination of the export_compile_commands list in the dotfile,
+ // and any additions specified on the command-line.
+ const std::vector<LabelPattern>& export_compile_commands() const {
+ return export_compile_commands_;
+ }
+
BuildSettings& build_settings() { return build_settings_; }
Builder& builder() { return builder_; }
LoaderImpl* loader() { return loader_.get(); }
@@ -207,6 +213,8 @@ class Setup {
std::vector<Token> args_tokens_;
std::unique_ptr<ParseNode> args_root_;
+ std::vector<LabelPattern> export_compile_commands_;
+
Setup(const Setup&) = delete;
Setup& operator=(const Setup&) = delete;
};
diff --git a/gn/src/gn/setup_unittest.cc b/gn/src/gn/setup_unittest.cc
index 370f7226c88..b46bf946c95 100644
--- a/gn/src/gn/setup_unittest.cc
+++ b/gn/src/gn/setup_unittest.cc
@@ -31,7 +31,7 @@ TEST_F(SetupTest, DotGNFileIsGenDep) {
base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n");
WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
- cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+ cmdline.AppendSwitchPath(switches::kRoot, in_path);
// Create another temp dir for writing the generated files to.
base::ScopedTempDir build_temp_dir;
@@ -46,6 +46,69 @@ TEST_F(SetupTest, DotGNFileIsGenDep) {
EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name));
}
+TEST_F(SetupTest, EmptyScriptExecutableDoesNotGenerateError) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ const char kDotfileContents[] = R"(
+buildconfig = "//BUILDCONFIG.gn"
+script_executable = ""
+)";
+
+ // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+ // pass it as --root.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name, kDotfileContents);
+
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitchPath(switches::kRoot, in_path);
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that the .gn file is in the scheduler's gen deps.
+ Setup setup;
+ Err err;
+ EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
+ true, cmdline, &err));
+}
+
+#if defined(OS_WIN)
+TEST_F(SetupTest, MissingScriptExeGeneratesSetupErrorOnWindows) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ const char kDotfileContents[] = R"(
+buildconfig = "//BUILDCONFIG.gn"
+script_executable = "this_does_not_exist"
+)";
+
+ // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file,
+ // pass it as --root.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name, kDotfileContents);
+
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitchPath(switches::kRoot, in_path);
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that the .gn file is in the scheduler's gen deps.
+ Setup setup;
+ Err err;
+ EXPECT_FALSE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
+ true, cmdline, &err));
+ EXPECT_TRUE(err.has_error());
+}
+#endif // defined(OS_WIN)
+
static void RunExtensionCheckTest(std::string extension,
bool success,
const std::string& expected_error_message) {
@@ -62,7 +125,7 @@ static void RunExtensionCheckTest(std::string extension,
build_file_extension = \"" +
extension + "\"");
WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
- cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path));
+ cmdline.AppendSwitchPath(switches::kRoot, in_path);
// Create another temp dir for writing the generated files to.
base::ScopedTempDir build_temp_dir;
@@ -85,9 +148,51 @@ TEST_F(SetupTest, NoSeparatorInExtension) {
#else
"Build file extension 'hello/world' cannot contain a path separator"
#endif
- );
+ );
}
TEST_F(SetupTest, Extension) {
RunExtensionCheckTest("yay", true, "");
}
+
+TEST_F(SetupTest, AddExportCompileCommands) {
+ base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
+
+ // Provide a project default export compile command list.
+ const char kDotfileContents[] = R"(
+buildconfig = "//BUILDCONFIG.gn"
+export_compile_commands = [ "//base/*" ]
+)";
+
+ // Create a temp directory containing the build.
+ base::ScopedTempDir in_temp_dir;
+ ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir());
+ base::FilePath in_path = in_temp_dir.GetPath();
+ base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn"));
+ WriteFile(dot_gn_name, kDotfileContents);
+
+ WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), "");
+ cmdline.AppendSwitch(switches::kRoot, FilePathToUTF8(in_path));
+
+ // Two additions to the compile commands list.
+ cmdline.AppendSwitch(switches::kAddExportCompileCommands,
+ "//tools:doom_melon");
+ cmdline.AppendSwitch(switches::kAddExportCompileCommands, "//src/gn:*");
+
+ // Create another temp dir for writing the generated files to.
+ base::ScopedTempDir build_temp_dir;
+ ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir());
+
+ // Run setup and check that the .gn file is in the scheduler's gen deps.
+ Setup setup;
+ Err err;
+ EXPECT_TRUE(setup.DoSetupWithErr(FilePathToUTF8(build_temp_dir.GetPath()),
+ true, cmdline, &err));
+
+ // The export compile commands should have three items.
+ const std::vector<LabelPattern>& export_cc = setup.export_compile_commands();
+ ASSERT_EQ(3u, export_cc.size());
+ EXPECT_EQ("//base/*", export_cc[0].Describe());
+ EXPECT_EQ("//tools:doom_melon", export_cc[1].Describe());
+ EXPECT_EQ("//src/gn:*", export_cc[2].Describe());
+}
diff --git a/gn/src/gn/string_atom.h b/gn/src/gn/string_atom.h
index c590e0b3c1c..51aa8d573a7 100644
--- a/gn/src/gn/string_atom.h
+++ b/gn/src/gn/string_atom.h
@@ -171,7 +171,7 @@ struct less<StringAtom> {
template <typename U>
bool operator()(const U& a, const StringAtom& b) const noexcept {
return a < b.str();
- };
+ }
};
template <>
diff --git a/gn/src/gn/substitution_list.cc b/gn/src/gn/substitution_list.cc
index 21311c665e2..aed66a060cd 100644
--- a/gn/src/gn/substitution_list.cc
+++ b/gn/src/gn/substitution_list.cc
@@ -49,13 +49,16 @@ bool SubstitutionList::Parse(const std::vector<std::string>& values,
SubstitutionList SubstitutionList::MakeForTest(const char* a,
const char* b,
- const char* c) {
+ const char* c,
+ const char* d) {
std::vector<std::string> input_strings;
input_strings.push_back(a);
if (b)
input_strings.push_back(b);
if (c)
input_strings.push_back(c);
+ if (d)
+ input_strings.push_back(d);
Err err;
SubstitutionList result;
diff --git a/gn/src/gn/substitution_list.h b/gn/src/gn/substitution_list.h
index 559b25d1747..0dbf206412d 100644
--- a/gn/src/gn/substitution_list.h
+++ b/gn/src/gn/substitution_list.h
@@ -27,7 +27,8 @@ class SubstitutionList {
// Makes a SubstitutionList from the given hardcoded patterns.
static SubstitutionList MakeForTest(const char* a,
const char* b = nullptr,
- const char* c = nullptr);
+ const char* c = nullptr,
+ const char* d = nullptr);
const std::vector<SubstitutionPattern>& list() const { return list_; }
diff --git a/gn/src/gn/substitution_type.cc b/gn/src/gn/substitution_type.cc
index 06c21b36ef1..723bf8a4469 100644
--- a/gn/src/gn/substitution_type.cc
+++ b/gn/src/gn/substitution_type.cc
@@ -163,7 +163,9 @@ bool IsValidSourceSubstitution(const Substitution* type) {
}
bool IsValidScriptArgsSubstitution(const Substitution* type) {
- return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName;
+ return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName ||
+ IsValidCompilerScriptArgsSubstitution(type) ||
+ IsValidRustScriptArgsSubstitution(type);
}
bool IsValidToolSubstitution(const Substitution* type) {
diff --git a/gn/src/gn/substitution_writer_unittest.cc b/gn/src/gn/substitution_writer_unittest.cc
index fc3c44692a7..eaa521ab9c3 100644
--- a/gn/src/gn/substitution_writer_unittest.cc
+++ b/gn/src/gn/substitution_writer_unittest.cc
@@ -45,6 +45,12 @@ TEST(SubstitutionWriter, ApplyPatternToSource) {
SourceFile result = SubstitutionWriter::ApplyPatternToSource(
nullptr, setup.settings(), pattern, SourceFile("//foo/bar/myfile.txt"));
ASSERT_EQ("//out/Debug/gen/foo/bar/myfile.tmp", result.value());
+
+ result = SubstitutionWriter::ApplyPatternToSource(
+ nullptr, setup.settings(), pattern,
+ SourceFile("//out/Debug/gen/generated_file.cc"));
+ ASSERT_EQ("//out/Debug/gen/BUILD_DIR/gen/generated_file.tmp", result.value())
+ << result.value();
}
TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {
diff --git a/gn/src/gn/swift_values.cc b/gn/src/gn/swift_values.cc
index a70c72f096d..3cfd37770c5 100644
--- a/gn/src/gn/swift_values.cc
+++ b/gn/src/gn/swift_values.cc
@@ -48,7 +48,7 @@ void SwiftValues::FillModuleDependencies(Target* target) {
pair.ptr->swift_values().public_modules().end());
}
- if (target->swift_values().builds_module())
+ if (target->builds_swift_module())
target->swift_values().public_modules_.push_back(target);
}
diff --git a/gn/src/gn/swift_values.h b/gn/src/gn/swift_values.h
index 3852366637a..91ec066ca1f 100644
--- a/gn/src/gn/swift_values.h
+++ b/gn/src/gn/swift_values.h
@@ -35,9 +35,6 @@ class SwiftValues {
std::string& module_name() { return module_name_; }
const std::string module_name() const { return module_name_; }
- // Returns whether the target generates a .swiftmodule.
- bool builds_module() const { return !module_output_file_.value().empty(); }
-
// Name of the generated .swiftmodule file. Computed when the target
// is resolved.
const OutputFile& module_output_file() const { return module_output_file_; }
diff --git a/gn/src/gn/switches.cc b/gn/src/gn/switches.cc
index 6caec3a9dd5..d86c349bd6b 100644
--- a/gn/src/gn/switches.cc
+++ b/gn/src/gn/switches.cc
@@ -119,8 +119,8 @@ const char kScriptExecutable_Help[] =
action targets and exec_script calls. By default GN searches the
PATH for Python to execute these scripts.
- If set to the empty string, the path specified in action targets
- and exec_script calls will be executed directly.
+ If set to the empty string, the path of scripts specified in action
+ targets and exec_script calls will be executed directly.
)";
const char kQuiet[] = "q";
@@ -153,8 +153,7 @@ Examples
)";
const char kRootTarget[] = "root-target";
-const char kRootTarget_HelpShort[] =
- "--root-target: Override the root target.";
+const char kRootTarget_HelpShort[] = "--root-target: Override the root target.";
const char kRootTarget_Help[] =
R"(--root-target: Override the root target.
@@ -284,6 +283,9 @@ const char kVersion_Help[] = "";
const char kDefaultToolchain[] = "default-toolchain";
const char kRegeneration[] = "regeneration";
+
+const char kAddExportCompileCommands[] = "add-export-compile-commands";
+
// -----------------------------------------------------------------------------
SwitchInfo::SwitchInfo() : short_help(""), long_help("") {}
diff --git a/gn/src/gn/switches.h b/gn/src/gn/switches.h
index 22099d3914b..3d1943fee57 100644
--- a/gn/src/gn/switches.h
+++ b/gn/src/gn/switches.h
@@ -114,6 +114,10 @@ extern const char kDefaultToolchain[];
// so it can be shared between command_gen and ninja_build_writer.
extern const char kRegeneration[];
+// This switch is read by Setup so needs to be in this global place, but is
+// relevant only by command_gen so is documented there.
+extern const char kAddExportCompileCommands[];
+
} // namespace switches
#endif // TOOLS_GN_SWITCHES_H_
diff --git a/gn/src/gn/target.cc b/gn/src/gn/target.cc
index 508ad7e11e6..2cebcf7b9b3 100644
--- a/gn/src/gn/target.cc
+++ b/gn/src/gn/target.cc
@@ -44,12 +44,16 @@ void MergeAllDependentConfigsFrom(const Target* from_target,
}
Err MakeTestOnlyError(const Item* from, const Item* to) {
+ bool with_toolchain = from->settings()->ShouldShowToolchain({
+ &from->label(),
+ &to->label(),
+ });
return Err(
from->defined_from(), "Test-only dependency not allowed.",
- from->label().GetUserVisibleName(false) +
+ from->label().GetUserVisibleName(with_toolchain) +
"\n"
"which is NOT marked testonly can't depend on\n" +
- to->label().GetUserVisibleName(false) +
+ to->label().GetUserVisibleName(with_toolchain) +
"\n"
"which is marked testonly. Only targets with \"testonly = true\"\n"
"can depend on other test-only targets.\n"
@@ -760,48 +764,45 @@ void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
dep->output_type() == RUST_LIBRARY ||
- dep->output_type() == RUST_PROC_MACRO ||
dep->output_type() == SOURCE_SET ||
(dep->output_type() == CREATE_BUNDLE &&
dep->bundle_data().is_framework())) {
inherited_libraries_.Append(dep, is_public);
}
+ // Collect Rust libraries that are accessible from the current target, or
+ // transitively part of the current target.
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
- dep->output_type() == RUST_LIBRARY) {
- rust_transitive_libs_.Append(dep, is_public);
-
- // Propagate public dependent libraries.
- for (const auto& transitive :
- dep->rust_transitive_libs_.GetOrderedAndPublicFlag()) {
- if (transitive.second) {
- rust_transitive_libs_.Append(transitive.first, is_public);
- }
- }
+ dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY ||
+ dep->output_type() == GROUP) {
+ // Here we have: `this` --[depends-on]--> `dep`
+ //
+ // The `this` target has direct access to `dep` since its a direct
+ // dependency, regardless of the edge being a public_dep or not, so we pass
+ // true for public-ness. Whereas, anything depending on `this` can only gain
+ // direct access to `dep` if the edge between `this` and `dep` is public, so
+ // we pass `is_public`.
+ //
+ // TODO(danakj): We should only need to track Rust rlibs or dylibs here, as
+ // it's used for passing to rustc with --extern. We currently track
+ // everything then drop non-Rust libs in ninja_rust_binary_target_writer.cc.
+ rust_transitive_inherited_libs_.Append(dep, true);
+ rust_transitive_inheritable_libs_.Append(dep, is_public);
+
+ rust_transitive_inherited_libs_.AppendInherited(
+ dep->rust_transitive_inheritable_libs(), true);
+ rust_transitive_inheritable_libs_.AppendInherited(
+ dep->rust_transitive_inheritable_libs(), is_public);
+ } else if (dep->output_type() == RUST_PROC_MACRO) {
+ // Proc-macros are inherited as a transitive dependency, but the things they
+ // depend on can't be used elsewhere, as the proc macro is not linked into
+ // the target (as it's only used during compilation).
+ rust_transitive_inherited_libs_.Append(dep, true);
+ rust_transitive_inheritable_libs_.Append(dep, is_public);
}
- // Rust libraries (those meant for consumption by another Rust target) are
- // handled the same way, whether static or dynamic.
- if (dep->output_type() == RUST_LIBRARY ||
- RustValues::InferredCrateType(dep) == RustValues::CRATE_DYLIB) {
- rust_transitive_libs_.AppendInherited(dep->rust_transitive_libs_,
- is_public);
-
- // If there is a transitive dependency that is not a rust library, place it
- // in the normal location
- for (const auto& inherited :
- rust_transitive_libs_.GetOrderedAndPublicFlag()) {
- if (!RustValues::IsRustLibrary(inherited.first)) {
- inherited_libraries_.Append(inherited.first, inherited.second);
- }
- }
- } else if (dep->output_type() == RUST_PROC_MACRO) {
- // We will need to specify the path to find a procedural macro,
- // but have no need to specify the paths to find its dependencies
- // as the procedural macro is now a complete .so.
- rust_transitive_libs_.Append(dep, is_public);
- } else if (dep->output_type() == SHARED_LIBRARY) {
+ if (dep->output_type() == SHARED_LIBRARY) {
// Shared library dependendencies are inherited across public shared
// library boundaries.
//
@@ -824,24 +825,37 @@ void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
// resolved by the compiler.
inherited_libraries_.AppendPublicSharedLibraries(dep->inherited_libraries(),
is_public);
- } else if (!dep->IsFinal()) {
- // The current target isn't linked, so propagate linked deps and
- // libraries up the dependency tree.
- inherited_libraries_.AppendInherited(dep->inherited_libraries(), is_public);
- rust_transitive_libs_.AppendInherited(dep->rust_transitive_libs_,
- is_public);
- } else if (dep->complete_static_lib()) {
- // Inherit only final targets through _complete_ static libraries.
- //
- // Inherited final libraries aren't linked into complete static libraries.
- // They are forwarded here so that targets that depend on complete
- // static libraries can link them in. Conversely, since complete static
- // libraries link in non-final targets they shouldn't be inherited.
- for (const auto& inherited :
- dep->inherited_libraries().GetOrderedAndPublicFlag()) {
- if (inherited.first->IsFinal()) {
- inherited_libraries_.Append(inherited.first,
- is_public && inherited.second);
+ } else {
+ InheritedLibraries transitive;
+
+ if (!dep->IsFinal()) {
+ // The current target isn't linked, so propagate linked deps and
+ // libraries up the dependency tree.
+ for (const auto& [inherited, inherited_is_public] :
+ dep->inherited_libraries().GetOrderedAndPublicFlag()) {
+ transitive.Append(inherited, is_public && inherited_is_public);
+ }
+ } else if (dep->complete_static_lib()) {
+ // Inherit only final targets through _complete_ static libraries.
+ //
+ // Inherited final libraries aren't linked into complete static libraries.
+ // They are forwarded here so that targets that depend on complete
+ // static libraries can link them in. Conversely, since complete static
+ // libraries link in non-final targets they shouldn't be inherited.
+ for (const auto& [inherited, inherited_is_public] :
+ dep->inherited_libraries().GetOrderedAndPublicFlag()) {
+ if (inherited->IsFinal()) {
+ transitive.Append(inherited, is_public && inherited_is_public);
+ }
+ }
+ }
+
+ for (const auto& [target, pub] : transitive.GetOrderedAndPublicFlag()) {
+ // Proc macros are not linked into targets that depend on them, so do not
+ // get inherited; they are consumed by the Rust compiler and only need to
+ // be specified in --extern.
+ if (target->output_type() != RUST_PROC_MACRO) {
+ inherited_libraries_.Append(target, pub);
}
}
}
@@ -879,7 +893,7 @@ void Target::PullRecursiveHardDeps() {
// by the current target).
if (pair.ptr->IsBinary() && !pair.ptr->all_headers_public() &&
pair.ptr->public_headers().empty() &&
- !pair.ptr->swift_values().builds_module()) {
+ !pair.ptr->builds_swift_module()) {
continue;
}
@@ -1072,19 +1086,24 @@ bool Target::ResolvePrecompiledHeaders(Err* err) {
// Already have a precompiled header values, the settings must match.
if (config_values_->precompiled_header() != cur.precompiled_header() ||
config_values_->precompiled_source() != cur.precompiled_source()) {
+ bool with_toolchain = settings()->ShouldShowToolchain({
+ &label(),
+ pch_header_settings_from,
+ &config->label(),
+ });
*err = Err(
defined_from(), "Precompiled header setting conflict.",
- "The target " + label().GetUserVisibleName(false) +
+ "The target " + label().GetUserVisibleName(with_toolchain) +
"\n"
"has conflicting precompiled header settings.\n"
"\n"
"From " +
- pch_header_settings_from->GetUserVisibleName(false) +
+ pch_header_settings_from->GetUserVisibleName(with_toolchain) +
"\n header: " + config_values_->precompiled_header() +
"\n source: " + config_values_->precompiled_source().value() +
"\n\n"
"From " +
- config->label().GetUserVisibleName(false) +
+ config->label().GetUserVisibleName(with_toolchain) +
"\n header: " + cur.precompiled_header() +
"\n source: " + cur.precompiled_source().value());
return false;
@@ -1121,7 +1140,7 @@ bool Target::CheckSourceSetLanguages(Err* err) const {
if (output_type() == Target::SOURCE_SET &&
source_types_used().RustSourceUsed()) {
*err = Err(defined_from(), "source_set contained Rust code.",
- label().GetUserVisibleName(false) +
+ label().GetUserVisibleName(!settings()->is_default()) +
" has Rust code. Only C/C++ source_sets are supported.");
return false;
}
@@ -1167,7 +1186,7 @@ bool Target::CheckAssertNoDeps(Err* err) const {
&failure_path_str, &failure_pattern)) {
*err = Err(
defined_from(), "assert_no_deps failed.",
- label().GetUserVisibleName(false) +
+ label().GetUserVisibleName(!settings()->is_default()) +
" has an assert_no_deps entry:\n " + failure_pattern->Describe() +
"\nwhich fails for the dependency path:\n" + failure_path_str);
return false;
diff --git a/gn/src/gn/target.h b/gn/src/gn/target.h
index d88f0a57a7a..c965d1bd500 100644
--- a/gn/src/gn/target.h
+++ b/gn/src/gn/target.h
@@ -239,7 +239,7 @@ class Target : public Item {
return output_type_ == ACTION || output_type_ == ACTION_FOREACH ||
output_type_ == COPY_FILES || output_type_ == CREATE_BUNDLE ||
output_type_ == BUNDLE_DATA || output_type_ == GENERATED_FILE ||
- (IsBinary() && has_swift_values() && swift_values().builds_module());
+ builds_swift_module();
}
// Returns the iterator range which can be used in range-based for loops
@@ -294,6 +294,10 @@ class Target : public Item {
return allow_circular_includes_from_;
}
+ // Pool option
+ const LabelPtrPair<Pool>& pool() const { return pool_; }
+ void set_pool(LabelPtrPair<Pool> pool) { pool_ = std::move(pool); }
+
const InheritedLibraries& inherited_libraries() const {
return inherited_libraries_;
}
@@ -311,14 +315,22 @@ class Target : public Item {
const SwiftValues& swift_values() const;
bool has_swift_values() const { return swift_values_.get(); }
+ // Return true if this targets builds a SwiftModule
+ bool builds_swift_module() const {
+ return IsBinary() && has_swift_values() &&
+ source_types_used().SwiftSourceUsed();
+ }
+
RustValues& rust_values();
const RustValues& rust_values() const;
bool has_rust_values() const { return rust_values_.get(); }
// Transitive closure of libraries that are depended on by this target
- InheritedLibraries& rust_transitive_libs() { return rust_transitive_libs_; }
- const InheritedLibraries& rust_transitive_libs() const {
- return rust_transitive_libs_;
+ const InheritedLibraries& rust_transitive_inherited_libs() const {
+ return rust_transitive_inherited_libs_;
+ }
+ const InheritedLibraries& rust_transitive_inheritable_libs() const {
+ return rust_transitive_inheritable_libs_;
}
const UniqueVector<SourceDir>& all_lib_dirs() const { return all_lib_dirs_; }
@@ -487,6 +499,8 @@ class Target : public Item {
std::set<Label> allow_circular_includes_from_;
+ LabelPtrPair<Pool> pool_;
+
// Static libraries, shared libraries, and source sets from transitive deps
// that need to be linked.
InheritedLibraries inherited_libraries_;
@@ -520,8 +534,18 @@ class Target : public Item {
// Used for Rust targets.
std::unique_ptr<RustValues> rust_values_;
- // Used by all targets, only useful to generate Rust targets though.
- InheritedLibraries rust_transitive_libs_;
+ // Used by all targets, only useful to generate Rust targets though. These
+ // present 2 different views of the public flags:
+ //
+ // Lists all transitive libraries, and for each one the public bit says if
+ // there is a public chain such that this target can make direct use of the
+ // lib. For each library marked public: "I have access to these targets."
+ InheritedLibraries rust_transitive_inherited_libs_;
+ // Lists all transitive libraries, and for each one the public bit says if a
+ // target depending on this target would inherit the libraries as public too.
+ // For each library marked public: "If you depend on me, you get access to
+ // these targets."
+ InheritedLibraries rust_transitive_inheritable_libs_;
// User for Swift targets.
std::unique_ptr<SwiftValues> swift_values_;
diff --git a/gn/src/gn/target_public_pair.h b/gn/src/gn/target_public_pair.h
new file mode 100644
index 00000000000..f211a81bf43
--- /dev/null
+++ b/gn/src/gn/target_public_pair.h
@@ -0,0 +1,131 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_TARGET_PUBLIC_PAIR_H_
+#define TOOLS_GN_TARGET_PUBLIC_PAIR_H_
+
+#include "gn/unique_vector.h"
+
+class Target;
+
+// C++ and Rust target resolution requires computing uniquified and
+// ordered lists of static/shared libraries that are collected through
+// the target's dependency tree.
+//
+// Maintaining the order is important to ensure the libraries are linked
+// in the correct order in the final link command line.
+//
+// Also each library must only appear once in the final list, even though
+// it may appear multiple times during the dependency tree walk, either as
+// a "private" or "public" dependency.
+//
+// The TargetPublicPair class below encodes a (target_ptr, is_public_flag)
+// pair, with convenience accessors and utility structs.
+//
+// The TargetPublicPairListBuilder is a builder-pattern class that generates
+// a unique vector of TargetPublicPair values (i.e. the final list described
+// above), and supporting the special logic required to build these lists
+// (see the comments for its Append() and AppendInherited() methods).
+//
+// A convenience encoding for a (target_ptr, is_public_flag) pair.
+class TargetPublicPair {
+ public:
+ TargetPublicPair() = default;
+ TargetPublicPair(const Target* target, bool is_public)
+ : target_(target), is_public_(is_public) {}
+ TargetPublicPair(std::pair<const Target*, bool> pair)
+ : target_(pair.first), is_public_(pair.second) {}
+
+ const Target* target() const { return target_; }
+ void set_target(const Target* target) { target_ = target; }
+
+ bool is_public() const { return is_public_; }
+ void set_is_public(bool is_public) { is_public_ = is_public; }
+
+ // Utility structs that can be used to instantiante containers
+ // that only use the target for lookups / comparisons. E.g.
+ //
+ // std::unordered_set<TargetPublicPair,
+ // TargetPublicPair::TargetHash,
+ // TargetPublicPair::TargetEqualTo>
+ //
+ // std::set<TargetPublicPair, TargetPublicPair::TargetLess>
+ //
+ struct TargetHash {
+ size_t operator()(TargetPublicPair p) const noexcept {
+ return std::hash<const Target*>()(p.target());
+ }
+ };
+
+ struct TargetEqualTo {
+ bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept {
+ return a.target() == b.target();
+ }
+ };
+
+ struct TargetLess {
+ bool operator()(TargetPublicPair a, TargetPublicPair b) const noexcept {
+ return a.target() < b.target();
+ }
+ };
+
+ private:
+ const Target* target_ = nullptr;
+ bool is_public_ = false;
+};
+
+// A helper type to build a uniquified ordered vector of TargetPublicPair
+// instances. Usage is:
+//
+// 1) Create builder instance.
+//
+// 2) Call Append() to add a direct dependency, or AppendInherited() to add
+// transitive ones, as many times as necessary.
+//
+// 3) Call Build() to retrieve final list as a vector.
+//
+class TargetPublicPairListBuilder
+ : public UniqueVector<TargetPublicPair,
+ TargetPublicPair::TargetHash,
+ TargetPublicPair::TargetEqualTo> {
+ public:
+ // Add (target, is_public) to the list being constructed. If the target
+ // was not already in the list, record the |is_public| flag as is,
+ // otherwise, set the recorded flag to true only if |is_public| is true, or
+ // don't do anything otherwise.
+ void Append(const Target* target, bool is_public) {
+ auto ret = EmplaceBackWithIndex(target, is_public);
+ if (!ret.first && is_public) {
+ // UniqueVector<T>::operator[]() always returns a const reference
+ // because the returned values are lookup keys in its set-like data
+ // structure (thus modifying them would break its internal consistency).
+ // However, because TargetHash and TargetEqualTo are being used to
+ // instantiate this template, only the target() part of the value must
+ // remain constant, and it is possible to modify the is_public() part
+ // in-place safely.
+ auto* pair = const_cast<TargetPublicPair*>(&(*this)[ret.second]);
+ pair->set_is_public(true);
+ }
+ }
+
+ // Append all pairs from any container with begin() and end() iterators
+ // that dereference to values that convert to a TargetPublicPair value.
+ // If |is_public| is false, the input pair will be appended with the
+ // value of the public flag to false.
+ template <
+ typename C,
+ typename = std::void_t<
+ decltype(static_cast<TargetPublicPair>(*std::declval<C>().begin())),
+ decltype(static_cast<TargetPublicPair>(*std::declval<C>().end()))>>
+ void AppendInherited(const C& other, bool is_public) {
+ for (const auto& pair : other) {
+ Append(pair.target(), is_public && pair.is_public());
+ }
+ }
+
+ // Build and return the final list to the caller.
+ std::vector<TargetPublicPair> Build() { return release(); }
+};
+
+#endif // TOOLS_GN_TARGET_PUBLIC_PAIR_H_
diff --git a/gn/src/gn/target_public_pair_unittest.cc b/gn/src/gn/target_public_pair_unittest.cc
new file mode 100644
index 00000000000..8b08a0c5b3c
--- /dev/null
+++ b/gn/src/gn/target_public_pair_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "gn/target_public_pair.h"
+#include "util/test/test.h"
+
+TEST(TargetPublicPairTest, ConstructionAndMutation) {
+ // Fake target pointer values.
+ const auto* a_target = reinterpret_cast<const Target*>(1000);
+ const auto* b_target = reinterpret_cast<const Target*>(2000);
+
+ TargetPublicPair a_pair(a_target, true);
+ EXPECT_EQ(a_target, a_pair.target());
+ EXPECT_TRUE(a_pair.is_public());
+
+ TargetPublicPair b_pair(b_target, false);
+ EXPECT_EQ(b_target, b_pair.target());
+ EXPECT_FALSE(b_pair.is_public());
+
+ a_pair.set_target(b_target);
+ EXPECT_EQ(b_target, a_pair.target());
+ EXPECT_TRUE(a_pair.is_public());
+
+ a_pair.set_is_public(false);
+ EXPECT_EQ(b_target, a_pair.target());
+ EXPECT_FALSE(a_pair.is_public());
+
+ a_pair = TargetPublicPair(a_target, true);
+ EXPECT_EQ(a_target, a_pair.target());
+ EXPECT_TRUE(a_pair.is_public());
+
+ b_pair = std::move(a_pair);
+ EXPECT_EQ(a_target, b_pair.target());
+ EXPECT_TRUE(b_pair.is_public());
+}
+
+TEST(TargetPublicPairTest, Builder) {
+ const auto* a_target = reinterpret_cast<const Target*>(1000);
+ const auto* b_target = reinterpret_cast<const Target*>(2000);
+ TargetPublicPairListBuilder builder;
+
+ builder.Append(a_target, false);
+ builder.Append(b_target, false);
+ builder.Append(a_target, true);
+ builder.Append(b_target, false);
+
+ auto list = builder.Build();
+ EXPECT_EQ(2u, list.size());
+ EXPECT_EQ(a_target, list[0].target());
+ EXPECT_EQ(b_target, list[1].target());
+ EXPECT_TRUE(list[0].is_public());
+ EXPECT_FALSE(list[1].is_public());
+}
diff --git a/gn/src/gn/target_unittest.cc b/gn/src/gn/target_unittest.cc
index b4ea699077b..005335d3a35 100644
--- a/gn/src/gn/target_unittest.cc
+++ b/gn/src/gn/target_unittest.cc
@@ -926,7 +926,7 @@ TEST_F(TargetTest, CheckStampFileName) {
&computed_outputs, &err));
ASSERT_EQ(1u, computed_outputs.size());
EXPECT_EQ("//out/Debug/obj/a/a.stamp", computed_outputs[0].value())
- << "was instead: " << computed_outputs[0].value();
+ << "was instead: " << computed_outputs[0].value();
}
// Tests Target::GetOutputFilesForSource for action_foreach targets (these, like
@@ -1219,13 +1219,16 @@ TEST_F(TargetTest, ResolvePrecompiledHeaders) {
TestWithScope setup;
Err err;
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar",
+ SourceDir("//toolchain/"), "default"));
// Target with no settings, no configs, should be a no-op.
EXPECT_TRUE(target.ResolvePrecompiledHeaders(&err));
// Config with PCH values.
- Config config_1(setup.settings(), Label(SourceDir("//foo/"), "c1"));
+ Config config_1(
+ setup.settings(),
+ Label(SourceDir("//foo/"), "c1", SourceDir("//toolchain/"), "default"));
std::string pch_1("pch.h");
SourceFile pcs_1("//pcs.cc");
config_1.own_values().set_precompiled_header(pch_1);
@@ -1246,7 +1249,9 @@ TEST_F(TargetTest, ResolvePrecompiledHeaders) {
EXPECT_TRUE(target.config_values().precompiled_source() == pcs_1);
// Second config with different PCH values.
- Config config_2(setup.settings(), Label(SourceDir("//foo/"), "c2"));
+ Config config_2(
+ setup.settings(),
+ Label(SourceDir("//foo/"), "c2", SourceDir("//toolchain/"), "default"));
std::string pch_2("pch2.h");
SourceFile pcs_2("//pcs2.cc");
config_2.own_values().set_precompiled_header(pch_2);
diff --git a/gn/src/gn/template.cc b/gn/src/gn/template.cc
index f0eca28df14..d2c6fc65e90 100644
--- a/gn/src/gn/template.cc
+++ b/gn/src/gn/template.cc
@@ -64,6 +64,10 @@ Value Template::Invoke(Scope* scope,
Scope template_scope(closure_.get());
template_scope.set_source_dir(scope->GetSourceDir());
+ // Track the invocation of the template on the template's scope
+ template_scope.SetTemplateInvocationEntry(
+ template_name, args[0].string_value(), invocation->GetRange().begin());
+
// Propagate build dependency files from invoker scope (template scope already
// propagated via parent scope).
template_scope.AddBuildDependencyFiles(
@@ -112,13 +116,21 @@ Value Template::Invoke(Scope* scope,
invoker_value = template_scope.GetMutableValue(variables::kInvoker,
Scope::SEARCH_NESTED, false);
if (invoker_value && invoker_value->type() == Value::SCOPE) {
- if (!invoker_value->scope_value()->CheckForUnusedVars(err))
+ if (!invoker_value->scope_value()->CheckForUnusedVars(err)) {
+ // If there was an error, append the caller location so the error message
+ // displays a stack trace of how it got here.
+ err->AppendSubErr(Err(invocation, "whence it was called."));
return Value();
+ }
}
// Check for unused variables in the template itself.
- if (!template_scope.CheckForUnusedVars(err))
+ if (!template_scope.CheckForUnusedVars(err)) {
+ // If there was an error, append the caller location so the error message
+ // displays a stack trace of how it got here.
+ err->AppendSubErr(Err(invocation, "whence it was called."));
return Value();
+ }
return result;
}
diff --git a/gn/src/gn/tool.cc b/gn/src/gn/tool.cc
index bf3aba61a0f..684dc3e34ea 100644
--- a/gn/src/gn/tool.cc
+++ b/gn/src/gn/tool.cc
@@ -4,6 +4,7 @@
#include "gn/tool.h"
+#include "gn/builtin_tool.h"
#include "gn/c_tool.h"
#include "gn/general_tool.h"
#include "gn/rust_tool.h"
@@ -52,6 +53,13 @@ const RustTool* Tool::AsRust() const {
return nullptr;
}
+BuiltinTool* Tool::AsBuiltin() {
+ return nullptr;
+}
+const BuiltinTool* Tool::AsBuiltin() const {
+ return nullptr;
+}
+
bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
const SubstitutionPattern& pattern) const {
for (const auto& cur : output_list.list()) {
@@ -342,7 +350,7 @@ const char* Tool::GetToolTypeForSourceType(SourceFile::Type type) {
// static
const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
- // The contents of this list might be surprising (i.e. stamp tool for copy
+ // The contents of this list might be surprising (i.e. phony tool for copy
// rules). See the header for why.
// TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
if (target->source_types_used().RustSourceUsed()) {
@@ -366,8 +374,6 @@ const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
}
}
switch (target->output_type()) {
- case Target::GROUP:
- return GeneralTool::kGeneralToolStamp;
case Target::EXECUTABLE:
return CTool::kCToolLink;
case Target::SHARED_LIBRARY:
@@ -376,15 +382,19 @@ const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
return CTool::kCToolSolinkModule;
case Target::STATIC_LIBRARY:
return CTool::kCToolAlink;
- case Target::SOURCE_SET:
- return GeneralTool::kGeneralToolStamp;
case Target::ACTION:
case Target::ACTION_FOREACH:
case Target::BUNDLE_DATA:
- case Target::CREATE_BUNDLE:
case Target::COPY_FILES:
+ case Target::CREATE_BUNDLE:
case Target::GENERATED_FILE:
- return GeneralTool::kGeneralToolStamp;
+ case Target::GROUP:
+ case Target::SOURCE_SET:
+ if (target->settings()->build_settings()->no_stamp_files()) {
+ return BuiltinTool::kBuiltinToolPhony;
+ } else {
+ return GeneralTool::kGeneralToolStamp;
+ }
default:
NOTREACHED();
return kToolNone;
diff --git a/gn/src/gn/tool.h b/gn/src/gn/tool.h
index 1e01af7778e..b95c64ba7bd 100644
--- a/gn/src/gn/tool.h
+++ b/gn/src/gn/tool.h
@@ -23,6 +23,7 @@ class Toolchain;
class CTool;
class GeneralTool;
class RustTool;
+class BuiltinTool;
// To add a new Tool category, create a subclass implementing SetComplete()
// Add a new category to ToolCategories
@@ -62,6 +63,8 @@ class Tool {
virtual const GeneralTool* AsGeneral() const;
virtual RustTool* AsRust();
virtual const RustTool* AsRust() const;
+ virtual BuiltinTool* AsBuiltin();
+ virtual const BuiltinTool* AsBuiltin() const;
// Basic information ---------------------------------------------------------
@@ -233,6 +236,7 @@ class Tool {
Err* err);
static const char* GetToolTypeForSourceType(SourceFile::Type type);
+
static const char* GetToolTypeForTargetFinalOutput(const Target* target);
protected:
diff --git a/gn/src/gn/toolchain.cc b/gn/src/gn/toolchain.cc
index bfad81db635..a65618466cd 100644
--- a/gn/src/gn/toolchain.cc
+++ b/gn/src/gn/toolchain.cc
@@ -9,13 +9,19 @@
#include <utility>
#include "base/logging.h"
+#include "gn/builtin_tool.h"
+#include "gn/settings.h"
#include "gn/target.h"
#include "gn/value.h"
Toolchain::Toolchain(const Settings* settings,
const Label& label,
const SourceFileSet& build_dependency_files)
- : Item(settings, label, build_dependency_files) {}
+ : Item(settings, label, build_dependency_files) {
+ // Ensure "phony" tool is part of all toolchains by default.
+ const char* phony_name = BuiltinTool::kBuiltinToolPhony;
+ tools_.emplace(phony_name, std::make_unique<BuiltinTool>(phony_name));
+}
Toolchain::~Toolchain() = default;
@@ -87,6 +93,20 @@ const RustTool* Toolchain::GetToolAsRust(const char* name) const {
return nullptr;
}
+BuiltinTool* Toolchain::GetToolAsBuiltin(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsBuiltin();
+ }
+ return nullptr;
+}
+
+const BuiltinTool* Toolchain::GetToolAsBuiltin(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsBuiltin();
+ }
+ return nullptr;
+}
+
void Toolchain::SetTool(std::unique_ptr<Tool> t) {
DCHECK(t->name() != Tool::kToolNone);
DCHECK(tools_.find(t->name()) == tools_.end());
@@ -120,6 +140,11 @@ const RustTool* Toolchain::GetToolForSourceTypeAsRust(
return GetToolAsRust(Tool::GetToolTypeForSourceType(type));
}
+const BuiltinTool* Toolchain::GetToolForSourceTypeAsBuiltin(
+ SourceFile::Type type) const {
+ return GetToolAsBuiltin(Tool::GetToolTypeForSourceType(type));
+}
+
const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
return GetTool(Tool::GetToolTypeForTargetFinalOutput(target));
}
@@ -138,3 +163,8 @@ const RustTool* Toolchain::GetToolForTargetFinalOutputAsRust(
const Target* target) const {
return GetToolAsRust(Tool::GetToolTypeForTargetFinalOutput(target));
}
+
+const BuiltinTool* Toolchain::GetToolForTargetFinalOutputAsBuiltin(
+ const Target* target) const {
+ return GetToolAsBuiltin(Tool::GetToolTypeForTargetFinalOutput(target));
+}
diff --git a/gn/src/gn/toolchain.h b/gn/src/gn/toolchain.h
index eb5a60c526e..270fada797f 100644
--- a/gn/src/gn/toolchain.h
+++ b/gn/src/gn/toolchain.h
@@ -16,6 +16,8 @@
#include "gn/tool.h"
#include "gn/value.h"
+class BuiltinTool;
+
// Holds information on a specific toolchain. This data is filled in when we
// encounter a toolchain definition.
//
@@ -62,6 +64,8 @@ class Toolchain : public Item {
const CTool* GetToolAsC(const char* name) const;
RustTool* GetToolAsRust(const char* name);
const RustTool* GetToolAsRust(const char* name) const;
+ BuiltinTool* GetToolAsBuiltin(const char* name);
+ const BuiltinTool* GetToolAsBuiltin(const char* name) const;
// Set a tool. When all tools are configured, you should call
// ToolchainSetupComplete().
@@ -93,6 +97,7 @@ class Toolchain : public Item {
const CTool* GetToolForSourceTypeAsC(SourceFile::Type type) const;
const GeneralTool* GetToolForSourceTypeAsGeneral(SourceFile::Type type) const;
const RustTool* GetToolForSourceTypeAsRust(SourceFile::Type type) const;
+ const BuiltinTool* GetToolForSourceTypeAsBuiltin(SourceFile::Type type) const;
// Returns the tool that produces the final output for the given target type.
// This isn't necessarily the tool you would expect. For copy target, this
@@ -103,6 +108,8 @@ class Toolchain : public Item {
const GeneralTool* GetToolForTargetFinalOutputAsGeneral(
const Target* target) const;
const RustTool* GetToolForTargetFinalOutputAsRust(const Target* target) const;
+ const BuiltinTool* GetToolForTargetFinalOutputAsBuiltin(
+ const Target* target) const;
const SubstitutionBits& substitution_bits() const {
DCHECK(setup_complete_);
diff --git a/gn/src/gn/trace.cc b/gn/src/gn/trace.cc
index c6eeed2d64d..bc3f50e6881 100644
--- a/gn/src/gn/trace.cc
+++ b/gn/src/gn/trace.cc
@@ -30,18 +30,25 @@ class TraceLog {
TraceLog() { events_.reserve(16384); }
// Trace items leaked intentionally.
- void Add(TraceItem* item) {
+ void Add(std::unique_ptr<TraceItem> item) {
std::lock_guard<std::mutex> lock(lock_);
- events_.push_back(item);
+ events_.push_back(std::move(item));
}
// Returns a copy for threadsafety.
- std::vector<TraceItem*> events() const { return events_; }
+ std::vector<TraceItem*> events() const {
+ std::vector<TraceItem*> events;
+ std::lock_guard<std::mutex> lock(lock_);
+ events.reserve(events_.size());
+ for (const auto& e : events_)
+ events.push_back(e.get());
+ return events;
+ }
private:
- std::mutex lock_;
+ mutable std::mutex lock_;
- std::vector<TraceItem*> events_;
+ std::vector<std::unique_ptr<TraceItem>> events_;
TraceLog(const TraceLog&) = delete;
TraceLog& operator=(const TraceLog&) = delete;
@@ -120,18 +127,17 @@ TraceItem::TraceItem(Type type,
TraceItem::~TraceItem() = default;
ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name)
- : item_(nullptr), done_(false) {
+ : done_(false) {
if (trace_log) {
- item_ = new TraceItem(t, name, std::this_thread::get_id());
+ item_ = std::make_unique<TraceItem>(t, name, std::this_thread::get_id());
item_->set_begin(TicksNow());
}
}
-ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label)
- : item_(nullptr), done_(false) {
+ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label) : done_(false) {
if (trace_log) {
- item_ = new TraceItem(t, label.GetUserVisibleName(false),
- std::this_thread::get_id());
+ item_ = std::make_unique<TraceItem>(t, label.GetUserVisibleName(false),
+ std::this_thread::get_id());
item_->set_begin(TicksNow());
}
}
@@ -155,7 +161,7 @@ void ScopedTrace::Done() {
done_ = true;
if (trace_log) {
item_->set_end(TicksNow());
- AddTrace(item_);
+ AddTrace(std::move(item_));
}
}
}
@@ -169,8 +175,8 @@ bool TracingEnabled() {
return !!trace_log;
}
-void AddTrace(TraceItem* item) {
- trace_log->Add(item);
+void AddTrace(std::unique_ptr<TraceItem> item) {
+ trace_log->Add(std::move(item));
}
std::string SummarizeTraces() {
@@ -244,19 +250,27 @@ void SaveTraces(const base::FilePath& file_name) {
std::string quote_buffer; // Allocate outside loop to prevent reallocationg.
+ // Trace viewer doesn't handle integer > 2^53 well, so re-numbering them to
+ // small numbers.
+ std::map<std::thread::id, int> tidmap;
+ std::vector<TraceItem*> events = trace_log->events();
+ for (const auto* item : events) {
+ int id = tidmap.size();
+ tidmap.emplace(item->thread_id(), id);
+ }
+
// Write main thread metadata (assume this is being written on the main
// thread).
- out << "{\"pid\":0,\"tid\":\"" << std::this_thread::get_id() << "\"";
+ out << "{\"pid\":0,\"tid\":\"" << tidmap[std::this_thread::get_id()] << "\"";
out << ",\"ts\":0,\"ph\":\"M\",";
out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
- std::vector<TraceItem*> events = trace_log->events();
for (size_t i = 0; i < events.size(); i++) {
const TraceItem& item = *events[i];
if (i != 0)
out << ",";
- out << "{\"pid\":0,\"tid\":\"" << item.thread_id() << "\"";
+ out << "{\"pid\":0,\"tid\":\"" << tidmap[item.thread_id()] << "\"";
out << ",\"ts\":" << item.begin() / kNanosecondsToMicroseconds;
out << ",\"ph\":\"X\""; // "X" = complete event with begin & duration.
out << ",\"dur\":" << item.delta().InMicroseconds();
diff --git a/gn/src/gn/trace.h b/gn/src/gn/trace.h
index b89e82a1ab6..b59cd34ed41 100644
--- a/gn/src/gn/trace.h
+++ b/gn/src/gn/trace.h
@@ -5,6 +5,7 @@
#ifndef TOOLS_GN_TRACE_H_
#define TOOLS_GN_TRACE_H_
+#include <memory>
#include <string>
#include <thread>
@@ -80,7 +81,7 @@ class ScopedTrace {
void Done();
private:
- TraceItem* item_;
+ std::unique_ptr<TraceItem> item_;
bool done_;
};
@@ -90,8 +91,8 @@ void EnableTracing();
// Returns whether tracing is enabled.
bool TracingEnabled();
-// Adds a trace event to the log. Takes ownership of the pointer.
-void AddTrace(TraceItem* item);
+// Adds a trace event to the log.
+void AddTrace(std::unique_ptr<TraceItem> item);
// Returns a summary of the current traces, or the empty string if tracing is
// not enabled.
diff --git a/gn/src/gn/unique_vector.h b/gn/src/gn/unique_vector.h
index 0f6994fe447..473786d4a0e 100644
--- a/gn/src/gn/unique_vector.h
+++ b/gn/src/gn/unique_vector.h
@@ -186,11 +186,28 @@ class UniqueVector {
push_back(*i);
}
- // Append another vector into this one.
- void Append(const UniqueVector& other) {
+ // Append from any iterable container with begin() and end()
+ // methods, whose iterators derefence to values convertible to T.
+ template <typename C,
+ typename = std::void_t<
+ decltype(static_cast<const T>(*std::declval<C>().begin())),
+ decltype(static_cast<const T>(*std::declval<C>().end()))>>
+ void Append(const C& other) {
Append(other.begin(), other.end());
}
+ // Append from any moveable iterable container with begin() and
+ // end() methods. This variant moves items from the container
+ // into the UniqueVector instance.
+ template <typename C,
+ typename = std::void_t<
+ decltype(static_cast<T>(*std::declval<C>().begin())),
+ decltype(static_cast<T>(*std::declval<C>().end()))>>
+ void Append(C&& other) {
+ for (auto it = other.begin(); it != other.end(); ++it)
+ push_back(std::move(*it));
+ }
+
// Returns true if the item is already in the vector.
bool Contains(const T& t) const {
size_t hash;
diff --git a/gn/src/gn/variables.cc b/gn/src/gn/variables.cc
index fba20e07ee5..28f174db389 100644
--- a/gn/src/gn/variables.cc
+++ b/gn/src/gn/variables.cc
@@ -561,6 +561,14 @@ const char kArgs_Help[] =
to the script. Typically you would use source expansion (see "gn help
source_expansion") to insert the source file names.
+ Args can also expand the substitution patterns corresponding to config
+ variables in the same way that compiler tools (see "gn help tool") do. These
+ allow actions that run compiler or compiler-like tools to access the results
+ of propagating configs through the build graph. For example:
+
+ args = [ "{{defines}}", "{{include_dirs}}", "{{rustenv}}", "--input-file",
+ "{{source}}" ]
+
See also "gn help action" and "gn help action_foreach".
)";
@@ -1104,10 +1112,10 @@ Example
# Locate the depfile in the output directory named like the
# inputs but with a ".d" appended.
- depfile = "$relative_target_output_dir/{{source_name}}.d"
+ depfile = "$target_gen_dir/{{source_name_part}}.d"
# Say our script uses "-o <d file>" to indicate the depfile.
- args = [ "{{source}}", "-o", depfile ]
+ args = [ "{{source}}", "-o", rebase_path(depfile, root_build_dir)]
}
)";
@@ -1634,15 +1642,20 @@ const char kOutputs_Help[] =
const char kPool[] = "pool";
const char kPool_HelpShort[] =
- "pool: [string] Label of the pool used by the action.";
+ "pool: [string] Label of the pool used by binary targets and actions.";
const char kPool_Help[] =
- R"(pool: Label of the pool used by the action.
+ R"(pool: Label of the pool used by binary targets actions.
- A fully-qualified label representing the pool that will be used for the
- action. Pools are defined using the pool() {...} declaration.
+ A fully-qualified label representing the pool that will be used for binary
+ targets and actions. Pools are defined using the pool() {...} declaration.
Example
+ executable("binary") {
+ pool = "//build:custom_pool"
+ ...
+ }
+
action("action") {
pool = "//build:custom_pool"
...
diff --git a/gn/src/gn/visibility.cc b/gn/src/gn/visibility.cc
index 08789906ca5..61423ff9534 100644
--- a/gn/src/gn/visibility.cc
+++ b/gn/src/gn/visibility.cc
@@ -92,9 +92,14 @@ bool Visibility::CheckItemVisibility(const Item* from,
const Item* to,
Err* err) {
if (!to->visibility().CanSeeMe(from->label())) {
- std::string to_label = to->label().GetUserVisibleName(false);
+ bool with_toolchain = from->settings()->ShouldShowToolchain({
+ &to->label(),
+ &from->label(),
+ });
+ std::string to_label = to->label().GetUserVisibleName(with_toolchain);
+ std::string from_label = from->label().GetUserVisibleName(with_toolchain);
*err = Err(from->defined_from(), "Dependency not allowed.",
- "The item " + from->label().GetUserVisibleName(false) +
+ "The item " + from_label +
"\n"
"can not depend on " +
to_label +
diff --git a/gn/src/gn/xcode_object.cc b/gn/src/gn/xcode_object.cc
index f3c5a54f95a..4399c55af5c 100644
--- a/gn/src/gn/xcode_object.cc
+++ b/gn/src/gn/xcode_object.cc
@@ -116,6 +116,7 @@ const SourceTypeForExt kSourceTypeForExt[] = {
{"js", "sourcecode.javascript"},
{"kext", "wrapper.kext"},
{"m", "sourcecode.c.objc"},
+ {"md", "net.daringfireball.markdown"},
{"mm", "sourcecode.cpp.objcpp"},
{"nib", "wrapper.nib"},
{"o", "compiled.mach-o.objfile"},
@@ -131,14 +132,15 @@ const SourceTypeForExt kSourceTypeForExt[] = {
{"storyboard", "file.storyboard"},
{"strings", "text.plist.strings"},
{"swift", "sourcecode.swift"},
+ {"ts", "sourcecode.javascript"},
{"ttf", "file"},
{"xcassets", "folder.assetcatalog"},
{"xcconfig", "text.xcconfig"},
{"xcdatamodel", "wrapper.xcdatamodel"},
{"xcdatamodeld", "wrapper.xcdatamodeld"},
{"xctest", "wrapper.cfbundle"},
- {"xpc", "wrapper.xpc-service"},
{"xib", "file.xib"},
+ {"xpc", "wrapper.xpc-service"},
{"y", "sourcecode.yacc"},
};
@@ -152,7 +154,7 @@ const char* GetSourceType(std::string_view ext) {
}
bool HasExplicitFileType(std::string_view ext) {
- return ext == "dart";
+ return ext == "dart" || ext == "ts";
}
bool IsSourceFileForIndexing(std::string_view ext) {
@@ -253,19 +255,26 @@ void PrintProperty(std::ostream& out,
struct PBXGroupComparator {
using PBXObjectPtr = std::unique_ptr<PBXObject>;
bool operator()(const PBXObjectPtr& lhs, const PBXObjectPtr& rhs) {
+ if (lhs.get() == rhs.get())
+ return false;
+
+ // Ensure that PBXGroup that should sort last are sorted last.
+ const bool lhs_sort_last = SortLast(lhs);
+ const bool rhs_sort_last = SortLast(rhs);
+ if (lhs_sort_last != rhs_sort_last)
+ return rhs_sort_last;
+
if (lhs->Class() != rhs->Class())
return rhs->Class() < lhs->Class();
- if (lhs->Class() == PBXGroupClass) {
- PBXGroup* lhs_group = static_cast<PBXGroup*>(lhs.get());
- PBXGroup* rhs_group = static_cast<PBXGroup*>(rhs.get());
- return lhs_group->name() < rhs_group->name();
- }
+ return lhs->Name() < rhs->Name();
+ }
- DCHECK_EQ(lhs->Class(), PBXFileReferenceClass);
- PBXFileReference* lhs_file = static_cast<PBXFileReference*>(lhs.get());
- PBXFileReference* rhs_file = static_cast<PBXFileReference*>(rhs.get());
- return lhs_file->Name() < rhs_file->Name();
+ bool SortLast(const PBXObjectPtr& ptr) {
+ if (ptr->Class() != PBXGroupClass)
+ return false;
+
+ return static_cast<PBXGroup*>(ptr.get())->SortLast();
}
};
} // namespace
@@ -380,10 +389,10 @@ void PBXBuildPhase::Visit(PBXObjectVisitorConst& visitor) const {
PBXTarget::PBXTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes)
: configurations_(
- std::make_unique<XCConfigurationList>(config_name, attributes, this)),
+ std::make_unique<XCConfigurationList>(configs, attributes, this)),
name_(name) {
if (!shell_script.empty()) {
build_phases_.push_back(
@@ -424,9 +433,9 @@ void PBXTarget::Visit(PBXObjectVisitorConst& visitor) const {
PBXAggregateTarget::PBXAggregateTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes)
- : PBXTarget(name, shell_script, config_name, attributes) {}
+ : PBXTarget(name, shell_script, configs, attributes) {}
PBXAggregateTarget::~PBXAggregateTarget() = default;
@@ -534,10 +543,9 @@ void PBXFileReference::Print(std::ostream& out, unsigned indent) const {
PrintProperty(out, rules, "includeInIndex", 0u);
} else {
std::string_view ext = FindExtension(&name_);
- if (HasExplicitFileType(ext))
- PrintProperty(out, rules, "explicitFileType", GetSourceType(ext));
- else
- PrintProperty(out, rules, "lastKnownFileType", GetSourceType(ext));
+ const char* prop_name =
+ HasExplicitFileType(ext) ? "explicitFileType" : "lastKnownFileType";
+ PrintProperty(out, rules, prop_name, GetSourceType(ext));
}
if (!name_.empty() && name_ != path_)
@@ -586,6 +594,7 @@ PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
const std::string& source_path) {
DCHECK(!navigator_path.empty());
DCHECK(!source_path.empty());
+
std::string::size_type sep = navigator_path.find("/");
if (sep == std::string::npos) {
// Prevent same file reference being created and added multiple times.
@@ -596,12 +605,12 @@ PBXFileReference* PBXGroup::AddSourceFile(const std::string& navigator_path,
PBXFileReference* child_as_file_reference =
static_cast<PBXFileReference*>(child.get());
if (child_as_file_reference->Name() == navigator_path &&
- child_as_file_reference->path() == source_path) {
+ child_as_file_reference->path() == navigator_path) {
return child_as_file_reference;
}
}
- return CreateChild<PBXFileReference>(navigator_path, source_path,
+ return CreateChild<PBXFileReference>(navigator_path, navigator_path,
std::string());
}
@@ -660,40 +669,59 @@ void PBXGroup::Print(std::ostream& out, unsigned indent) const {
out << indent_str << Reference() << " = {\n";
PrintProperty(out, rules, "isa", ToString(Class()));
PrintProperty(out, rules, "children", children_);
- if (!name_.empty())
+ if (!name_.empty() && name_ != path_)
PrintProperty(out, rules, "name", name_);
- if (is_source_ && !path_.empty())
+ if (!path_.empty())
PrintProperty(out, rules, "path", path_);
PrintProperty(out, rules, "sourceTree", "<group>");
out << indent_str << "};\n";
}
+bool PBXGroup::SortLast() const {
+ return false;
+}
+
PBXObject* PBXGroup::AddChildImpl(std::unique_ptr<PBXObject> child) {
DCHECK(child);
DCHECK(child->Class() == PBXGroupClass ||
child->Class() == PBXFileReferenceClass);
- PBXObject* child_ptr = child.get();
- if (autosorted()) {
- auto iter = std::lower_bound(children_.begin(), children_.end(), child,
- PBXGroupComparator());
- children_.insert(iter, std::move(child));
- } else {
- children_.push_back(std::move(child));
- }
- return child_ptr;
+ auto iter = std::lower_bound(children_.begin(), children_.end(), child,
+ PBXGroupComparator());
+ return children_.insert(iter, std::move(child))->get();
+}
+
+// PBXMainGroup ---------------------------------------------------------------
+
+PBXMainGroup::PBXMainGroup(const std::string& source_path)
+ : PBXGroup(source_path, std::string()) {}
+
+PBXMainGroup::~PBXMainGroup() = default;
+
+std::string PBXMainGroup::Name() const {
+ return std::string();
+}
+
+// PBXProductsGroup -----------------------------------------------------------
+
+PBXProductsGroup::PBXProductsGroup() : PBXGroup(std::string(), "Products") {}
+
+PBXProductsGroup::~PBXProductsGroup() = default;
+
+bool PBXProductsGroup::SortLast() const {
+ return true;
}
// PBXNativeTarget ------------------------------------------------------------
PBXNativeTarget::PBXNativeTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes,
const std::string& product_type,
const std::string& product_name,
const PBXFileReference* product_reference)
- : PBXTarget(name, shell_script, config_name, attributes),
+ : PBXTarget(name, shell_script, configs, attributes),
product_reference_(product_reference),
product_type_(product_type),
product_name_(product_name) {
@@ -746,20 +774,15 @@ void PBXNativeTarget::Print(std::ostream& out, unsigned indent) const {
// PBXProject -----------------------------------------------------------------
PBXProject::PBXProject(const std::string& name,
- const std::string& config_name,
+ std::vector<std::string> configs,
const std::string& source_path,
const PBXAttributes& attributes)
- : name_(name), config_name_(config_name), target_for_indexing_(nullptr) {
- main_group_ = std::make_unique<PBXGroup>();
- main_group_->set_autosorted(false);
-
- sources_ = main_group_->CreateChild<PBXGroup>(source_path, "Source");
- sources_->set_is_source(true);
-
- products_ = main_group_->CreateChild<PBXGroup>(std::string(), "Products");
+ : name_(name), configs_(std::move(configs)), target_for_indexing_(nullptr) {
+ main_group_ = std::make_unique<PBXMainGroup>(source_path);
+ products_ = main_group_->CreateChild<PBXProductsGroup>();
configurations_ =
- std::make_unique<XCConfigurationList>(config_name, attributes, this);
+ std::make_unique<XCConfigurationList>(configs_, attributes, this);
}
PBXProject::~PBXProject() = default;
@@ -777,7 +800,7 @@ void PBXProject::AddSourceFile(const std::string& navigator_path,
const std::string& source_path,
PBXNativeTarget* target) {
PBXFileReference* file_reference =
- sources_->AddSourceFile(navigator_path, source_path);
+ main_group_->AddSourceFile(navigator_path, source_path);
std::string_view ext = FindExtension(&source_path);
if (!IsSourceFileForIndexing(ext))
return;
@@ -787,15 +810,16 @@ void PBXProject::AddSourceFile(const std::string& navigator_path,
}
void PBXProject::AddAggregateTarget(const std::string& name,
+ const std::string& output_dir,
const std::string& shell_script) {
PBXAttributes attributes;
attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
attributes["CODE_SIGNING_REQUIRED"] = "NO";
- attributes["CONFIGURATION_BUILD_DIR"] = ".";
+ attributes["CONFIGURATION_BUILD_DIR"] = output_dir;
attributes["PRODUCT_NAME"] = name;
targets_.push_back(std::make_unique<PBXAggregateTarget>(
- name, shell_script, config_name_, attributes));
+ name, shell_script, configs_, attributes));
}
void PBXProject::AddIndexingTarget() {
@@ -804,7 +828,7 @@ void PBXProject::AddIndexingTarget() {
attributes["CLANG_ENABLE_OBJC_WEAK"] = "YES";
attributes["CODE_SIGNING_REQUIRED"] = "NO";
attributes["EXECUTABLE_PREFIX"] = "";
- attributes["HEADER_SEARCH_PATHS"] = sources_->path();
+ attributes["HEADER_SEARCH_PATHS"] = main_group_->path();
attributes["PRODUCT_NAME"] = "sources";
PBXFileReference* product_reference =
@@ -813,8 +837,8 @@ void PBXProject::AddIndexingTarget() {
const char product_type[] = "com.apple.product-type.tool";
targets_.push_back(std::make_unique<PBXNativeTarget>(
- "sources", std::string(), config_name_, attributes, product_type,
- "sources", product_reference));
+ "sources", std::string(), configs_, attributes, product_type, "sources",
+ product_reference));
target_for_indexing_ = static_cast<PBXNativeTarget*>(targets_.back().get());
}
@@ -851,7 +875,7 @@ PBXNativeTarget* PBXProject::AddNativeTarget(
attributes["EXCLUDED_SOURCE_FILE_NAMES"] = "*.*";
targets_.push_back(std::make_unique<PBXNativeTarget>(
- name, shell_script, config_name_, attributes, output_type, product_name,
+ name, shell_script, configs_, attributes, output_type, product_name,
product));
return static_cast<PBXNativeTarget*>(targets_.back().get());
}
@@ -913,6 +937,7 @@ void PBXProject::Print(std::ostream& out, unsigned indent) const {
PrintProperty(out, rules, "knownRegions",
std::vector<std::string>({"en", "Base"}));
PrintProperty(out, rules, "mainGroup", main_group_);
+ PrintProperty(out, rules, "productRefGroup", products_);
PrintProperty(out, rules, "projectDirPath", project_dir_path_);
PrintProperty(out, rules, "projectRoot", project_root_);
PrintProperty(out, rules, "targets", targets_);
@@ -967,13 +992,14 @@ void PBXShellScriptBuildPhase::Print(std::ostream& out, unsigned indent) const {
const IndentRules rules = {false, indent + 1};
out << indent_str << Reference() << " = {\n";
PrintProperty(out, rules, "isa", ToString(Class()));
+ PrintProperty(out, rules, "alwaysOutOfDate", 1u);
PrintProperty(out, rules, "buildActionMask", 0x7fffffffu);
PrintProperty(out, rules, "files", files_);
PrintProperty(out, rules, "inputPaths", EmptyPBXObjectVector());
PrintProperty(out, rules, "name", name_);
PrintProperty(out, rules, "outputPaths", EmptyPBXObjectVector());
PrintProperty(out, rules, "runOnlyForDeploymentPostprocessing", 0u);
- PrintProperty(out, rules, "shellPath", "/usr/bin/python3");
+ PrintProperty(out, rules, "shellPath", "/bin/sh");
PrintProperty(out, rules, "shellScript", shell_script_);
PrintProperty(out, rules, "showEnvVarsInLog", 0u);
out << indent_str << "};\n";
@@ -1067,13 +1093,16 @@ void XCBuildConfiguration::Print(std::ostream& out, unsigned indent) const {
// XCConfigurationList --------------------------------------------------------
-XCConfigurationList::XCConfigurationList(const std::string& name,
- const PBXAttributes& attributes,
- const PBXObject* owner_reference)
+XCConfigurationList::XCConfigurationList(
+ const std::vector<std::string>& configs,
+ const PBXAttributes& attributes,
+ const PBXObject* owner_reference)
: owner_reference_(owner_reference) {
DCHECK(owner_reference_);
- configurations_.push_back(
- std::make_unique<XCBuildConfiguration>(name, attributes));
+ for (const std::string& config_name : configs) {
+ configurations_.push_back(
+ std::make_unique<XCBuildConfiguration>(config_name, attributes));
+ }
}
XCConfigurationList::~XCConfigurationList() = default;
@@ -1110,7 +1139,7 @@ void XCConfigurationList::Print(std::ostream& out, unsigned indent) const {
out << indent_str << Reference() << " = {\n";
PrintProperty(out, rules, "isa", ToString(Class()));
PrintProperty(out, rules, "buildConfigurations", configurations_);
- PrintProperty(out, rules, "defaultConfigurationIsVisible", 1u);
+ PrintProperty(out, rules, "defaultConfigurationIsVisible", 0u);
PrintProperty(out, rules, "defaultConfigurationName",
configurations_[0]->Name());
out << indent_str << "};\n";
diff --git a/gn/src/gn/xcode_object.h b/gn/src/gn/xcode_object.h
index e29eb3d6c08..076e9937d28 100644
--- a/gn/src/gn/xcode_object.h
+++ b/gn/src/gn/xcode_object.h
@@ -144,7 +144,7 @@ class PBXTarget : public PBXObject {
public:
PBXTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes);
~PBXTarget() override;
@@ -174,7 +174,7 @@ class PBXAggregateTarget : public PBXTarget {
public:
PBXAggregateTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes);
~PBXAggregateTarget() override;
@@ -284,12 +284,6 @@ class PBXGroup : public PBXObject {
PBXFileReference* AddSourceFile(const std::string& navigator_path,
const std::string& source_path);
- bool is_source() const { return is_source_; }
- void set_is_source(bool is_source) { is_source_ = is_source; }
-
- bool autosorted() const { return autosorted_; }
- void set_autosorted(bool autosorted) { autosorted_ = autosorted; }
-
template <typename T, typename... Args>
T* CreateChild(Args&&... args) {
return static_cast<T*>(
@@ -303,26 +297,50 @@ class PBXGroup : public PBXObject {
void Visit(PBXObjectVisitorConst& visitor) const override;
void Print(std::ostream& out, unsigned indent) const override;
+ // Returns whether the current PBXGroup should sort last when sorting
+ // children of a PBXGroup. This should only be used for the "Products"
+ // group which is hidden in Xcode UI when it is the last children of
+ // the main PBXProject group.
+ virtual bool SortLast() const;
+
private:
PBXObject* AddChildImpl(std::unique_ptr<PBXObject> child);
std::vector<std::unique_ptr<PBXObject>> children_;
std::string name_;
std::string path_;
- bool is_source_ = false;
- bool autosorted_ = true;
PBXGroup(const PBXGroup&) = delete;
PBXGroup& operator=(const PBXGroup&) = delete;
};
+// PBXMainGroup ---------------------------------------------------------------
+
+class PBXMainGroup : public PBXGroup {
+ public:
+ explicit PBXMainGroup(const std::string& source_path);
+ ~PBXMainGroup() override;
+
+ std::string Name() const override;
+};
+
+// PBXProductsGroup -----------------------------------------------------------
+
+class PBXProductsGroup : public PBXGroup {
+ public:
+ explicit PBXProductsGroup();
+ ~PBXProductsGroup() override;
+
+ bool SortLast() const override;
+};
+
// PBXNativeTarget ------------------------------------------------------------
class PBXNativeTarget : public PBXTarget {
public:
PBXNativeTarget(const std::string& name,
const std::string& shell_script,
- const std::string& config_name,
+ const std::vector<std::string>& configs,
const PBXAttributes& attributes,
const std::string& product_type,
const std::string& product_name,
@@ -351,7 +369,7 @@ class PBXNativeTarget : public PBXTarget {
class PBXProject : public PBXObject {
public:
PBXProject(const std::string& name,
- const std::string& config_name,
+ std::vector<std::string> configs,
const std::string& source_path,
const PBXAttributes& attributes);
~PBXProject() override;
@@ -362,6 +380,7 @@ class PBXProject : public PBXObject {
const std::string& source_path,
PBXNativeTarget* target);
void AddAggregateTarget(const std::string& name,
+ const std::string& output_dir,
const std::string& shell_script);
void AddIndexingTarget();
PBXNativeTarget* AddNativeTarget(
@@ -393,9 +412,8 @@ class PBXProject : public PBXObject {
std::string project_root_;
std::vector<std::unique_ptr<PBXTarget>> targets_;
std::string name_;
- std::string config_name_;
+ std::vector<std::string> configs_;
- PBXGroup* sources_ = nullptr;
PBXGroup* products_ = nullptr;
PBXNativeTarget* target_for_indexing_ = nullptr;
@@ -506,7 +524,7 @@ class XCBuildConfiguration : public PBXObject {
class XCConfigurationList : public PBXObject {
public:
- XCConfigurationList(const std::string& name,
+ XCConfigurationList(const std::vector<std::string>& configs,
const PBXAttributes& attributes,
const PBXObject* owner_reference);
~XCConfigurationList() override;
diff --git a/gn/src/gn/xcode_object_unittest.cc b/gn/src/gn/xcode_object_unittest.cc
index 0498c3f1f36..e20a1d0ca5d 100644
--- a/gn/src/gn/xcode_object_unittest.cc
+++ b/gn/src/gn/xcode_object_unittest.cc
@@ -38,7 +38,7 @@ std::unique_ptr<PBXGroup> GetPBXGroupObject() {
// Instantiate a PBXProject object with arbitrary names.
std::unique_ptr<PBXProject> GetPBXProjectObject() {
std::unique_ptr<PBXProject> pbx_project(
- new PBXProject("project", "config", "out/build", PBXAttributes()));
+ new PBXProject("project", {"config"}, "out/build", PBXAttributes()));
return pbx_project;
}
@@ -61,7 +61,7 @@ std::unique_ptr<PBXBuildFile> GetPBXBuildFileObject(
// Instantiate a PBXAggregateTarget object with arbitrary names.
std::unique_ptr<PBXAggregateTarget> GetPBXAggregateTargetObject() {
std::unique_ptr<PBXAggregateTarget> pbx_aggregate_target(
- new PBXAggregateTarget("target_name", "shell_script", "config_name",
+ new PBXAggregateTarget("target_name", "shell_script", {"config_name"},
PBXAttributes()));
return pbx_aggregate_target;
}
@@ -70,7 +70,7 @@ std::unique_ptr<PBXAggregateTarget> GetPBXAggregateTargetObject() {
std::unique_ptr<PBXNativeTarget> GetPBXNativeTargetObject(
const PBXFileReference* product_reference) {
std::unique_ptr<PBXNativeTarget> pbx_native_target(new PBXNativeTarget(
- "target_name", "ninja gn_unittests", "config_name", PBXAttributes(),
+ "target_name", "ninja gn_unittests", {"config_name"}, PBXAttributes(),
"com.apple.product-type.application", "product_name", product_reference));
return pbx_native_target;
}
@@ -104,7 +104,7 @@ std::unique_ptr<XCBuildConfiguration> GetXCBuildConfigurationObject() {
std::unique_ptr<XCConfigurationList> GetXCConfigurationListObject(
const PBXObject* owner_reference) {
std::unique_ptr<XCConfigurationList> xc_configuration_list(
- new XCConfigurationList("config_list_name", PBXAttributes(),
+ new XCConfigurationList({"config_list_name"}, PBXAttributes(),
owner_reference));
return xc_configuration_list;
}
diff --git a/gn/src/gn/xcode_writer.cc b/gn/src/gn/xcode_writer.cc
index 5e57ec97fb6..2673d3c1859 100644
--- a/gn/src/gn/xcode_writer.cc
+++ b/gn/src/gn/xcode_writer.cc
@@ -11,15 +11,17 @@
#include <optional>
#include <sstream>
#include <string>
+#include <string_view>
#include <utility>
#include "base/environment.h"
+#include "base/files/file_enumerator.h"
#include "base/logging.h"
#include "base/sha1.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
#include "gn/args.h"
#include "gn/build_settings.h"
#include "gn/builder.h"
@@ -40,67 +42,14 @@
namespace {
-// This is the template of the script used to build the target. It invokes
-// ninja (supporting --ninja-executable parameter), parsing ninja's output
-// using a regular expression looking for relative path to the source root
-// from root_build_dir that are at the start of a path and converting them
-// to absolute paths (use str.replace(rel_root_src, abs_root_src) would be
-// simpler but would fail if rel_root_src is present multiple time in the
-// path).
-const char kBuildScriptTemplate[] = R"(
-import re
-import os
-import subprocess
-import sys
-
-rel_root_src = '%s'
-abs_root_src = os.path.abspath(rel_root_src) + '/'
-
-build_target = '%s'
-ninja_binary = '%s'
-ninja_params = [ '-C', '.' ]
-
-%s
-
-if build_target:
- ninja_params.append(build_target)
- print('Compile "' + build_target + '" via ninja')
-else:
- print('Compile "all" via ninja')
-
-process = subprocess.Popen(
- [ ninja_binary ] + ninja_params,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- universal_newlines=True,
- encoding='utf-8',
- env=environ)
-
-pattern = re.compile('(?<!/)' + re.escape(rel_root_src))
-
-for line in iter(process.stdout.readline, ''):
- while True:
- match = pattern.search(line)
- if not match:
- break
- span = match.span()
- print(line[:span[0]], end='')
- print(abs_root_src, end='')
- line = line[span[1]:]
- print(line, flush=True, end='')
-
-process.wait()
-
-sys.exit(process.returncode)
-)";
-
enum TargetOsType {
WRITER_TARGET_OS_IOS,
WRITER_TARGET_OS_MACOS,
};
const char* kXCTestFileSuffixes[] = {
- "egtest.m", "egtest.mm", "xctest.m", "xctest.mm", "UITests.m", "UITests.mm",
+ "egtest.m", "egtest.mm", "egtest.swift", "xctest.m", "xctest.mm",
+ "xctest.swift", "UITests.m", "UITests.mm", "UITests.swift",
};
const char kXCTestModuleTargetNamePostfix[] = "_module";
@@ -131,35 +80,50 @@ TargetOsType GetTargetOs(const Args& args) {
return WRITER_TARGET_OS_MACOS;
}
-std::string GetNinjaExecutable(const std::string& ninja_executable) {
- return ninja_executable.empty() ? "ninja" : ninja_executable;
-}
-
-std::string ComputeScriptEnviron(base::Environment* environment) {
+std::string GetBuildScript(const std::string& target_name,
+ const std::string& ninja_executable,
+ const std::string& build_dir,
+ base::Environment* environment) {
+ // Launch ninja with a sanitized environment (Xcode sets many environment
+ // variables overridding settings, including the SDK, thus breaking hermetic
+ // build).
std::stringstream buffer;
- buffer << "environ = {}";
+ buffer << "exec env -i ";
+
+ // Write environment.
for (const auto& variable : kSafeEnvironmentVariables) {
- buffer << "\nenviron['" << variable.name << "'] = ";
+ buffer << variable.name << "=";
if (variable.capture_at_generation) {
std::string value;
environment->GetVar(variable.name, &value);
buffer << "'" << value << "'";
} else {
- buffer << "os.environ.get('" << variable.name << "', '')";
+ buffer << "\"${" << variable.name << "}\"";
}
+ buffer << " ";
+ }
+
+ if (ninja_executable.empty()) {
+ buffer << "ninja";
+ } else {
+ buffer << ninja_executable;
+ }
+
+ buffer << " -C " << build_dir;
+
+ if (!target_name.empty()) {
+ buffer << " '" << target_name << "'";
}
return buffer.str();
}
-std::string GetBuildScript(const std::string& target_name,
+std::string GetBuildScript(const Label& target_label,
const std::string& ninja_executable,
- const std::string& root_src_dir,
+ const std::string& build_dir,
base::Environment* environment) {
- std::string environ_script = ComputeScriptEnviron(environment);
- std::string ninja = GetNinjaExecutable(ninja_executable);
- return base::StringPrintf(kBuildScriptTemplate, root_src_dir.c_str(),
- target_name.c_str(), ninja.c_str(),
- environ_script.c_str());
+ std::string target_name = target_label.GetUserVisibleName(false);
+ base::TrimString(target_name, "/", &target_name);
+ return GetBuildScript(target_name, ninja_executable, build_dir, environment);
}
bool IsApplicationTarget(const Target* target) {
@@ -241,6 +205,49 @@ void AddPBXTargetDependency(const PBXTarget* base_pbxtarget,
dependent_pbxtarget->AddDependency(std::move(dependency));
}
+// Returns a SourceFile for absolute path `file_path` below `//`.
+SourceFile FilePathToSourceFile(const BuildSettings* build_settings,
+ const base::FilePath& file_path) {
+ const std::string file_path_utf8 = FilePathToUTF8(file_path);
+ return SourceFile("//" + file_path_utf8.substr(
+ build_settings->root_path_utf8().size() + 1));
+}
+
+// Returns the list of patterns to use when looking for additional files
+// from `options`.
+std::vector<base::FilePath::StringType> GetAdditionalFilesPatterns(
+ const XcodeWriter::Options& options) {
+ return base::SplitString(options.additional_files_patterns,
+ FILE_PATH_LITERAL(";"), base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
+}
+
+// Returns the list of roots to use when looking for additional files
+// from `options`.
+std::vector<base::FilePath> GetAdditionalFilesRoots(
+ const BuildSettings* build_settings,
+ const XcodeWriter::Options& options) {
+ if (options.additional_files_roots.empty()) {
+ return {build_settings->root_path()};
+ }
+
+ const std::vector<base::FilePath::StringType> roots =
+ base::SplitString(options.additional_files_roots, FILE_PATH_LITERAL(";"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ std::vector<base::FilePath> root_paths;
+ for (const base::FilePath::StringType& root : roots) {
+ const std::string rebased_root =
+ RebasePath(FilePathToUTF8(root), SourceDir("//"),
+ build_settings->root_path_utf8());
+
+ root_paths.push_back(
+ build_settings->root_path().Append(UTF8ToFilePath(rebased_root)));
+ }
+
+ return root_paths;
+}
+
// Helper class to resolve list of XCTest files per target.
//
// Uses a cache of file found per intermediate targets to reduce the need
@@ -373,21 +380,17 @@ void RecursivelyAssignIds(PBXProject* project) {
project->Visit(visitor);
}
-// Returns a configuration name derived from the build directory. This gives
-// standard names if using the Xcode convention of naming the build directory
-// out/$configuration-$platform (e.g. out/Debug-iphonesimulator).
-std::string ConfigNameFromBuildSettings(const BuildSettings* build_settings) {
- std::string config_name = FilePathToUTF8(build_settings->build_dir()
- .Resolve(base::FilePath())
- .StripTrailingSeparators()
- .BaseName());
-
- std::string::size_type separator = config_name.find('-');
- if (separator != std::string::npos)
- config_name = config_name.substr(0, separator);
-
- DCHECK(!config_name.empty());
- return config_name;
+// Returns a list of configuration names from the options passed to the
+// generator. If no configuration names have been passed, return default
+// value.
+std::vector<std::string> ConfigListFromOptions(const std::string& configs) {
+ std::vector<std::string> result = base::SplitString(
+ configs, ";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ if (result.empty())
+ result.push_back(std::string("Release"));
+
+ return result;
}
// Returns the path to root_src_dir from settings.
@@ -600,6 +603,10 @@ class XcodeProject {
const std::map<const Target*, PBXNativeTarget*>& bundle_targets,
Err* err);
+ // Tweak `output_dir` to be relative to the configuration specific output
+ // directory (see --xcode-config-build-dir=... flag).
+ std::string GetConfigOutputDir(std::string_view output_dir);
+
// Generates the content of the .xcodeproj file into |out|.
void WriteFileContent(std::ostream& out) const;
@@ -616,7 +623,7 @@ XcodeProject::XcodeProject(const BuildSettings* build_settings,
: build_settings_(build_settings),
options_(options),
project_(options.project_name,
- ConfigNameFromBuildSettings(build_settings),
+ ConfigListFromOptions(options.configurations),
SourcePathFromBuildSettings(build_settings),
ProjectAttributesFromBuildSettings(build_settings)) {}
@@ -685,14 +692,34 @@ bool XcodeProject::AddSourcesFromBuilder(const Builder& builder, Err* err) {
if (!build_settings_->root_path().IsParent(path))
continue;
- const std::string as8bit = path.As8Bit();
- const SourceFile source(
- "//" + as8bit.substr(build_settings_->root_path().value().size() + 1));
-
+ const SourceFile source = FilePathToSourceFile(build_settings_, path);
if (ShouldIncludeFileInProject(source))
sources.insert(source);
}
+ // Add any files from --xcode-additional-files-patterns, using the root
+ // listed in --xcode-additional-files-roots.
+ if (!options_.additional_files_patterns.empty()) {
+ const std::vector<base::FilePath::StringType> patterns =
+ GetAdditionalFilesPatterns(options_);
+ const std::vector<base::FilePath> roots =
+ GetAdditionalFilesRoots(build_settings_, options_);
+
+ for (const base::FilePath& root : roots) {
+ for (const base::FilePath::StringType& pattern : patterns) {
+ base::FileEnumerator it(root, /*recursive*/ true,
+ base::FileEnumerator::FILES, pattern,
+ base::FileEnumerator::FolderSearchPolicy::ALL);
+
+ for (base::FilePath path = it.Next(); !path.empty(); path = it.Next()) {
+ const SourceFile source = FilePathToSourceFile(build_settings_, path);
+ if (ShouldIncludeFileInProject(source))
+ sources.insert(source);
+ }
+ }
+ }
+ }
+
// Sort files to ensure deterministic generation of the project file (and
// nicely sorted file list in Xcode).
std::vector<SourceFile> sorted_sources(sources.begin(), sources.end());
@@ -711,11 +738,10 @@ bool XcodeProject::AddSourcesFromBuilder(const Builder& builder, Err* err) {
bool XcodeProject::AddTargetsFromBuilder(const Builder& builder, Err* err) {
std::unique_ptr<base::Environment> env(base::Environment::Create());
- const std::string root_src_dir =
- RebasePath("//", build_settings_->build_dir());
- project_.AddAggregateTarget("All", GetBuildScript(options_.root_target_name,
- options_.ninja_executable,
- root_src_dir, env.get()));
+ project_.AddAggregateTarget(
+ "All", GetConfigOutputDir("."),
+ GetBuildScript(options_.root_target_name, options_.ninja_executable,
+ GetConfigOutputDir("."), env.get()));
const std::optional<std::vector<const Target*>> targets =
GetTargetsFromBuilder(builder, err);
@@ -946,15 +972,13 @@ PBXNativeTarget* XcodeProject::AddBinaryTarget(const Target* target,
output_dir = RebasePath(output_dir, build_settings_->build_dir());
}
- const std::string root_src_dir =
- RebasePath("//", build_settings_->build_dir());
return project_.AddNativeTarget(
target->label().name(), "compiled.mach-o.executable",
target->output_name().empty() ? target->label().name()
: target->output_name(),
- "com.apple.product-type.tool", output_dir,
- GetBuildScript(target->label().name(), options_.ninja_executable,
- root_src_dir, env));
+ "com.apple.product-type.tool", GetConfigOutputDir(output_dir),
+ GetBuildScript(target->label(), options_.ninja_executable,
+ GetConfigOutputDir("."), env));
}
PBXNativeTarget* XcodeProject::AddBundleTarget(const Target* target,
@@ -978,19 +1002,33 @@ PBXNativeTarget* XcodeProject::AddBundleTarget(const Target* target,
const std::string& target_output_name = RebasePath(
target->bundle_data().GetBundleRootDirOutput(target->settings()).value(),
build_settings_->build_dir());
+
const std::string output_dir =
RebasePath(target->bundle_data().GetBundleDir(target->settings()).value(),
build_settings_->build_dir());
- const std::string root_src_dir =
- RebasePath("//", build_settings_->build_dir());
+
return project_.AddNativeTarget(
pbxtarget_name, std::string(), target_output_name,
- target->bundle_data().product_type(), output_dir,
- GetBuildScript(pbxtarget_name, options_.ninja_executable, root_src_dir,
- env),
+ target->bundle_data().product_type(), GetConfigOutputDir(output_dir),
+ GetBuildScript(target->label(), options_.ninja_executable,
+ GetConfigOutputDir("."), env),
xcode_extra_attributes);
}
+std::string XcodeProject::GetConfigOutputDir(std::string_view output_dir) {
+ if (options_.configuration_build_dir.empty())
+ return std::string(output_dir);
+
+ base::FilePath config_output_dir(options_.configuration_build_dir);
+ if (output_dir != ".") {
+ config_output_dir = config_output_dir.Append(UTF8ToFilePath(output_dir));
+ }
+
+ return RebasePath(FilePathToUTF8(config_output_dir.StripTrailingSeparators()),
+ build_settings_->build_dir(),
+ build_settings_->root_path_utf8());
+}
+
void XcodeProject::WriteFileContent(std::ostream& out) const {
out << "// !$*UTF8*$!\n"
<< "{\n"
diff --git a/gn/src/gn/xcode_writer.h b/gn/src/gn/xcode_writer.h
index 96ed14fe876..9c960e5ec70 100644
--- a/gn/src/gn/xcode_writer.h
+++ b/gn/src/gn/xcode_writer.h
@@ -11,6 +11,8 @@
#include <string>
#include <vector>
+#include "base/files/file_path.h"
+
class Builder;
class BuildSettings;
class Err;
@@ -42,6 +44,30 @@ class XcodeWriter {
// files for those target will still be listed in the generated project).
std::string dir_filters_string;
+ // If specified, should be a semicolon-separated list of configuration
+ // names. It will be used to generate all the configuration variations
+ // in the project. If empty, the project is assumed to only use a single
+ // configuration "Release".
+ std::string configurations;
+
+ // If specified, should be the path for the configuration's build
+ // directory. It can use Xcode variables such as ${CONFIGURATION} or
+ // ${EFFECTIVE_PLATFORM_NAME}. If empty, it is assumed to be the same
+ // as the project directory.
+ base::FilePath configuration_build_dir;
+
+ // If specified, should be a semicolon-separated list of file patterns.
+ // It will be used to add files to the project that are not referenced
+ // from the BUILD.gn files. This is usually used to add documentation
+ // files.
+ base::FilePath::StringType additional_files_patterns;
+
+ // If specified, should be a semicolon-separated list of file roots.
+ // It will be used to add files to the project that are not referenced
+ // from the BUILD.gn files. This is usually used to add documentation
+ // files.
+ base::FilePath::StringType additional_files_roots;
+
// Control which version of the build system should be used for the
// generated Xcode project.
XcodeBuildSystem build_system = XcodeBuildSystem::kLegacy;
diff --git a/gn/src/util/atomic_write.cc b/gn/src/util/atomic_write.cc
new file mode 100644
index 00000000000..51130d33273
--- /dev/null
+++ b/gn/src/util/atomic_write.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "util/atomic_write.h"
+
+#include "base/files/file_util.h"
+
+namespace util {
+
+int WriteFileAtomically(const base::FilePath& filename,
+ const char* data,
+ int size) {
+ base::FilePath dir = filename.DirName();
+ base::FilePath temp_file_path;
+
+ {
+ base::File temp_file =
+ base::CreateAndOpenTemporaryFileInDir(dir, &temp_file_path);
+ if (!temp_file.IsValid()) {
+ return -1;
+ }
+ if (temp_file.WriteAtCurrentPos(data, size) != size) {
+ return -1;
+ }
+ }
+
+ if (!base::ReplaceFile(temp_file_path, filename, NULL)) {
+ return -1;
+ }
+ return size;
+}
+
+} // namespace util
diff --git a/gn/src/util/atomic_write.h b/gn/src/util/atomic_write.h
new file mode 100644
index 00000000000..232917ac48e
--- /dev/null
+++ b/gn/src/util/atomic_write.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TOOLS_GN_ATOMIC_WRITE_H_
+#define TOOLS_GN_ATOMIC_WRITE_H_
+
+#include "base/files/file_path.h"
+
+namespace util {
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there. The write is performed atomically by first writing the
+// contents to a temporary file and then moving it into place. Returns the
+// number of bytes written, or -1 on error.
+int WriteFileAtomically(const base::FilePath& filename,
+ const char* data,
+ int size);
+
+} // namespace util
+
+#endif // TOOLS_GN_ATOMIC_WRITE_H_
diff --git a/gn/src/util/atomic_write_unittest.cc b/gn/src/util/atomic_write_unittest.cc
new file mode 100644
index 00000000000..c1d3372ae0c
--- /dev/null
+++ b/gn/src/util/atomic_write_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "util/atomic_write.h"
+
+#include <string>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "util/test/test.h"
+
+class ImportantFileWriterTest : public testing::Test {
+ public:
+ ImportantFileWriterTest() = default;
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ file_ = temp_dir_.GetPath().AppendASCII("test-file");
+ }
+
+ protected:
+ base::FilePath file_;
+
+ private:
+ base::ScopedTempDir temp_dir_;
+};
+
+// Test that WriteFileAtomically works.
+TEST_F(ImportantFileWriterTest, Basic) {
+ const std::string data = "Test string for writing.";
+ EXPECT_FALSE(base::PathExists(file_));
+ EXPECT_TRUE(util::WriteFileAtomically(file_, data.data(), data.size()));
+ std::string actual;
+ EXPECT_TRUE(ReadFileToString(file_, &actual));
+ EXPECT_EQ(data, actual);
+}
diff --git a/gn/src/util/worker_pool.cc b/gn/src/util/worker_pool.cc
index 9f6a47b38af..f594699ddd0 100644
--- a/gn/src/util/worker_pool.cc
+++ b/gn/src/util/worker_pool.cc
@@ -53,7 +53,7 @@ void ProcessorGroupSetter::SetProcessorGroup(std::thread* thread) {
int GetThreadCount() {
std::string thread_count =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
switches::kThreads);
// See if an override was specified on the command line.
diff --git a/gn/tools/update_reference.sh b/gn/tools/update_reference.sh
new file mode 100755
index 00000000000..50427e1157d
--- /dev/null
+++ b/gn/tools/update_reference.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Check for the existance of the AUTHORS file as an easy way to determine if
+# it's being run from the correct directory.
+if test -f "AUTHORS"; then
+ echo Building gn...
+ ninja -C out gn
+ echo Generating new docs/reference.md...
+ out/gn help --markdown all > docs/reference.md
+else
+ echo Please run this command from the GN checkout root directory.
+fi