diff options
author | Michal Klocek <michal.klocek@qt.io> | 2023-06-03 07:33:02 +0200 |
---|---|---|
committer | Michal Klocek <michal.klocek@qt.io> | 2023-07-06 14:33:05 +0000 |
commit | 266cfa0fb83513250bcefd8234e0916c195a4b2e (patch) | |
tree | 762d08bbe1280c7e4b6324dbb53aae04477faa5a | |
parent | b5eefeb8ec4af885f132e9bb71e64192d719309b (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>
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 <anything>** @@ -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) — 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 ✅) — 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) — 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 ✅) — 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]):**   **@property**<br>— **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. -— **def [RunSteps](/infra/recipes/gn.py#102)(api, repository):** +— **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 + — **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 + — **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 + — **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 |