summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gn/AUTHORS2
-rw-r--r--gn/README.md31
-rw-r--r--gn/base/containers/flat_set.h141
-rwxr-xr-xgn/build/gen.py35
-rw-r--r--gn/docs/cross_compiles.md2
-rw-r--r--gn/docs/language.md6
-rw-r--r--gn/docs/quick_start.md17
-rw-r--r--gn/docs/reference.md450
-rw-r--r--gn/docs/style_guide.md2
-rw-r--r--gn/examples/rust_example/.gn1
-rw-r--r--gn/examples/rust_example/BUILD.gn5
-rw-r--r--gn/examples/rust_example/BUILDCONFIG.gn23
-rw-r--r--gn/examples/rust_example/README.txt (renamed from gn/tools/gn/example/README.txt)2
-rw-r--r--gn/examples/rust_example/build/BUILD.gn27
-rw-r--r--gn/examples/rust_example/hello_world/bar/src/BUILD.gn4
-rw-r--r--gn/examples/rust_example/hello_world/bar/src/lib.rs23
-rw-r--r--gn/examples/rust_example/hello_world/foo/src/BUILD.gn9
-rw-r--r--gn/examples/rust_example/hello_world/foo/src/lib.rs11
-rw-r--r--gn/examples/rust_example/hello_world/src/BUILD.gn12
-rw-r--r--gn/examples/rust_example/hello_world/src/main.rs5
-rw-r--r--gn/examples/simple_build/.gn (renamed from gn/tools/gn/example/.gn)0
-rw-r--r--gn/examples/simple_build/BUILD.gn (renamed from gn/tools/gn/example/BUILD.gn)0
-rw-r--r--gn/examples/simple_build/README.md12
-rw-r--r--gn/examples/simple_build/build/BUILD.gn (renamed from gn/tools/gn/example/build/BUILD.gn)0
-rw-r--r--gn/examples/simple_build/build/BUILDCONFIG.gn (renamed from gn/tools/gn/example/build/BUILDCONFIG.gn)0
-rw-r--r--gn/examples/simple_build/build/toolchain/BUILD.gn (renamed from gn/tools/gn/example/build/toolchain/BUILD.gn)0
-rw-r--r--gn/examples/simple_build/hello.cc (renamed from gn/tools/gn/example/hello.cc)0
-rw-r--r--gn/examples/simple_build/hello_shared.cc (renamed from gn/tools/gn/example/hello_shared.cc)0
-rw-r--r--gn/examples/simple_build/hello_shared.h (renamed from gn/tools/gn/example/hello_shared.h)0
-rw-r--r--gn/examples/simple_build/hello_static.cc (renamed from gn/tools/gn/example/hello_static.cc)0
-rw-r--r--gn/examples/simple_build/hello_static.h (renamed from gn/tools/gn/example/hello_static.h)0
-rw-r--r--gn/infra/README.recipes.md26
-rw-r--r--gn/infra/config/cq.cfg27
-rw-r--r--gn/infra/config/recipes.cfg2
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json5
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json7
-rw-r--r--gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json5
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json5
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json5
-rw-r--r--gn/infra/recipe_modules/windows_sdk/examples/full.expected/win.json5
-rwxr-xr-xgn/infra/recipes.py64
-rw-r--r--gn/infra/recipes/gn.expected/ci_linux.json21
-rw-r--r--gn/infra/recipes/gn.expected/ci_mac.json29
-rw-r--r--gn/infra/recipes/gn.expected/ci_win.json45
-rw-r--r--gn/infra/recipes/gn.expected/cipd_exists.json19
-rw-r--r--gn/infra/recipes/gn.expected/cipd_register.json21
-rw-r--r--gn/infra/recipes/gn.expected/cq_linux.json23
-rw-r--r--gn/infra/recipes/gn.expected/cq_mac.json31
-rw-r--r--gn/infra/recipes/gn.expected/cq_win.json47
-rw-r--r--gn/infra/recipes/gn.py32
-rw-r--r--gn/tools/gn/action_target_generator.cc2
-rw-r--r--gn/tools/gn/analyzer.cc7
-rw-r--r--gn/tools/gn/analyzer_unittest.cc9
-rw-r--r--gn/tools/gn/args.cc18
-rw-r--r--gn/tools/gn/binary_target_generator.cc74
-rw-r--r--gn/tools/gn/binary_target_generator.h3
-rw-r--r--gn/tools/gn/build_settings.cc2
-rw-r--r--gn/tools/gn/build_settings.h1
-rw-r--r--gn/tools/gn/builder.cc27
-rw-r--r--gn/tools/gn/builder_record.cc8
-rw-r--r--gn/tools/gn/builder_record.h7
-rw-r--r--gn/tools/gn/bundle_data.cc8
-rw-r--r--gn/tools/gn/bundle_data_target_generator.cc4
-rw-r--r--gn/tools/gn/bundle_file_rule.cc77
-rw-r--r--gn/tools/gn/c_include_iterator.cc6
-rw-r--r--gn/tools/gn/c_include_iterator.h6
-rw-r--r--gn/tools/gn/c_substitution_type.cc85
-rw-r--r--gn/tools/gn/c_substitution_type.h44
-rw-r--r--gn/tools/gn/c_tool.cc232
-rw-r--r--gn/tools/gn/c_tool.h137
-rw-r--r--gn/tools/gn/command_desc.cc24
-rw-r--r--gn/tools/gn/command_format.cc9
-rw-r--r--gn/tools/gn/command_help.cc17
-rw-r--r--gn/tools/gn/command_meta.cc9
-rw-r--r--gn/tools/gn/commands.cc8
-rw-r--r--gn/tools/gn/compile_commands_writer.cc149
-rw-r--r--gn/tools/gn/compile_commands_writer_unittest.cc36
-rw-r--r--gn/tools/gn/config.cc2
-rw-r--r--gn/tools/gn/config.h2
-rw-r--r--gn/tools/gn/config_values.cc2
-rw-r--r--gn/tools/gn/config_values.h4
-rw-r--r--gn/tools/gn/config_values_extractors.h5
-rw-r--r--gn/tools/gn/config_values_generator.cc2
-rw-r--r--gn/tools/gn/config_values_generator.h3
-rw-r--r--gn/tools/gn/create_bundle_target_generator.cc7
-rw-r--r--gn/tools/gn/desc_builder.cc15
-rw-r--r--gn/tools/gn/escape.cc20
-rw-r--r--gn/tools/gn/escape.h18
-rw-r--r--gn/tools/gn/escape_unittest.cc7
-rw-r--r--gn/tools/gn/exec_process_unittest.cc25
-rw-r--r--gn/tools/gn/filesystem_utils.cc83
-rw-r--r--gn/tools/gn/filesystem_utils_unittest.cc18
-rw-r--r--gn/tools/gn/function_exec_script.cc2
-rw-r--r--gn/tools/gn/function_get_target_outputs.cc27
-rw-r--r--gn/tools/gn/function_process_file_template.cc2
-rw-r--r--gn/tools/gn/function_read_file.cc2
-rw-r--r--gn/tools/gn/function_rebase_path_unittest.cc3
-rw-r--r--gn/tools/gn/function_toolchain.cc428
-rw-r--r--gn/tools/gn/function_toolchain_unittest.cc61
-rw-r--r--gn/tools/gn/function_write_file.cc2
-rw-r--r--gn/tools/gn/functions.cc5
-rw-r--r--gn/tools/gn/functions.h9
-rw-r--r--gn/tools/gn/functions_target.cc81
-rw-r--r--gn/tools/gn/functions_target_rust_unittest.cc373
-rw-r--r--gn/tools/gn/functions_target_unittest.cc20
-rw-r--r--gn/tools/gn/general_tool.cc51
-rw-r--r--gn/tools/gn/general_tool.h46
-rw-r--r--gn/tools/gn/generated_file_target_generator.cc5
-rw-r--r--gn/tools/gn/generated_file_target_generator.h4
-rw-r--r--gn/tools/gn/header_checker.cc8
-rw-r--r--gn/tools/gn/import_manager.cc3
-rw-r--r--gn/tools/gn/input_file.cc2
-rw-r--r--gn/tools/gn/input_file.h2
-rw-r--r--gn/tools/gn/input_file_manager.cc4
-rw-r--r--gn/tools/gn/input_file_manager.h6
-rw-r--r--gn/tools/gn/json_project_writer.cc86
-rw-r--r--gn/tools/gn/json_project_writer.h7
-rw-r--r--gn/tools/gn/json_project_writer_unittest.cc137
-rw-r--r--gn/tools/gn/label.cc6
-rw-r--r--gn/tools/gn/label.h25
-rw-r--r--gn/tools/gn/lib_file.cc7
-rw-r--r--gn/tools/gn/lib_file.h8
-rw-r--r--gn/tools/gn/loader.cc15
-rw-r--r--gn/tools/gn/location.cc2
-rw-r--r--gn/tools/gn/location.h8
-rw-r--r--gn/tools/gn/metadata.cc220
-rw-r--r--gn/tools/gn/metadata.h27
-rw-r--r--gn/tools/gn/metadata_unittest.cc49
-rw-r--r--gn/tools/gn/misc/vim/gn-format.py2
-rw-r--r--gn/tools/gn/ninja_action_target_writer.cc3
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.cc792
-rw-r--r--gn/tools/gn/ninja_binary_target_writer.h95
-rw-r--r--gn/tools/gn/ninja_binary_target_writer_unittest.cc1089
-rw-r--r--gn/tools/gn/ninja_build_writer.cc16
-rw-r--r--gn/tools/gn/ninja_build_writer_unittest.cc31
-rw-r--r--gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc3
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer.cc714
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer.h107
-rw-r--r--gn/tools/gn/ninja_c_binary_target_writer_unittest.cc1187
-rw-r--r--gn/tools/gn/ninja_copy_target_writer.cc7
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer.cc30
-rw-r--r--gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc29
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer.cc247
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer.h37
-rw-r--r--gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc578
-rw-r--r--gn/tools/gn/ninja_target_command_util.cc80
-rw-r--r--gn/tools/gn/ninja_target_command_util.h10
-rw-r--r--gn/tools/gn/ninja_target_writer.cc37
-rw-r--r--gn/tools/gn/ninja_target_writer.h2
-rw-r--r--gn/tools/gn/ninja_toolchain_writer.cc56
-rw-r--r--gn/tools/gn/ninja_toolchain_writer.h9
-rw-r--r--gn/tools/gn/ninja_toolchain_writer_unittest.cc18
-rw-r--r--gn/tools/gn/operators.cc2
-rw-r--r--gn/tools/gn/ordered_set.h6
-rw-r--r--gn/tools/gn/output_file.cc11
-rw-r--r--gn/tools/gn/output_file.h9
-rw-r--r--gn/tools/gn/rust_substitution_type.cc48
-rw-r--r--gn/tools/gn/rust_substitution_type.h29
-rw-r--r--gn/tools/gn/rust_tool.cc156
-rw-r--r--gn/tools/gn/rust_tool.h98
-rw-r--r--gn/tools/gn/rust_values.cc9
-rw-r--r--gn/tools/gn/rust_values.h67
-rw-r--r--gn/tools/gn/rust_values_generator.cc206
-rw-r--r--gn/tools/gn/rust_values_generator.h39
-rw-r--r--gn/tools/gn/rust_variables.cc129
-rw-r--r--gn/tools/gn/rust_variables.h38
-rw-r--r--gn/tools/gn/scheduler.cc9
-rw-r--r--gn/tools/gn/scheduler.h10
-rw-r--r--gn/tools/gn/scope.h4
-rw-r--r--gn/tools/gn/settings.cc7
-rw-r--r--gn/tools/gn/settings.h3
-rw-r--r--gn/tools/gn/setup.cc19
-rw-r--r--gn/tools/gn/setup.h9
-rw-r--r--gn/tools/gn/source_dir.cc22
-rw-r--r--gn/tools/gn/source_dir.h18
-rw-r--r--gn/tools/gn/source_file.cc76
-rw-r--r--gn/tools/gn/source_file.h72
-rw-r--r--gn/tools/gn/source_file_type.cc33
-rw-r--r--gn/tools/gn/source_file_type.h31
-rw-r--r--gn/tools/gn/source_file_unittest.cc3
-rw-r--r--gn/tools/gn/substitution_list.h4
-rw-r--r--gn/tools/gn/substitution_pattern.cc37
-rw-r--r--gn/tools/gn/substitution_pattern.h12
-rw-r--r--gn/tools/gn/substitution_pattern_unittest.cc36
-rw-r--r--gn/tools/gn/substitution_type.cc345
-rw-r--r--gn/tools/gn/substitution_type.h165
-rw-r--r--gn/tools/gn/substitution_writer.cc268
-rw-r--r--gn/tools/gn/substitution_writer.h12
-rw-r--r--gn/tools/gn/substitution_writer_unittest.cc100
-rw-r--r--gn/tools/gn/target.cc90
-rw-r--r--gn/tools/gn/target.h32
-rw-r--r--gn/tools/gn/target_generator.cc20
-rw-r--r--gn/tools/gn/target_generator.h3
-rw-r--r--gn/tools/gn/target_unittest.cc10
-rw-r--r--gn/tools/gn/test_with_scope.cc87
-rw-r--r--gn/tools/gn/test_with_scope.h3
-rw-r--r--gn/tools/gn/tokenizer.cc7
-rw-r--r--gn/tools/gn/tokenizer.h6
-rw-r--r--gn/tools/gn/tool.cc334
-rw-r--r--gn/tools/gn/tool.h165
-rw-r--r--gn/tools/gn/toolchain.cc248
-rw-r--r--gn/tools/gn/toolchain.h73
-rw-r--r--gn/tools/gn/trace.cc8
-rw-r--r--gn/tools/gn/unique_vector.h39
-rw-r--r--gn/tools/gn/unique_vector_unittest.cc9
-rw-r--r--gn/tools/gn/variables.cc15
-rw-r--r--gn/tools/gn/visual_studio_writer.cc10
-rw-r--r--gn/tools/gn/visual_studio_writer_unittest.cc4
-rw-r--r--gn/util/exe_path.cc1
-rw-r--r--gn/util/sys_info.cc4
-rw-r--r--gn/util/worker_pool.cc55
211 files changed, 8521 insertions, 4227 deletions
diff --git a/gn/AUTHORS b/gn/AUTHORS
index fe152fd3877..17b4959f9ce 100644
--- a/gn/AUTHORS
+++ b/gn/AUTHORS
@@ -12,6 +12,7 @@ Google Inc. <*@google.com>
IBM Inc. <*@*.ibm.com>
Loongson Technology Corporation Limited. <*@loongson.cn>
MIPS Technologies, Inc. <*@mips.com>
+NVIDIA Corporation <*@nvidia.com>
Opera Software ASA <*@opera.com>
The Chromium Authors <*@chromium.org>
Vewd Software AS <*@vewd.com>
@@ -19,6 +20,7 @@ Vivaldi Technologies AS <*@vivaldi.com>
Yandex LLC <*@yandex-team.ru>
Alexis Menard <alexis.menard@intel.com>
+Alfredo Mazzinghi <mzz.lrd@gmail.com>
Andrew Boyarshin <andrew.boyarshin@gmail.com>
Anuj Kumar Sharma <anujk.sharma@samsung.com>
DanCraft99 <simputest@gmail.com>
diff --git a/gn/README.md b/gn/README.md
index a370e008839..a8d9fa3724f 100644
--- a/gn/README.md
+++ b/gn/README.md
@@ -1,11 +1,15 @@
# GN
GN is a meta-build system that generates build files for
-[Ninja](https://ninja-build.org). There is documentation in
-[docs/](https://gn.googlesource.com/gn/+/master/docs/) and
-[a presentation on it](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing).
+[Ninja](https://ninja-build.org).
-## Getting started
+Related resources:
+
+ * Documentation in [docs/](https://gn.googlesource.com/gn/+/master/docs/).
+ * An introductory [presentation](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing).
+ * The [mailing list](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev).
+
+## Getting a binary
You can download the latest version of GN binary for
[Linux](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest),
@@ -29,6 +33,23 @@ On Linux and Mac, 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`.
+## Examples
+
+There is a simple example in [examples/simple_build](examples/simple_build)
+directory that is a good place to get started with the minimal configuration.
+
+For a maximal configuration see the Chromium setup:
+ * [.gn](https://cs.chromium.org/chromium/src/.gn)
+ * [BUILDCONFIG.gn](https://cs.chromium.org/chromium/src/build/config/BUILDCONFIG.gn)
+ * [Toolchain setup](https://cs.chromium.org/chromium/src/build/toolchain/)
+ * [Compiler setup](https://cs.chromium.org/chromium/src/build/config/compiler/BUILD.gn)
+
+and the Fuchsia setup:
+ * [.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/.gn)
+ * [BUILDCONFIG.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/config/BUILDCONFIG.gn)
+ * [Toolchain setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/toolchain/)
+ * [Compiler setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/master/build/config/BUILD.gn)
+
## Reporting bugs
If you find a bug, you can see if it is known or report it in the [bug
@@ -64,6 +85,6 @@ project').
## Community
-You may ask questions and follow along w/ GN's development on Chromium's
+You may ask questions and follow along with GN's development on Chromium's
[gn-dev@](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev)
Google Group.
diff --git a/gn/base/containers/flat_set.h b/gn/base/containers/flat_set.h
new file mode 100644
index 00000000000..700617f2825
--- /dev/null
+++ b/gn/base/containers/flat_set.h
@@ -0,0 +1,141 @@
+// Copyright 2017 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 BASE_CONTAINERS_FLAT_SET_H_
+#define BASE_CONTAINERS_FLAT_SET_H_
+
+#include <functional>
+
+#include "base/containers/flat_tree.h"
+#include "base/template_util.h"
+
+namespace base {
+
+// flat_set is a container with a std::set-like interface that stores its
+// contents in a sorted vector.
+//
+// Please see //base/containers/README.md for an overview of which container
+// to select.
+//
+// PROS
+//
+// - Good memory locality.
+// - Low overhead, especially for smaller sets.
+// - Performance is good for more workloads than you might expect (see
+// overview link above).
+// - Supports C++14 set interface.
+//
+// CONS
+//
+// - Inserts and removals are O(n).
+//
+// IMPORTANT NOTES
+//
+// - Iterators are invalidated across mutations.
+// - If possible, construct a flat_set in one operation by inserting into
+// a std::vector and moving that vector into the flat_set constructor.
+// - For multiple removals use base::EraseIf() which is O(n) rather than
+// O(n * removed_items).
+//
+// QUICK REFERENCE
+//
+// Most of the core functionality is inherited from flat_tree. Please see
+// flat_tree.h for more details for most of these functions. As a quick
+// reference, the functions available are:
+//
+// Constructors (inputs need not be sorted):
+// flat_set(InputIterator first, InputIterator last,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& compare = Compare());
+// flat_set(const flat_set&);
+// flat_set(flat_set&&);
+// flat_set(std::vector<Key>,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& compare = Compare()); // Re-use storage.
+// flat_set(std::initializer_list<value_type> ilist,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
+// const Compare& comp = Compare());
+//
+// Assignment functions:
+// flat_set& operator=(const flat_set&);
+// flat_set& operator=(flat_set&&);
+// flat_set& operator=(initializer_list<Key>);
+//
+// Memory management functions:
+// void reserve(size_t);
+// size_t capacity() const;
+// void shrink_to_fit();
+//
+// Size management functions:
+// void clear();
+// size_t size() const;
+// size_t max_size() const;
+// bool empty() const;
+//
+// Iterator functions:
+// iterator begin();
+// const_iterator begin() const;
+// const_iterator cbegin() const;
+// iterator end();
+// const_iterator end() const;
+// const_iterator cend() const;
+// reverse_iterator rbegin();
+// const reverse_iterator rbegin() const;
+// const_reverse_iterator crbegin() const;
+// reverse_iterator rend();
+// const_reverse_iterator rend() const;
+// const_reverse_iterator crend() const;
+//
+// Insert and accessor functions:
+// pair<iterator, bool> insert(const key_type&);
+// pair<iterator, bool> insert(key_type&&);
+// void insert(InputIterator first, InputIterator last,
+// FlatContainerDupes = KEEP_FIRST_OF_DUPES);
+// iterator insert(const_iterator hint, const key_type&);
+// iterator insert(const_iterator hint, key_type&&);
+// pair<iterator, bool> emplace(Args&&...);
+// iterator emplace_hint(const_iterator, Args&&...);
+//
+// Erase functions:
+// iterator erase(iterator);
+// iterator erase(const_iterator);
+// iterator erase(const_iterator first, const_iterator& last);
+// template <typename K> size_t erase(const K& key);
+//
+// Comparators (see std::set documentation).
+// key_compare key_comp() const;
+// value_compare value_comp() const;
+//
+// Search functions:
+// template <typename K> size_t count(const K&) const;
+// template <typename K> iterator find(const K&);
+// template <typename K> const_iterator find(const K&) const;
+// template <typename K> bool contains(const K&) const;
+// template <typename K> pair<iterator, iterator> equal_range(K&);
+// template <typename K> iterator lower_bound(const K&);
+// template <typename K> const_iterator lower_bound(const K&) const;
+// template <typename K> iterator upper_bound(const K&);
+// template <typename K> const_iterator upper_bound(const K&) const;
+//
+// General functions:
+// void swap(flat_set&&);
+//
+// Non-member operators:
+// bool operator==(const flat_set&, const flat_set);
+// bool operator!=(const flat_set&, const flat_set);
+// bool operator<(const flat_set&, const flat_set);
+// bool operator>(const flat_set&, const flat_set);
+// bool operator>=(const flat_set&, const flat_set);
+// bool operator<=(const flat_set&, const flat_set);
+//
+template <class Key, class Compare = std::less<>>
+using flat_set = typename ::base::internal::flat_tree<
+ Key,
+ Key,
+ ::base::internal::GetKeyFromValueIdentity<Key>,
+ Compare>;
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_FLAT_SET_H_ \ No newline at end of file
diff --git a/gn/build/gen.py b/gn/build/gen.py
index d547a8ff83e..38085b4d9db 100755
--- a/gn/build/gen.py
+++ b/gn/build/gen.py
@@ -46,7 +46,7 @@ class Platform(object):
@staticmethod
def known_platforms():
- return ['linux', 'darwin', 'msvc', 'aix', 'fuchsia', 'openbsd']
+ return ['linux', 'darwin', 'msvc', 'aix', 'fuchsia', 'freebsd', 'openbsd']
def platform(self):
return self._platform
@@ -95,6 +95,9 @@ def main(argv):
help='The path to generate the build files in.')
parser.add_option('--no-strip', action='store_true',
help='Don\'t strip release build. Useful for profiling.')
+ parser.add_option('--no-static-libstdc++', action='store_true',
+ default=False, dest='no_static_libstdcpp',
+ help='Don\'t link libstdc++ statically')
options, args = parser.parse_args(argv)
if args:
@@ -205,7 +208,9 @@ def WriteGenericNinja(path, static_libraries, executables,
'build %s: %s %s' % (src_to_obj(src_file),
settings['tool'],
escape_path_ninja(
- os.path.join(REPO_ROOT, src_file))),
+ os.path.relpath(
+ os.path.join(REPO_ROOT, src_file),
+ os.path.dirname(path)))),
' includes = %s' % ' '.join(
['-I' + escape_path_ninja(dirname) for dirname in
include_dirs + settings.get('include_dirs', [])]),
@@ -274,7 +279,7 @@ def WriteGNNinja(path, platform, host, options):
cflags_cc = os.environ.get('CXXFLAGS', '').split()
ldflags = os.environ.get('LDFLAGS', '').split()
libflags = os.environ.get('LIBFLAGS', '').split()
- include_dirs = [REPO_ROOT, os.path.dirname(path)]
+ include_dirs = [os.path.relpath(REPO_ROOT, os.path.dirname(path)), '.']
libs = []
if not platform.is_msvc():
@@ -322,10 +327,11 @@ def WriteGNNinja(path, platform, host, options):
cflags_cc.extend(['-std=c++14', '-Wno-c++11-narrowing'])
if platform.is_linux():
- ldflags.extend([
- '-static-libstdc++',
- '-Wl,--as-needed',
- ])
+ ldflags.append('-Wl,--as-needed')
+
+ if not options.no_static_libstdcpp:
+ ldflags.append('-static-libstdc++')
+
# This is needed by libc++.
libs.append('-ldl')
elif platform.is_darwin():
@@ -425,6 +431,8 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/bundle_data_target_generator.cc',
'tools/gn/bundle_file_rule.cc',
'tools/gn/c_include_iterator.cc',
+ 'tools/gn/c_substitution_type.cc',
+ 'tools/gn/c_tool.cc',
'tools/gn/command_analyze.cc',
'tools/gn/command_args.cc',
'tools/gn/command_check.cc',
@@ -468,6 +476,7 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/function_template.cc',
'tools/gn/function_toolchain.cc',
'tools/gn/function_write_file.cc',
+ 'tools/gn/general_tool.cc',
'tools/gn/generated_file_target_generator.cc',
'tools/gn/group_target_generator.cc',
'tools/gn/header_checker.cc',
@@ -489,10 +498,12 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/ninja_binary_target_writer.cc',
'tools/gn/ninja_build_writer.cc',
'tools/gn/ninja_bundle_data_target_writer.cc',
+ 'tools/gn/ninja_c_binary_target_writer.cc',
'tools/gn/ninja_copy_target_writer.cc',
'tools/gn/ninja_create_bundle_target_writer.cc',
'tools/gn/ninja_generated_file_target_writer.cc',
'tools/gn/ninja_group_target_writer.cc',
+ 'tools/gn/ninja_rust_binary_target_writer.cc',
'tools/gn/ninja_target_command_util.cc',
'tools/gn/ninja_target_writer.cc',
'tools/gn/ninja_toolchain_writer.cc',
@@ -509,6 +520,11 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/pool.cc',
'tools/gn/qt_creator_writer.cc',
'tools/gn/runtime_deps.cc',
+ 'tools/gn/rust_substitution_type.cc',
+ 'tools/gn/rust_values_generator.cc',
+ 'tools/gn/rust_tool.cc',
+ 'tools/gn/rust_values.cc',
+ 'tools/gn/rust_variables.cc',
'tools/gn/scheduler.cc',
'tools/gn/scope.cc',
'tools/gn/scope_per_file_provider.cc',
@@ -516,7 +532,6 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/setup.cc',
'tools/gn/source_dir.cc',
'tools/gn/source_file.cc',
- 'tools/gn/source_file_type.cc',
'tools/gn/standard_out.cc',
'tools/gn/string_utils.cc',
'tools/gn/substitution_list.cc',
@@ -578,10 +593,12 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/function_toolchain_unittest.cc',
'tools/gn/function_write_file_unittest.cc',
'tools/gn/functions_target_unittest.cc',
+ 'tools/gn/functions_target_rust_unittest.cc',
'tools/gn/functions_unittest.cc',
'tools/gn/header_checker_unittest.cc',
'tools/gn/inherited_libraries_unittest.cc',
'tools/gn/input_conversion_unittest.cc',
+ 'tools/gn/json_project_writer_unittest.cc',
'tools/gn/label_pattern_unittest.cc',
'tools/gn/label_unittest.cc',
'tools/gn/loader_unittest.cc',
@@ -589,10 +606,12 @@ def WriteGNNinja(path, platform, host, options):
'tools/gn/metadata_walk_unittest.cc',
'tools/gn/ninja_action_target_writer_unittest.cc',
'tools/gn/ninja_binary_target_writer_unittest.cc',
+ 'tools/gn/ninja_c_binary_target_writer_unittest.cc',
'tools/gn/ninja_build_writer_unittest.cc',
'tools/gn/ninja_bundle_data_target_writer_unittest.cc',
'tools/gn/ninja_copy_target_writer_unittest.cc',
'tools/gn/ninja_create_bundle_target_writer_unittest.cc',
+ 'tools/gn/ninja_rust_binary_target_writer_unittest.cc',
'tools/gn/ninja_generated_file_target_writer_unittest.cc',
'tools/gn/ninja_group_target_writer_unittest.cc',
'tools/gn/ninja_target_writer_unittest.cc',
diff --git a/gn/docs/cross_compiles.md b/gn/docs/cross_compiles.md
index 6da66543102..01de4567188 100644
--- a/gn/docs/cross_compiles.md
+++ b/gn/docs/cross_compiles.md
@@ -13,7 +13,7 @@ the platform that the build is run on, and the target is the platform
where the code will actually run (This is different from
[autotools](http://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html)'
terminology, but uses the more common terminology for cross
-compiling**).**
+compiling).
(Confusingly, GN also refers to each build artifact -- an executable,
library, etc. -- as a target. On this page, we will use "target" only to
diff --git a/gn/docs/language.md b/gn/docs/language.md
index dcc6c9ef4e9..16cb2a47ec7 100644
--- a/gn/docs/language.md
+++ b/gn/docs/language.md
@@ -95,9 +95,9 @@ You can encode 8-bit characters using "$0xFF" syntax, so a string with newlines
### Lists
-There is no way to get the length of a list. If you find yourself
-wanting to do this kind of thing, you're trying to do too much work in
-the build.
+Aside from telling empty lists from non empty lists (`a == []`), there is no
+way to get the length of a list. If you find yourself wanting to do this kind
+of thing, you're trying to do too much work in the build.
Lists support appending:
diff --git a/gn/docs/quick_start.md b/gn/docs/quick_start.md
index 629b36d62c9..d3941c54bf9 100644
--- a/gn/docs/quick_start.md
+++ b/gn/docs/quick_start.md
@@ -2,6 +2,10 @@
[TOC]
+Note: the gn repo has been ported from chromium, and the tutorial within this
+document has not yet been updated to reflect this. Building the tutorial files
+will require the chromium tree, even though gn is now independent of chromium.
+
## Running GN
You just run `gn` from the command line. There is a script in
@@ -94,7 +98,8 @@ is_component_build = true
### Adding a build file
-Create a `tools/gn/tutorial/BUILD.gn` file and enter the following:
+Create a `tools/gn/tutorial/BUILD.gn` file [in the chromium repo](https://cs.chromium.org/chromium/src/tools/gn/tutorial/) and enter
+the following:
```
executable("hello_world") {
@@ -134,9 +139,11 @@ ninja -C out/Default hello_world
out/Default/hello_world
```
-GN encourages target names for static libraries that aren't globally
-unique. To build one of these, you can pass the label with no leading
-"//" to ninja:
+You should see "Hello, world." output to the console.
+
+Side note: GN encourages target names for static libraries that aren't globally
+unique. To build one of these, you can pass the label with its path (but no leading
+"//") to ninja:
```
ninja -C out/Default tools/gn/tutorial:hello_world
@@ -352,7 +359,7 @@ See `gn help desc` for more.
### Performance
-You can see what took a long time by running it with the --time command
+You can see what took a long time by running it with the `--time` command
line flag. This will output a summary of timings for various things.
You can also make a trace of how the build files were executed:
diff --git a/gn/docs/reference.md b/gn/docs/reference.md
index fe23b196b84..1601d01a5c2 100644
--- a/gn/docs/reference.md
+++ b/gn/docs/reference.md
@@ -27,6 +27,7 @@
* [generated_file: Declare a generated_file target.](#func_generated_file)
* [group: Declare a named group of targets.](#func_group)
* [loadable_module: Declare a loadable module target.](#func_loadable_module)
+ * [rust_library: Declare a Rust library target.](#func_rust_library)
* [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)
@@ -77,6 +78,7 @@
* [target_os: [string] The desired operating system for the build.](#var_target_os)
* [target_out_dir: [string] Directory for target output files.](#var_target_out_dir)
* [Variables you set in targets](#target_variables)
+ * [aliased_deps: [scope] Set of crate-dependency pairs.](#var_aliased_deps)
* [all_dependent_configs: [label list] Configs to be forced on dependents.](#var_all_dependent_configs)
* [allow_circular_includes_from: [label list] Permit includes from deps.](#var_allow_circular_includes_from)
* [arflags: [string list] Arguments passed to static_library archiver.](#var_arflags)
@@ -101,12 +103,16 @@
* [complete_static_lib: [boolean] Links all deps into a static library.](#var_complete_static_lib)
* [configs: [label list] Configs applying to this target or config.](#var_configs)
* [contents: Contents to write to file.](#var_contents)
+ * [crate_name: [string] The name for the compiled crate.](#var_crate_name)
+ * [crate_root: [string] The root source file for a binary or library.](#var_crate_root)
+ * [crate_type: [string] The type of linkage to use on a shared_library.](#var_crate_type)
* [data: [file list] Runtime data file dependencies.](#var_data)
* [data_deps: [label list] Non-linked dependencies.](#var_data_deps)
* [data_keys: [string list] Keys from which to collect metadata.](#var_data_keys)
* [defines: [string list] C preprocessor defines.](#var_defines)
* [depfile: [string] File name for input dependencies for actions.](#var_depfile)
* [deps: [label list] Private linked dependencies.](#var_deps)
+ * [edition: [string] The rustc edition to use in compiliation.](#var_edition)
* [friend: [label pattern list] Allow targets to include private headers.](#var_friend)
* [include_dirs: [directory list] Additional include directories.](#var_include_dirs)
* [inputs: [file list] Additional compile-time dependencies.](#var_inputs)
@@ -145,12 +151,13 @@
* [dotfile: Info about the toplevel .gn file.](#dotfile)
* [execution: Build graph and execution overview.](#execution)
* [grammar: Language and grammar for GN build files.](#grammar)
- * [input_conversion: Processing input from exec_script and read_file.](#input_conversion)
+ * [input_conversion: Processing input from exec_script and read_file.](#io_conversion)
* [label_pattern: Matching more than one label.](#label_pattern)
* [labels: About labels.](#labels)
+ * [metadata_collection: About metadata and its collection.](#metadata_collection)
* [ninja_rules: How Ninja build rules are named.](#ninja_rules)
* [nogncheck: Annotating includes for checking.](#nogncheck)
- * [output_conversion: Specifies how to transform a value to output.](#output_conversion)
+ * [output_conversion: Specifies how to transform a value to output.](#io_conversion)
* [runtime_deps: How runtime dependency computation works.](#runtime_deps)
* [source_expansion: Map sources to outputs for scripts.](#source_expansion)
* [switches: Show available command-line switches.](#switch_list)
@@ -778,12 +785,15 @@
#### **Compilation Database**
```
- --export-compile-commands
+ --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. This is
- used for various Clang-based tooling, allowing for the replay of individual
- compilations independent of the build system.
+ 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 the list
+ of 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.
```
### <a name="cmd_help"></a>**gn help &lt;anything&gt;**
@@ -1268,7 +1278,7 @@
script = "idl_processor.py"
sources = [ "foo.idl", "bar.idl" ]
- # Our script reads this file each time, so we need to list is as a
+ # Our script reads this file each time, so we need to list it as a
# dependency so we can rebuild if it changes.
inputs = [ "my_configuration.txt" ]
@@ -1529,17 +1539,29 @@
```
### <a name="func_executable"></a>**executable**: Declare an executable target.
+#### **Language and compilation**
+
+```
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ target containing both C and C++ sources is acceptable, but a
+ target containing C and Rust sources is not).
+```
+
#### **Variables**
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
+ Rust variables: aliased_deps, crate_root, crate_name, edition
```
### <a name="func_generated_file"></a>**generated_file**: Declare a generated_file target.
@@ -1711,17 +1733,61 @@
"shared_library" target type instead.
```
+#### **Language and compilation**
+
+```
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ target containing both C and C++ sources is acceptable, but a
+ target containing C and Rust sources is not).
+```
+
+#### **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
+ Deps: data_deps, deps, public_deps
+ Dependent configs: all_dependent_configs, public_configs
+ General: check_includes, configs, data, friend, inputs, metadata,
+ output_name, output_extension, public, sources, testonly,
+ visibility
+ Rust variables: aliased_deps, crate_root, crate_name, crate_type, edition
+```
+### <a name="func_rust_library"></a>**rust_library**: Declare a Rust library target.
+
+```
+ A Rust library is an archive containing additional rust-c provided metadata.
+ These are the files produced by the rustc compiler with the `.rlib`
+ extension, and are the intermediate step for most Rust-based binaries.
+```
+
+#### **Language and compilation**
+
+```
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ target containing both C and C++ sources is acceptable, but a
+ target containing C and Rust sources is not).
+```
+
#### **Variables**
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
+ Rust variables: aliased_deps, crate_root, crate_name, edition
```
### <a name="func_shared_library"></a>**shared_library**: Declare a shared library target.
@@ -1733,21 +1799,40 @@
instead.
```
+#### **Language and compilation**
+
+```
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ target containing both C and C++ sources is acceptable, but a
+ target containing C and Rust sources is not).
+```
+
#### **Variables**
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
+ Rust variables: aliased_deps, crate_root, crate_name, crate_type, edition
```
### <a name="func_source_set"></a>**source_set**: Declare a source set target.
```
+ The language of a source_set target is determined by the extensions present
+ in its sources.
+```
+
+#### **C-language source_sets**
+
+```
A source set is a collection of sources that get compiled, but are not linked
to produce any kind of library. Instead, the resulting object files are
implicitly added to the linker line of all targets that depend on the source
@@ -1770,12 +1855,22 @@
when linking multiple static libraries into a shared library.
```
+#### **Rust-language source_sets**
+
+```
+ A Rust source set is a collection of sources that get passed along to the
+ final target that depends on it. No compilation is performed, and the source
+ files are simply added as dependencies on the eventual rustc invocation that
+ would produce a binary.
+```
+
#### **Variables**
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
@@ -1798,12 +1893,20 @@
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
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Deps: data_deps, deps, public_deps
Dependent configs: all_dependent_configs, public_configs
General: check_includes, configs, data, friend, inputs, metadata,
output_name, output_extension, public, sources, testonly,
visibility
+ Rust variables: aliased_deps, crate_root, crate_name, edition
+
+ The tools and commands used to create this target type will be
+ determined by the source files in its sources. Targets containing
+ multiple compiler-incompatible languages are not allowed (e.g. a
+ 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.
@@ -1899,7 +2002,8 @@
```
Flags: cflags, cflags_c, cflags_cc, cflags_objc, cflags_objcc,
asmflags, defines, include_dirs, inputs, ldflags, lib_dirs,
- libs, precompiled_header, precompiled_source
+ libs, precompiled_header, precompiled_source, rustflags,
+ rustenv
Nested configs: configs
```
@@ -1913,7 +2017,7 @@
```
config("myconfig") {
- includes = [ "include/common" ]
+ include_dirs = [ "include/common" ]
defines = [ "ENABLE_DOOM_MELON" ]
}
@@ -2049,7 +2153,7 @@
unspecified or the empty list which means no arguments.
input_conversion:
- Controls how the file is read and parsed. See "gn help input_conversion".
+ Controls how the file is read and parsed. See "gn help io_conversion".
If unspecified, defaults to the empty string which causes the script
result to be discarded. exec script will return None.
@@ -2346,10 +2450,10 @@
defined execution order, and it obviously can't reference targets that are
defined after the function call).
- Only copy and action targets are supported. The outputs from binary targets
- will depend on the toolchain definition which won't necessarily have been
- loaded by the time a given line of code has run, and source sets and groups
- have no useful output file.
+ Only copy, generated_file, and action targets are supported. The outputs from
+ binary targets will depend on the toolchain definition which won't
+ necessarily have been loaded by the time a given line of code has run, and
+ source sets and groups have no useful output file.
```
#### **Return value**
@@ -2358,8 +2462,8 @@
The names in the resulting list will be absolute file paths (normally like
"//out/Debug/bar.exe", depending on the build directory).
- action targets: this will just return the files specified in the "outputs"
- variable of the target.
+ action, copy, and generated_file targets: this will just return the files
+ specified in the "outputs" variable of the target.
action_foreach targets: this will return the result of applying the output
template to the sources (see "gn help source_expansion"). This will be the
@@ -2593,7 +2697,7 @@
Filename to read, relative to the build file.
input_conversion
- Controls how the file is read and parsed. See "gn help input_conversion".
+ Controls how the file is read and parsed. See "gn help io_conversion".
```
#### **Example**
@@ -3076,6 +3180,9 @@
Platform specific tools:
"copy_bundle_data": [iOS, macOS] Tool to copy files in a bundle.
"compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
+
+ Rust tools:
+ "rustc": Rust compiler and linker
```
#### **Tool variables**
@@ -3086,6 +3193,15 @@
The command to run.
+ command_launcher [string]
+ Valid for: all tools except "action" (optional)
+
+ The prefix with which to launch the command (e.g. the path to a Goma or
+ CCache compiler launcher).
+
+ Note that this prefix will not be included in the compilation database or
+ IDE files generated from the build.
+
default_output_dir [string with substitutions]
Valid for: linker tools
@@ -3139,6 +3255,20 @@
Example: description = "Compiling {{source}}"
+ exe_output_extension [string, optional, rust tools only]
+ rlib_output_extension [string, optional, rust tools only]
+ dylib_output_extension [string, optional, rust tools only]
+ cdylib_output_extension [string, optional, rust tools only]
+ proc_macro_output_extension [string, optional, rust tools only]
+ Valid for: Rust tools
+
+ These specify the default tool output for each of the crate types.
+ The default is empty for executables, shared, and static libraries and
+ ".rlib" for rlibs. Note that the Rust compiler complains with an error
+ if external crates do not take the form `lib<name>.rlib` or
+ `lib<name>.<shared_extension>`, where `<shared_extension>` is `.so`,
+ `.dylib`, or `.dll` as appropriate for the platform.
+
lib_switch [string, optional, link tools only]
lib_dir_switch [string, optional, link tools only]
Valid for: Linker tools except "alink"
@@ -3452,6 +3582,39 @@
Expands to the path to the partial Info.plist generated by the
assets catalog compiler. Usually based on the target_name of
the create_bundle target.
+
+ Rust tools have the notion of a single input and a single output, along
+ with a set of compiler-specific flags. The following expansions are
+ available:
+
+ {{crate_name}}
+ Expands to the string representing the crate name of target under
+ compilation.
+
+ {{crate_type}}
+ Expands to the string representing the type of crate for the target
+ under compilation.
+
+ {{externs}}
+ Expands to the list of --extern flags needed to include addition Rust
+ libraries in this target. Includes any specified renamed dependencies.
+
+ {{rustc_output_extension}}
+ Expands to the output extension for this target's crate type.
+
+ {{rustc_output_prefix}}
+ Expands to the prefix for shared and static libraries. This should
+ generally be "lib". Empty for executable targets.
+
+ {{rustdeps}}
+ Expands to the list of -Ldependency=<path> strings needed to compile
+ this target.
+
+ {{rustenv}}
+ Expands to the list of environment variables.
+
+ {{rustflags}}
+ Expands to the list of strings representing Rust compiler flags.
```
#### **Separate linking and dependencies for shared libraries**
@@ -3690,7 +3853,7 @@
The list or string to write.
output_conversion
- Controls how the output is written. See "gn help output_conversion".
+ Controls how the output is written. See "gn help io_conversion".
```
## <a name="predefined_variables"></a>Built-in predefined variables
@@ -4024,6 +4187,36 @@
```
## <a name="target_variables"></a>Variables you set in targets
+### <a name="var_aliased_deps"></a>**aliased_deps**: [scope] Set of crate-dependency pairs.
+
+```
+ Valid for `rust_library` targets and `executable`, `static_library`, and
+ `shared_library` targets that contain Rust sources.
+
+ A scope, each key indicating the renamed crate and the corresponding value
+ specifying the label of the dependency producing the relevant binary.
+
+ All dependencies listed in this field *must* be listed as deps of the target.
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ "//bar" ]
+ }
+
+ This target would compile the `foo` crate with the following `extern` flag:
+ `rustc ...command... --extern bar=<build_out_dir>/obj/bar`
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ ":bar" ]
+ aliased_deps = {
+ bar_renamed = ":bar"
+ }
+ }
+
+ With the addition of `aliased_deps`, above target would instead compile with:
+ `rustc ...command... --extern bar_renamed=<build_out_dir>/obj/bar`
+```
### <a name="var_all_dependent_configs"></a>**all_dependent_configs**: Configs to be forced on dependents.
```
@@ -4699,6 +4892,47 @@
The contents of the file for a generated_file target.
See "gn help generated_file".
```
+### <a name="var_crate_name"></a>**crate_name**: [string] The name for the compiled crate.
+
+```
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ If crate_name is not set, then this rule will use the target name.
+```
+### <a name="var_crate_root"></a>**crate_root**: [string] The root source file for a binary or library.
+
+```
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ This file is usually the `main.rs` or `lib.rs` for binaries and libraries,
+ respectively.
+
+ If crate_root is not set, then this rule will look for a lib.rs file (or
+ main.rs for executable) or a single file in sources, if sources contains
+ only one file.
+```
+### <a name="var_crate_type"></a>**crate_type**: [string] The type of linkage to use on a shared_library.
+
+```
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ Options for this field are "cdylib", "staticlib", "proc-macro", and "dylib".
+ This field sets the `crate-type` attribute for the `rustc` tool on static
+ libraries, as well as the appropiate output extension in the
+ `rust_output_extension` attribute. Since outputs must be explicit, the `lib`
+ crate type (where the Rust compiler produces what it thinks is the
+ appropriate library type) is not supported.
+
+ It should be noted that the "dylib" crate type in Rust is unstable in the set
+ of symbols it exposes, and most usages today are potentially wrong and will
+ be broken in the future.
+
+ Static libraries, rust libraries, and executables have this field set
+ automatically.
+```
### <a name="var_data"></a>**data**: Runtime data file dependencies.
```
@@ -4867,6 +5101,16 @@
See also "public_deps".
```
+### <a name="var_edition"></a>**edition**: [string] The rustc edition to use in compiliation.
+
+```
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ This indicates the compiler edition to use in compilition. Should be a value
+ like "2015" or "2018", indiicating the appropriate value to pass to the
+ `--edition=<>` flag in rustc.
+```
### <a name="var_friend"></a>**friend**: Allow targets to include private headers.
```
@@ -5072,7 +5316,7 @@
being relative to the current build file.
libs and lib_dirs work differently than other flags in two respects.
- First, then are inherited across static library boundaries until a
+ First, they are inherited across static library boundaries until a
shared library or executable target is reached. Second, they are
uniquified so each one is only passed once (the first instance of it
will be the one used).
@@ -5114,7 +5358,7 @@
library) containing the current target.
libs and lib_dirs work differently than other flags in two respects.
- First, then are inherited across static library boundaries until a
+ First, they are inherited across static library boundaries until a
shared library or executable target is reached. Second, they are
uniquified so each one is only passed once (the first instance of it
will be the one used).
@@ -5207,7 +5451,7 @@
```
Controls how the "contents" of a generated_file target is formatted.
- See "gn help output_conversion".
+ See "gn help io_conversion".
```
### <a name="var_output_dir"></a>**output_dir**: [directory] Directory to put output file in.
@@ -5737,6 +5981,10 @@
and they do not cross dependency boundaries (so specifying a .def file in a
static library or source set will have no effect on the executable or shared
library they're linked into).
+
+ For Rust targets that do not specify a crate_root, then the crate_root will
+ look for a lib.rs file (or main.rs for executable) or a single file in
+ sources, if sources contains only one file.
```
#### **Sources for non-binary targets**
@@ -5832,13 +6080,13 @@
### <a name="var_walk_keys"></a>**walk_keys**: Key(s) for managing the metadata collection walk.
```
- Defaults to [].
+ Defaults to [""].
These keys are used to control the next step in a collection walk, acting as
barriers. If a specified key is defined in a target's metadata, the walk will
use the targets listed in that value to determine which targets are walked.
- If no walk_keys are specified for a generated_file target (i.e. "[]"), the
+ If no walk_keys are specified for a generated_file target (i.e. "[""]"), the
walk will touch all deps and data_deps of the specified target recursively.
See "gn help generated_file".
@@ -6575,6 +6823,147 @@
//net -> //net:net
//tools/gn -> //tools/gn:gn
```
+### <a name="metadata_collection"></a>**Metadata Collection**
+
+```
+ Metadata is information attached to targets throughout the dependency tree. GN
+ allows for the collection of this data into files written during the generation
+ step, enabing users to expose and aggregate this data based on the dependency
+ tree.
+```
+
+#### **generated_file targets**
+
+```
+ Similar to the write_file() function, the generated_file target type
+ creates a file in the specified location with the specified content. The
+ primary difference between the function and the target type is that the
+ write_file function does the file write at parse time, while the
+ generated_file target type writes at target resolution time. See
+ "gn help generated_file" for more detail.
+
+ When written at target resolution time, the generated_file enables GN to
+ collect and write aggregated metadata from dependents.
+
+ A generated_file target can declare either 'contents' (to write statically
+ known contents to a file) or 'data_keys'(to aggregate metadata and write the
+ result to a file). It can also specify 'walk_keys' (to restrict the metadata
+ collection), 'output_conversion', and 'rebase'.
+```
+
+#### **Collection and Aggregation**
+
+```
+ Targets can declare a 'metadata' variable containing a scope, and this
+ metadata is collected and written to file by generated_file aggregation
+ targets. The 'metadata' scope must contain only list values, since the
+ aggregation step collects a list of these values.
+
+ During the target resolution, generated_file targets will walk their
+ dependencies recursively, collecting metadata based on the specified
+ 'data_keys'. 'data_keys' is specified as a list of strings, used by the walk
+ to identify which variables in dependencies' 'metadata' scopes to collect.
+
+ The walk begins with the listed dependencies of the 'generated_file' target,
+ for each checking the "metadata" scope for any of the "data_keys". If
+ present, the data in those variables is appended to the aggregate list. Note
+ that this means that if more than one walk key is specified, the data in all
+ of them will be aggregated into one list. From there, the walk will then
+ recurse into the dependencies of each target it encounters, collecting the
+ specified metadata for each.
+
+ For example:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+ my_extra_files = [ "bar.cpp" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files", "my_extra_files" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data:
+
+ foo.cpp
+ bar.cpp
+ baz.cpp
+
+ The dependency walk can be limited by using the "walk_keys". This is a list of
+ labels that should be included in the walk. All labels specified here should
+ also be in one of the deps lists. These keys act as barriers, where the walk
+ will only recurse into targets listed here. An empty list in all specified
+ barriers will end that portion of the walk.
+
+ group("a") {
+ metadata = {
+ my_files = [ "foo.cpp" ]
+ my_files_barrier [ ":b" ]
+ }
+
+ deps = [ ":b", ":c" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ }
+ }
+
+ group("c") {
+ metadata = {
+ my_files = [ "doom_melon.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files", "my_extra_files" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data (note that `doom_melon.cpp` is
+ not included):
+
+ foo.cpp
+ bar.cpp
+
+ A common example of this sort of barrier is in builds that have host tools
+ built as part of the tree, but do not want the metadata from those host tools
+ to be collected with the target-side code.
+```
+
+#### **Common Uses**
+
+```
+ Metadata can be used to collect information about the different targets in the
+ build, and so a common use is to provide post-build tooling with a set of data
+ necessary to do aggregation tasks. For example, if each test target specifies
+ the output location of its binary to run in a metadata field, that can be
+ collected into a single file listing the locations of all tests in the
+ dependency tree. A local build tool (or continuous integration infrastructure)
+ can then use that file to know which tests exist, and where, and run them
+ accordingly.
+
+ Another use is in image creation, where a post-build image tool needs to know
+ various pieces of information about the components it should include in order
+ to put together the correct image.
+```
### <a name="ninja_rules"></a>**Ninja build rules**
#### **The "all" and "default" rules**
@@ -6807,8 +7196,9 @@
{{source_target_relative}}
The path to the source file relative to the target's directory. This will
generally be used for replicating the source directory layout in the
- output directory. This can only be used in actions and it is an error to
- use in process_file_template where there is no "target".
+ output directory. This can only be used in actions and bundle_data
+ targets. It is an error to use in process_file_template where there is no
+ "target".
"//foo/bar/baz.txt" => "baz.txt"
```
diff --git a/gn/docs/style_guide.md b/gn/docs/style_guide.md
index 09f7261914f..8c27a8cac9b 100644
--- a/gn/docs/style_guide.md
+++ b/gn/docs/style_guide.md
@@ -191,7 +191,7 @@ to the link line of the final binary.
* Source sets can have no sources, while static libraries will give strange
platform-specific errors if they have no sources. If a target has only
headers (for include checking purposes) or conditionally has no sources on
- sone platforms, use a source set.
+ some platforms, use a source set.
* In cases where a lot of the symbols are not needed for a particular link
(this especially happens when linking test binaries), putting that code in
diff --git a/gn/examples/rust_example/.gn b/gn/examples/rust_example/.gn
new file mode 100644
index 00000000000..9fe0b4226cd
--- /dev/null
+++ b/gn/examples/rust_example/.gn
@@ -0,0 +1 @@
+buildconfig = "//BUILDCONFIG.gn"
diff --git a/gn/examples/rust_example/BUILD.gn b/gn/examples/rust_example/BUILD.gn
new file mode 100644
index 00000000000..e28f02366e4
--- /dev/null
+++ b/gn/examples/rust_example/BUILD.gn
@@ -0,0 +1,5 @@
+group("default") {
+ deps = [
+ "//hello_world/src:hello_world",
+ ]
+}
diff --git a/gn/examples/rust_example/BUILDCONFIG.gn b/gn/examples/rust_example/BUILDCONFIG.gn
new file mode 100644
index 00000000000..62b5bb69800
--- /dev/null
+++ b/gn/examples/rust_example/BUILDCONFIG.gn
@@ -0,0 +1,23 @@
+if (target_os == "") {
+ target_os = host_os
+}
+if (target_cpu == "") {
+ target_cpu = host_cpu
+}
+if (current_cpu == "") {
+ current_cpu = target_cpu
+}
+if (current_os == "") {
+ current_os = target_os
+}
+
+_configs = [ "//build:rust_defaults" ]
+
+set_defaults("executable") {
+ configs = _configs
+}
+set_defaults("rust_library") {
+ configs = _configs
+}
+
+set_default_toolchain("//build:rust")
diff --git a/gn/tools/gn/example/README.txt b/gn/examples/rust_example/README.txt
index d0ddeed8b47..8bfbdd0570c 100644
--- a/gn/tools/gn/example/README.txt
+++ b/gn/examples/rust_example/README.txt
@@ -1,4 +1,4 @@
This is an example directory structure that compiles some simple targets using
-gcc. It is intended to show how to set up a simple GN build.
+gcc. It is intended to show how to set up a simple Rust GN build.
Don't miss the ".gn" file in this directory!
diff --git a/gn/examples/rust_example/build/BUILD.gn b/gn/examples/rust_example/build/BUILD.gn
new file mode 100644
index 00000000000..e4aecfde1ef
--- /dev/null
+++ b/gn/examples/rust_example/build/BUILD.gn
@@ -0,0 +1,27 @@
+toolchain("rust") {
+ outfile = "{{target_out_dir}}/{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}}"
+ tool("rustc") {
+ depfile = "{{target_out_dir}}/{{crate_name}}.d"
+ command = "rustc --edition={{edition}} --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
+ description = "RUST $outfile"
+ cdylib_output_extension = ".so"
+ proc_macro_output_extension = ".so"
+ outputs = [
+ outfile,
+ ]
+ }
+
+ tool("stamp") {
+ command = "touch {{output}}"
+ description = "STAMP {{output}}"
+ }
+
+ tool("copy") {
+ command = "cp -af {{source}} {{output}}"
+ description = "COPY {{source}} {{output}}"
+ }
+}
+
+config("rust_defaults") {
+ rustflags = [ "-Cdebuginfo=2" ]
+}
diff --git a/gn/examples/rust_example/hello_world/bar/src/BUILD.gn b/gn/examples/rust_example/hello_world/bar/src/BUILD.gn
new file mode 100644
index 00000000000..49c6eeef37a
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/bar/src/BUILD.gn
@@ -0,0 +1,4 @@
+rust_library("bar") {
+ crate_root = "lib.rs"
+ edition = "2018"
+}
diff --git a/gn/examples/rust_example/hello_world/bar/src/lib.rs b/gn/examples/rust_example/hello_world/bar/src/lib.rs
new file mode 100644
index 00000000000..43defe93b5e
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/bar/src/lib.rs
@@ -0,0 +1,23 @@
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn it_works() {
+ assert_eq!(2 + 2, 4);
+ }
+}
+
+#[derive(Debug)]
+pub struct Foo {
+ s: &'static str,
+ i: &'static str
+}
+
+impl Foo {
+ pub fn new(s: &'static str) -> Foo {
+ Foo{s: s, i: "bar"}
+ }
+}
+
+pub fn answer() -> i32 {
+ 42
+} \ No newline at end of file
diff --git a/gn/examples/rust_example/hello_world/foo/src/BUILD.gn b/gn/examples/rust_example/hello_world/foo/src/BUILD.gn
new file mode 100644
index 00000000000..61ee1f3bae3
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/foo/src/BUILD.gn
@@ -0,0 +1,9 @@
+rust_library("foo") {
+ sources = [
+ "lib.rs",
+ ]
+ edition = "2018"
+ deps = [
+ "//hello_world/bar/src:bar",
+ ]
+}
diff --git a/gn/examples/rust_example/hello_world/foo/src/lib.rs b/gn/examples/rust_example/hello_world/foo/src/lib.rs
new file mode 100644
index 00000000000..73c615c98d1
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/foo/src/lib.rs
@@ -0,0 +1,11 @@
+#[derive(Debug)]
+pub struct Foo {
+ s: &'static str,
+ i: &'static str
+}
+
+impl Foo {
+ pub fn new(s: &'static str) -> Foo {
+ Foo{s: s, i: "foo"}
+ }
+} \ No newline at end of file
diff --git a/gn/examples/rust_example/hello_world/src/BUILD.gn b/gn/examples/rust_example/hello_world/src/BUILD.gn
new file mode 100644
index 00000000000..d6a0a664968
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/src/BUILD.gn
@@ -0,0 +1,12 @@
+executable("hello_world") {
+ sources = [
+ "main.rs",
+ ]
+ deps = [
+ "//hello_world/foo/src:foo",
+ ]
+ edition = "2018"
+ aliased_deps = {
+ baz = "//hello_world/foo/src:foo"
+ }
+}
diff --git a/gn/examples/rust_example/hello_world/src/main.rs b/gn/examples/rust_example/hello_world/src/main.rs
new file mode 100644
index 00000000000..ad70a120e05
--- /dev/null
+++ b/gn/examples/rust_example/hello_world/src/main.rs
@@ -0,0 +1,5 @@
+fn main() {
+ let f = baz::Foo::new("hello");
+ println!("Hello world!");
+ println!("I'm from a dependency: {:?}!", f);
+} \ No newline at end of file
diff --git a/gn/tools/gn/example/.gn b/gn/examples/simple_build/.gn
index e5b6d4a3db7..e5b6d4a3db7 100644
--- a/gn/tools/gn/example/.gn
+++ b/gn/examples/simple_build/.gn
diff --git a/gn/tools/gn/example/BUILD.gn b/gn/examples/simple_build/BUILD.gn
index a18390e6489..a18390e6489 100644
--- a/gn/tools/gn/example/BUILD.gn
+++ b/gn/examples/simple_build/BUILD.gn
diff --git a/gn/examples/simple_build/README.md b/gn/examples/simple_build/README.md
new file mode 100644
index 00000000000..78b39178a93
--- /dev/null
+++ b/gn/examples/simple_build/README.md
@@ -0,0 +1,12 @@
+# GN Simple Build Example
+
+This is an example directory structure that compiles some simple targets using
+gcc. It is intended to show how to set up a simple GN build. It is deliberately
+simplistic so the structure is more clear, and doesn't support everything on
+every platform.
+
+It is recommended that you take this and add on as your requirements indicate.
+You may also want to see the Chromium and Fuchsia build configurations for the
+maximal functionality (the [root README](../../README.md) has links to these).
+
+Don't miss the ".gn" file in this directory which may be hidden on your system!
diff --git a/gn/tools/gn/example/build/BUILD.gn b/gn/examples/simple_build/build/BUILD.gn
index 8eae46a1aad..8eae46a1aad 100644
--- a/gn/tools/gn/example/build/BUILD.gn
+++ b/gn/examples/simple_build/build/BUILD.gn
diff --git a/gn/tools/gn/example/build/BUILDCONFIG.gn b/gn/examples/simple_build/build/BUILDCONFIG.gn
index e419fd96a8a..e419fd96a8a 100644
--- a/gn/tools/gn/example/build/BUILDCONFIG.gn
+++ b/gn/examples/simple_build/build/BUILDCONFIG.gn
diff --git a/gn/tools/gn/example/build/toolchain/BUILD.gn b/gn/examples/simple_build/build/toolchain/BUILD.gn
index d9457d7eae4..d9457d7eae4 100644
--- a/gn/tools/gn/example/build/toolchain/BUILD.gn
+++ b/gn/examples/simple_build/build/toolchain/BUILD.gn
diff --git a/gn/tools/gn/example/hello.cc b/gn/examples/simple_build/hello.cc
index c4aa4482949..c4aa4482949 100644
--- a/gn/tools/gn/example/hello.cc
+++ b/gn/examples/simple_build/hello.cc
diff --git a/gn/tools/gn/example/hello_shared.cc b/gn/examples/simple_build/hello_shared.cc
index 58be84c27b4..58be84c27b4 100644
--- a/gn/tools/gn/example/hello_shared.cc
+++ b/gn/examples/simple_build/hello_shared.cc
diff --git a/gn/tools/gn/example/hello_shared.h b/gn/examples/simple_build/hello_shared.h
index 7af804b3be4..7af804b3be4 100644
--- a/gn/tools/gn/example/hello_shared.h
+++ b/gn/examples/simple_build/hello_shared.h
diff --git a/gn/tools/gn/example/hello_static.cc b/gn/examples/simple_build/hello_static.cc
index cdf4e67b05c..cdf4e67b05c 100644
--- a/gn/tools/gn/example/hello_static.cc
+++ b/gn/examples/simple_build/hello_static.cc
diff --git a/gn/tools/gn/example/hello_static.h b/gn/examples/simple_build/hello_static.h
index f15a6336d2a..f15a6336d2a 100644
--- a/gn/tools/gn/example/hello_static.h
+++ b/gn/examples/simple_build/hello_static.h
diff --git a/gn/infra/README.recipes.md b/gn/infra/README.recipes.md
index d8005aaf002..128ecb71b19 100644
--- a/gn/infra/README.recipes.md
+++ b/gn/infra/README.recipes.md
@@ -1,5 +1,5 @@
<!--- AUTOGENERATED BY `./recipes.py test train` -->
-# Package documentation for [gn]()
+# Repo documentation for [gn]()
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
@@ -94,15 +94,15 @@ Recipe for building GN.
&mdash; **def [RunSteps](/infra/recipe_modules/windows_sdk/examples/full.py#13)(api):**
-[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-buildbucket
-[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-cipd
-[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-context
-[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-file
-[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-json
-[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-path
-[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-platform
-[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-properties
-[recipe_engine/recipe_modules/python]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-python
-[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-raw_io
-[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/README.recipes.md#recipe_modules-step
-[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/82e233fa4e6fed0a5dd99501e0d52d3dce25c994/recipe_engine/recipe_api.py#1006
+[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-buildbucket
+[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-cipd
+[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-context
+[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-file
+[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-json
+[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-path
+[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-platform
+[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-properties
+[recipe_engine/recipe_modules/python]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-python
+[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-raw_io
+[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/README.recipes.md#recipe_modules-step
+[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/0589a429cf3c164004dae4ced4c75784a50afd81/recipe_engine/recipe_api.py#838
diff --git a/gn/infra/config/cq.cfg b/gn/infra/config/cq.cfg
deleted file mode 100644
index bb3a274fd1d..00000000000
--- a/gn/infra/config/cq.cfg
+++ /dev/null
@@ -1,27 +0,0 @@
-# See http://luci-config.appspot.com/schemas/projects/refs:cq.cfg for the
-# documentation of this file format.
-
-version: 1
-cq_name: "gn"
-git_repo_url: "https://gn.googlesource.com/gn"
-gerrit {}
-verifiers {
- try_job {
- buckets {
- name: "luci.gn.try",
- builders {
- name: "linux"
- }
- builders {
- name: "mac"
- }
- builders {
- name: "win"
- }
- }
- }
- gerrit_cq_ability {
- committer_list: "project-gn-committers"
- dry_run_access_list: "project-gn-tryjob-access"
- }
-}
diff --git a/gn/infra/config/recipes.cfg b/gn/infra/config/recipes.cfg
index 70e68920ff2..6b7363bd1c4 100644
--- a/gn/infra/config/recipes.cfg
+++ b/gn/infra/config/recipes.cfg
@@ -3,7 +3,7 @@
"deps": {
"recipe_engine": {
"branch": "master",
- "revision": "82e233fa4e6fed0a5dd99501e0d52d3dce25c994",
+ "revision": "0589a429cf3c164004dae4ced4c75784a50afd81",
"url": "https://chromium.googlesource.com/infra/luci/recipes-py"
}
},
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json
index 51d94309ff2..40db66c972c 100644
--- a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/linux.json
@@ -16,8 +16,7 @@
"name": "ninja"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
index e5a7fc0c79b..e2acecdfa75 100644
--- a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/mac.json
@@ -18,7 +18,7 @@
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -76,8 +76,7 @@
"name": "reset XCode"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json
index 51d94309ff2..40db66c972c 100644
--- a/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json
+++ b/gn/infra/recipe_modules/macos_sdk/examples/full.expected/win.json
@@ -16,8 +16,7 @@
"name": "ninja"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
index 51d94309ff2..40db66c972c 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/linux.json
@@ -16,8 +16,7 @@
"name": "ninja"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
index 51d94309ff2..40db66c972c 100644
--- a/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
+++ b/gn/infra/recipe_modules/windows_sdk/examples/full.expected/mac.json
@@ -16,8 +16,7 @@
"name": "ninja"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
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 092354d907b..def8e7a2b6d 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
@@ -101,8 +101,7 @@
"name": "taskkill mspdbsrv"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes.py b/gn/infra/recipes.py
index d726c689795..2fe00862255 100755
--- a/gn/infra/recipes.py
+++ b/gn/infra/recipes.py
@@ -10,7 +10,7 @@
** DO NOT MODIFY **
*******************
-This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/master/doc/recipes.py.
+This is a copy of https://chromium.googlesource.com/infra/luci/recipes-py/+/master/recipes.py.
To fix bugs, fix in the googlesource repo then run the autoroller.
"""
@@ -32,16 +32,10 @@ from cStringIO import StringIO
#
# url (str) - the url to the engine repo we want to use.
# revision (str) - the git revision for the engine to get.
-# path_override (str) - the subdirectory in the engine repo we should use to
-# find it's recipes.py entrypoint. This is here for completeness, but will
-# essentially always be empty. It would be used if the recipes-py repo was
-# merged as a subdirectory of some other repo and you depended on that
-# subdirectory.
# branch (str) - the branch to fetch for the engine as an absolute ref (e.g.
# refs/heads/master)
-# repo_type ("GIT"|"GITILES") - An ignored enum which will be removed soon.
EngineDep = namedtuple('EngineDep',
- 'url revision path_override branch repo_type')
+ 'url revision branch')
class MalformedRecipesCfg(Exception):
@@ -73,9 +67,12 @@ def parse(repo_root, recipes_cfg_path):
raise MalformedRecipesCfg('unknown version %d' % pb['api_version'],
recipes_cfg_path)
- # If we're running ./doc/recipes.py from the recipe_engine repo itself, then
+ # If we're running ./recipes.py from the recipe_engine repo itself, then
# return None to signal that there's no EngineDep.
- if pb['project_id'] == 'recipe_engine':
+ repo_name = pb.get('repo_name')
+ if not repo_name:
+ repo_name = pb['project_id']
+ if repo_name == 'recipe_engine':
return None, pb.get('recipes_path', '')
engine = pb['deps']['recipe_engine']
@@ -86,7 +83,6 @@ def parse(repo_root, recipes_cfg_path):
recipes_cfg_path)
engine.setdefault('revision', '')
- engine.setdefault('path_override', '')
engine.setdefault('branch', 'refs/heads/master')
recipes_path = pb.get('recipes_path', '')
@@ -94,12 +90,6 @@ def parse(repo_root, recipes_cfg_path):
if not engine['branch'].startswith('refs/'):
engine['branch'] = 'refs/heads/' + engine['branch']
- engine.setdefault('repo_type', 'GIT')
- if engine['repo_type'] not in ('GIT', 'GITILES'):
- raise MalformedRecipesCfg(
- 'Unsupported "repo_type" value in dependency "recipe_engine"',
- recipes_cfg_path)
-
recipes_path = os.path.join(
repo_root, recipes_path.replace('/', os.path.sep))
return EngineDep(**engine), recipes_path
@@ -110,6 +100,20 @@ def parse(repo_root, recipes_cfg_path):
_BAT = '.bat' if sys.platform.startswith(('win', 'cygwin')) else ''
GIT = 'git' + _BAT
VPYTHON = 'vpython' + _BAT
+CIPD = 'cipd' + _BAT
+REQUIRED_BINARIES = {GIT, VPYTHON, 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):
+ full_path = os.path.join(path, basename)
+ if _is_executable(full_path):
+ return True
+ return False
def _subprocess_call(argv, **kwargs):
@@ -132,7 +136,7 @@ def _git_output(argv, **kwargs):
def parse_args(argv):
"""This extracts a subset of the arguments that this bootstrap script cares
about. Currently this consists of:
- * an override for the recipe engine in the form of `-O recipe_engin=/path`
+ * an override for the recipe engine in the form of `-O recipe_engine=/path`
* the --package option.
"""
PREFIX = 'recipe_engine='
@@ -160,33 +164,39 @@ def checkout_engine(engine_path, repo_root, recipes_cfg_path):
if not engine_path:
revision = dep.revision
- subpath = dep.path_override
branch = dep.branch
# Ensure that we have the recipe engine cloned.
- engine = os.path.join(recipes_path, '.recipe_deps', 'recipe_engine')
- engine_path = os.path.join(engine, subpath)
+ engine_path = os.path.join(recipes_path, '.recipe_deps', 'recipe_engine')
with open(os.devnull, 'w') as NUL:
# Note: this logic mirrors the logic in recipe_engine/fetch.py
- _git_check_call(['init', engine], stdout=NUL)
+ _git_check_call(['init', engine_path], stdout=NUL)
try:
_git_check_call(['rev-parse', '--verify', '%s^{commit}' % revision],
- cwd=engine, stdout=NUL, stderr=NUL)
+ cwd=engine_path, stdout=NUL, stderr=NUL)
except subprocess.CalledProcessError:
- _git_check_call(['fetch', url, branch], cwd=engine, stdout=NUL,
+ _git_check_call(['fetch', url, branch], cwd=engine_path, stdout=NUL,
stderr=NUL)
try:
- _git_check_call(['diff', '--quiet', revision], cwd=engine)
+ _git_check_call(['diff', '--quiet', revision], cwd=engine_path)
except subprocess.CalledProcessError:
- _git_check_call(['reset', '-q', '--hard', revision], cwd=engine)
+ _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
def main():
+ for required_binary in REQUIRED_BINARIES:
+ if not _is_on_path(required_binary):
+ return 'Required binary is not found on PATH: %s' % required_binary
+
if '--verbose' in sys.argv:
logging.getLogger().setLevel(logging.INFO)
@@ -211,7 +221,7 @@ def main():
return _subprocess_call([
VPYTHON, '-u',
- os.path.join(engine_path, 'recipes.py')] + args)
+ os.path.join(engine_path, 'recipe_engine', 'main.py')] + args)
if __name__ == '__main__':
diff --git a/gn/infra/recipes/gn.expected/ci_linux.json b/gn/infra/recipes/gn.expected/ci_linux.json
index 993c4918fad..620f4559f75 100644
--- a/gn/infra/recipes/gn.expected/ci_linux.json
+++ b/gn/infra/recipes/gn.expected/ci_linux.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "2d72510e447ab60a9728aeea2362d8be2cbd7789"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,18 +61,18 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -207,6 +207,8 @@
"{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
"-json-output",
"/path/to/tmp/json"
],
@@ -215,15 +217,14 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/ci_mac.json b/gn/infra/recipes/gn.expected/ci_mac.json
index bb30be4d360..6ff9fe92e39 100644
--- a/gn/infra/recipes/gn.expected/ci_mac.json
+++ b/gn/infra/recipes/gn.expected/ci_mac.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "2d72510e447ab60a9728aeea2362d8be2cbd7789"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,12 +61,12 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -93,7 +93,7 @@
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -161,7 +161,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "debug.build.generate",
"~followup_annotations": [
@@ -180,7 +180,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "debug.build.ninja",
"~followup_annotations": [
@@ -221,7 +221,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "release.build.generate",
"~followup_annotations": [
@@ -240,7 +240,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "release.build.ninja",
"~followup_annotations": [
@@ -273,6 +273,8 @@
"{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
"-json-output",
"/path/to/tmp/json"
],
@@ -281,15 +283,14 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/ci_win.json b/gn/infra/recipes/gn.expected/ci_win.json
index 764f2fa3835..6df7ef53496 100644
--- a/gn/infra/recipes/gn.expected/ci_win.json
+++ b/gn/infra/recipes/gn.expected/ci_win.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "2d72510e447ab60a9728aeea2362d8be2cbd7789"
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
@@ -62,7 +62,7 @@
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -131,26 +131,10 @@
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "debug"
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "debug.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -216,26 +200,10 @@
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "release"
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "release.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -318,6 +286,8 @@
"{\"data\": [{\"file\": \"gn.exe\"}, {\"version_file\": \".versions/gn.exe.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]\\\\gn\\\\out\"}",
"-out",
"[CLEANUP]\\gn.cipd",
+ "-hash-algo",
+ "sha256",
"-json-output",
"/path/to/tmp/json"
],
@@ -326,15 +296,14 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cipd_exists.json b/gn/infra/recipes/gn.expected/cipd_exists.json
index c6f38253428..337f548c6f6 100644
--- a/gn/infra/recipes/gn.expected/cipd_exists.json
+++ b/gn/infra/recipes/gn.expected/cipd_exists.json
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,18 +61,18 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -207,6 +207,8 @@
"{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
"-json-output",
"/path/to/tmp/json"
],
@@ -215,7 +217,7 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -259,8 +261,7 @@
"name": "Package is up-to-date"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cipd_register.json b/gn/infra/recipes/gn.expected/cipd_register.json
index 76efe86ef21..ce6a2213c1f 100644
--- a/gn/infra/recipes/gn.expected/cipd_register.json
+++ b/gn/infra/recipes/gn.expected/cipd_register.json
@@ -50,7 +50,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -61,18 +61,18 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -207,6 +207,8 @@
"{\"data\": [{\"file\": \"gn\"}, {\"version_file\": \".versions/gn.cipd_version\"}], \"install_mode\": \"copy\", \"package\": \"gn/gn/${platform}\", \"root\": \"[START_DIR]/gn/out\"}",
"-out",
"[CLEANUP]/gn.cipd",
+ "-hash-algo",
+ "sha256",
"-json-output",
"/path/to/tmp/json"
],
@@ -215,7 +217,7 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
@@ -268,15 +270,14 @@
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"result\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"40-chars-fake-of-the-package-instance_id\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"gn/gn/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@}@@@",
"@@@STEP_LOG_END@json.output@@@"
]
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cq_linux.json b/gn/infra/recipes/gn.expected/cq_linux.json
index 8fe2ad2a688..329ac6e904d 100644
--- a/gn/infra/recipes/gn.expected/cq_linux.json
+++ b/gn/infra/recipes/gn.expected/cq_linux.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "refs/heads/master"
+ ""
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
@@ -48,11 +48,11 @@
"git",
"fetch",
"https://gn.googlesource.com/gn",
- "refs/changes/00/1000/1"
+ "refs/changes/56/123456/7"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.fetch 1000/1",
+ "name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -65,7 +65,7 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.cherry-pick 1000/1",
+ "name": "git.cherry-pick 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -77,7 +77,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a\n@Subdir sysroot\nfuchsia/sysroot/${platform} git_revision:a28dfa20af063e5ca00634024c85732e20220419",
"-json-output",
"/path/to/tmp/json"
],
@@ -88,18 +88,18 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ], @@@",
"@@@STEP_LOG_LINE@json.output@ \"sysroot\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:a28\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/sysroot/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -227,8 +227,7 @@
]
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "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 7872a57c4ed..b84aab80eff 100644
--- a/gn/infra/recipes/gn.expected/cq_mac.json
+++ b/gn/infra/recipes/gn.expected/cq_mac.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "refs/heads/master"
+ ""
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
@@ -48,11 +48,11 @@
"git",
"fetch",
"https://gn.googlesource.com/gn",
- "refs/changes/00/1000/1"
+ "refs/changes/56/123456/7"
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.fetch 1000/1",
+ "name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -65,7 +65,7 @@
],
"cwd": "[START_DIR]/gn",
"infra_step": true,
- "name": "git.cherry-pick 1000/1",
+ "name": "git.cherry-pick 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -77,7 +77,7 @@
"-root",
"[START_DIR]/cipd",
"-ensure-file",
- "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} goma",
+ "infra/ninja/${platform} version:1.8.2\nfuchsia/clang/${platform} git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a",
"-json-output",
"/path/to/tmp/json"
],
@@ -88,12 +88,12 @@
"@@@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-goma------------\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:b92\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"fuchsia/clang/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -120,7 +120,7 @@
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-git_revision:434\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/tools/mac_toolchain/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -188,7 +188,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "debug.build.generate",
"~followup_annotations": [
@@ -207,7 +207,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "debug.build.ninja",
"~followup_annotations": [
@@ -248,7 +248,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "release.build.generate",
"~followup_annotations": [
@@ -267,7 +267,7 @@
"CC": "[START_DIR]/cipd/bin/clang",
"CFLAGS": "--sysroot=/some/xcode/path",
"CXX": "[START_DIR]/cipd/bin/clang++",
- "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a [START_DIR]/cipd/lib/libc++abi.a [START_DIR]/cipd/lib/libunwind.a"
+ "LDFLAGS": "--sysroot=/some/xcode/path -nostdlib++ [START_DIR]/cipd/lib/libc++.a"
},
"name": "release.build.ninja",
"~followup_annotations": [
@@ -293,8 +293,7 @@
"name": "reset XCode"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.expected/cq_win.json b/gn/infra/recipes/gn.expected/cq_win.json
index 92f665a627b..979c3b9eb39 100644
--- a/gn/infra/recipes/gn.expected/cq_win.json
+++ b/gn/infra/recipes/gn.expected/cq_win.json
@@ -21,7 +21,7 @@
"fetch",
"--tags",
"https://gn.googlesource.com/gn",
- "refs/heads/master"
+ ""
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
@@ -48,11 +48,11 @@
"git",
"fetch",
"https://gn.googlesource.com/gn",
- "refs/changes/00/1000/1"
+ "refs/changes/56/123456/7"
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
- "name": "git.fetch 1000/1",
+ "name": "git.fetch 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -65,7 +65,7 @@
],
"cwd": "[START_DIR]\\gn",
"infra_step": true,
- "name": "git.cherry-pick 1000/1",
+ "name": "git.cherry-pick 123456/7",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
]
@@ -89,7 +89,7 @@
"@@@STEP_LOG_LINE@json.output@ \"\": [@@@",
"@@@STEP_LOG_LINE@json.output@ {@@@",
"@@@STEP_LOG_LINE@json.output@ \"instance_id\": \"resolved-instance_id-of-version:1.8.2---\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/${platform}\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"package\": \"infra/ninja/resolved-platform\"@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
"@@@STEP_LOG_LINE@json.output@ ]@@@",
"@@@STEP_LOG_LINE@json.output@ }@@@",
@@ -158,26 +158,10 @@
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "debug"
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "debug.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -243,26 +227,10 @@
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "release"
},
{
"cmd": [],
- "env": {
- "VSINSTALLDIR": "[CACHE]\\windows_sdk"
- },
- "env_prefixes": {
- "PATH": [
- "[CACHE]\\windows_sdk\\win_sdk\\bin\\x64"
- ]
- },
"name": "release.build",
"~followup_annotations": [
"@@@STEP_NEST_LEVEL@1@@@"
@@ -338,8 +306,7 @@
"name": "taskkill mspdbsrv"
},
{
- "name": "$result",
- "recipe_result": null,
- "status_code": 0
+ "jsonResult": null,
+ "name": "$result"
}
] \ No newline at end of file
diff --git a/gn/infra/recipes/gn.py b/gn/infra/recipes/gn.py
index 7a079b54d8f..f219b810d9d 100644
--- a/gn/infra/recipes/gn.py
+++ b/gn/infra/recipes/gn.py
@@ -54,7 +54,8 @@ def RunSteps(api, repository):
pkgs = api.cipd.EnsureFile()
pkgs.add_package('infra/ninja/${platform}', 'version:1.8.2')
if api.platform.is_linux or api.platform.is_mac:
- pkgs.add_package('fuchsia/clang/${platform}', 'goma')
+ pkgs.add_package('fuchsia/clang/${platform}',
+ 'git_revision:b920a7f65b13237dc4d5b2b836b29a954fff440a')
if api.platform.is_linux:
pkgs.add_package('fuchsia/sysroot/${platform}',
'git_revision:a28dfa20af063e5ca00634024c85732e20220419',
@@ -90,15 +91,13 @@ def RunSteps(api, repository):
step_test_data=
lambda: api.raw_io.test_api.stream_output('/some/xcode/path')
).stdout.strip()
- stdlib = '%s %s %s' % (cipd_dir.join('lib', 'libc++.a'),
- cipd_dir.join('lib', 'libc++abi.a'),
- cipd_dir.join('lib', 'libunwind.a'))
+ stdlib = '-nostdlib++ %s' % cipd_dir.join('lib', 'libc++.a')
env = {
'CC': cipd_dir.join('bin', 'clang'),
'CXX': cipd_dir.join('bin', 'clang++'),
'AR': cipd_dir.join('bin', 'llvm-ar'),
'CFLAGS': sysroot,
- 'LDFLAGS': '%s -nostdlib++ %s' % (sysroot, stdlib),
+ 'LDFLAGS': '%s %s' % (sysroot, stdlib),
}
else:
env = {}
@@ -157,35 +156,32 @@ def RunSteps(api, repository):
def GenTests(api):
- REVISION = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
-
for platform in ('linux', 'mac', 'win'):
yield (api.test('ci_' + platform) + api.platform.name(platform) +
api.buildbucket.ci_build(
+ project='gn',
git_repo='gn.googlesource.com/gn',
- revision=REVISION,
))
yield (api.test('cq_' + platform) + api.platform.name(platform) +
api.buildbucket.try_build(
- gerrit_host='gn-review.googlesource.com',
- change_number=1000,
- patch_set=1,
+ project='gn',
+ git_repo='gn.googlesource.com/gn',
))
yield (api.test('cipd_exists') + api.buildbucket.ci_build(
project='infra-internal',
git_repo='gn.googlesource.com/gn',
- revision=REVISION,
- ) + api.step_data('rev-parse', api.raw_io.stream_output(REVISION)) +
- api.step_data('cipd search gn/gn/${platform} git_revision:' + REVISION,
+ revision='a' * 40,
+ ) + api.step_data('rev-parse', api.raw_io.stream_output('a' * 40)) +
+ api.step_data('cipd search gn/gn/${platform} git_revision:' + 'a' * 40,
api.cipd.example_search('gn/gn/linux-amd64',
- ['git_revision:' + REVISION])))
+ ['git_revision:' + 'a' * 40])))
yield (api.test('cipd_register') + api.buildbucket.ci_build(
project='infra-internal',
git_repo='gn.googlesource.com/gn',
- revision=REVISION,
- ) + api.step_data('rev-parse', api.raw_io.stream_output(REVISION)) +
- api.step_data('cipd search gn/gn/${platform} git_revision:' + REVISION,
+ revision='a' * 40,
+ ) + api.step_data('rev-parse', api.raw_io.stream_output('a' * 40)) +
+ api.step_data('cipd search gn/gn/${platform} git_revision:' + 'a' * 40,
api.cipd.example_search('gn/gn/linux-amd64', [])))
diff --git a/gn/tools/gn/action_target_generator.cc b/gn/tools/gn/action_target_generator.cc
index c0ba6c8058f..61e9d091bc5 100644
--- a/gn/tools/gn/action_target_generator.cc
+++ b/gn/tools/gn/action_target_generator.cc
@@ -74,7 +74,7 @@ void ActionTargetGenerator::DoRun() {
const auto& required_args_substitutions =
target_->action_values().args().required_types();
bool has_rsp_file_name = base::ContainsValue(required_args_substitutions,
- SUBSTITUTION_RSP_FILE_NAME);
+ &SubstitutionRspFileName);
if (target_->action_values().uses_rsp_file() && !has_rsp_file_name) {
*err_ = Err(
function_call_, "Missing {{response_file_name}} in args.",
diff --git a/gn/tools/gn/analyzer.cc b/gn/tools/gn/analyzer.cc
index 312c22aab01..c58cf8cf6a4 100644
--- a/gn/tools/gn/analyzer.cc
+++ b/gn/tools/gn/analyzer.cc
@@ -142,11 +142,12 @@ Err JSONToInputs(const Label& default_toolchain,
strings = GetStringVector(*dict, "files", &err);
if (err.has_error())
return err;
- for (auto s : strings) {
- if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s))
+ for (auto& s : strings) {
+ if (!IsPathSourceAbsolute(s) && !IsPathAbsolute(s)) {
return Err(Location(),
"\"" + s + "\" is not a source-absolute or absolute path.");
- inputs->source_vec.push_back(SourceFile(s));
+ }
+ inputs->source_vec.emplace_back(std::move(s));
}
strings = GetStringVector(*dict, "additional_compile_targets", &err);
diff --git a/gn/tools/gn/analyzer_unittest.cc b/gn/tools/gn/analyzer_unittest.cc
index a6d73a28d27..d796300b00f 100644
--- a/gn/tools/gn/analyzer_unittest.cc
+++ b/gn/tools/gn/analyzer_unittest.cc
@@ -3,16 +3,19 @@
// found in the LICENSE file.
#include "tools/gn/analyzer.h"
+
+#include <tools/gn/c_tool.h>
#include "tools/gn/build_settings.h"
#include "tools/gn/builder.h"
#include "tools/gn/config.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/loader.h"
#include "tools/gn/pool.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
#include "tools/gn/substitution_list.h"
#include "tools/gn/target.h"
-#include "tools/gn/tool.h"
+#include <tools/gn/tool.h>
#include "tools/gn/toolchain.h"
#include "util/test/test.h"
@@ -378,10 +381,10 @@ TEST_F(AnalyzerTest, AffectedToolchainpropagatesToDependentTargets) {
// The tool is not used anywhere, but is required to construct the dependency
// between a target and the toolchain.
- std::unique_ptr<Tool> fake_tool(new Tool());
+ std::unique_ptr<Tool> fake_tool = Tool::CreateTool(CTool::kCToolLink);
fake_tool->set_outputs(
SubstitutionList::MakeForTest("//out/debug/output.txt"));
- toolchain->SetTool(Toolchain::TYPE_LINK, std::move(fake_tool));
+ toolchain->SetTool(std::move(fake_tool));
builder_.ItemDefined(std::unique_ptr<Item>(target));
builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
diff --git a/gn/tools/gn/args.cc b/gn/tools/gn/args.cc
index d1d7611eb4d..802c3731d5a 100644
--- a/gn/tools/gn/args.cc
+++ b/gn/tools/gn/args.cc
@@ -4,6 +4,7 @@
#include "tools/gn/args.h"
+#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
#include "tools/gn/string_utils.h"
#include "tools/gn/variables.h"
@@ -267,9 +268,22 @@ Args::ValueWithOverrideMap Args::GetAllArguments() const {
std::lock_guard<std::mutex> lock(lock_);
- // Default values.
+ // Sort the keys from declared_arguments_per_toolchain_ so
+ // the return value will be deterministic.
+ std::vector<const Settings*> keys;
+ keys.reserve(declared_arguments_per_toolchain_.size());
for (const auto& map_pair : declared_arguments_per_toolchain_) {
- for (const auto& arg : map_pair.second)
+ keys.push_back(map_pair.first);
+ }
+ std::sort(keys.begin(), keys.end(),
+ [](const Settings* a, const Settings* b) -> bool {
+ return a->toolchain_label() < b->toolchain_label();
+ });
+
+ // Default values.
+ for (const auto& key : keys) {
+ const auto& value = declared_arguments_per_toolchain_[key];
+ for (const auto& arg : value)
result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second)));
}
diff --git a/gn/tools/gn/binary_target_generator.cc b/gn/tools/gn/binary_target_generator.cc
index 60af24237e4..4feadbabb68 100644
--- a/gn/tools/gn/binary_target_generator.cc
+++ b/gn/tools/gn/binary_target_generator.cc
@@ -9,6 +9,9 @@
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/rust_values_generator.h"
+#include "tools/gn/rust_variables.h"
#include "tools/gn/scope.h"
#include "tools/gn/settings.h"
#include "tools/gn/value_extractors.h"
@@ -60,6 +63,16 @@ void BinaryTargetGenerator::DoRun() {
if (!FillCompleteStaticLib())
return;
+ if (!ValidateSources())
+ return;
+
+ if (target_->source_types_used().RustSourceUsed()) {
+ RustTargetGenerator rustgen(target_, scope_, function_call_, err_);
+ rustgen.Run();
+ if (err_->has_error())
+ return;
+ }
+
// Config values (compiler flags, etc.) set directly on this target.
ConfigValuesGenerator gen(&target_->config_values(), scope_,
scope_->GetSourceDir(), err_);
@@ -68,6 +81,40 @@ void BinaryTargetGenerator::DoRun() {
return;
}
+bool BinaryTargetGenerator::FillSources() {
+ bool ret = TargetGenerator::FillSources();
+ for (std::size_t i = 0; i < target_->sources().size(); ++i) {
+ const auto& source = target_->sources()[i];
+ switch (source.type()) {
+ case SourceFile::SOURCE_CPP:
+ case SourceFile::SOURCE_H:
+ case SourceFile::SOURCE_C:
+ case SourceFile::SOURCE_M:
+ case SourceFile::SOURCE_MM:
+ case SourceFile::SOURCE_S:
+ case SourceFile::SOURCE_ASM:
+ case SourceFile::SOURCE_O:
+ case SourceFile::SOURCE_DEF:
+ case SourceFile::SOURCE_GO:
+ case SourceFile::SOURCE_RS:
+ case SourceFile::SOURCE_RC:
+ // These are allowed.
+ break;
+ case SourceFile::SOURCE_UNKNOWN:
+ case SourceFile::SOURCE_NUMTYPES:
+ *err_ =
+ Err(scope_->GetValue(variables::kSources, true)->list_value()[i],
+ std::string("Only source, header, and object files belong in "
+ "the sources of a ") +
+ Target::GetStringForOutputType(target_->output_type()) +
+ ". " + source.value() + " is not one of the valid types.");
+ }
+
+ target_->source_types_used().Set(source.type());
+ }
+ return ret;
+}
+
bool BinaryTargetGenerator::FillCompleteStaticLib() {
if (target_->output_type() == Target::STATIC_LIBRARY) {
const Value* value = scope_->GetValue(variables::kCompleteStaticLib, true);
@@ -132,16 +179,6 @@ bool BinaryTargetGenerator::FillOutputDir() {
return true;
}
-bool BinaryTargetGenerator::FillOutputExtension() {
- const Value* value = scope_->GetValue(variables::kOutputExtension, true);
- if (!value)
- return true;
- if (!value->VerifyTypeIs(Value::STRING, err_))
- return false;
- target_->set_output_extension(value->string_value());
- return true;
-}
-
bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
const Value* value =
scope_->GetValue(variables::kAllowCircularIncludesFrom, true);
@@ -179,3 +216,20 @@ bool BinaryTargetGenerator::FillAllowCircularIncludesFrom() {
target_->allow_circular_includes_from().insert(cur);
return true;
}
+
+bool BinaryTargetGenerator::ValidateSources() {
+ // For Rust targets, if the only source file is the root `sources` can be
+ // omitted/empty.
+ if (scope_->GetValue(variables::kRustCrateRoot, false)) {
+ target_->source_types_used().Set(SourceFile::SOURCE_RS);
+ }
+
+ if (target_->source_types_used().MixedSourceUsed()) {
+ *err_ =
+ Err(function_call_, "More than one language used in target sources.",
+ "Mixed sources are not allowed, unless they are "
+ "compilation-compatible (e.g. Objective C and C++).");
+ return false;
+ }
+ return true;
+}
diff --git a/gn/tools/gn/binary_target_generator.h b/gn/tools/gn/binary_target_generator.h
index 40fc3141e26..b88da50d128 100644
--- a/gn/tools/gn/binary_target_generator.h
+++ b/gn/tools/gn/binary_target_generator.h
@@ -22,6 +22,7 @@ class BinaryTargetGenerator : public TargetGenerator {
protected:
void DoRun() override;
+ bool FillSources() override;
private:
bool FillCompleteStaticLib();
@@ -29,8 +30,8 @@ class BinaryTargetGenerator : public TargetGenerator {
bool FillOutputName();
bool FillOutputPrefixOverride();
bool FillOutputDir();
- bool FillOutputExtension();
bool FillAllowCircularIncludesFrom();
+ bool ValidateSources();
Target::OutputType output_type_;
diff --git a/gn/tools/gn/build_settings.cc b/gn/tools/gn/build_settings.cc
index 3cc211bb515..711c6b8c852 100644
--- a/gn/tools/gn/build_settings.cc
+++ b/gn/tools/gn/build_settings.cc
@@ -22,8 +22,6 @@ BuildSettings::BuildSettings(const BuildSettings& other)
build_dir_(other.build_dir_),
build_args_(other.build_args_) {}
-BuildSettings::~BuildSettings() = default;
-
void BuildSettings::SetRootTargetLabel(const Label& r) {
root_target_label_ = r;
}
diff --git a/gn/tools/gn/build_settings.h b/gn/tools/gn/build_settings.h
index 51a7d6b4bb3..646472f72d7 100644
--- a/gn/tools/gn/build_settings.h
+++ b/gn/tools/gn/build_settings.h
@@ -30,7 +30,6 @@ class BuildSettings {
BuildSettings();
BuildSettings(const BuildSettings& other);
- ~BuildSettings();
// Root target label.
const Label& root_target_label() const { return root_target_label_; }
diff --git a/gn/tools/gn/builder.cc b/gn/tools/gn/builder.cc
index 1d04db01331..fc271c4a34c 100644
--- a/gn/tools/gn/builder.cc
+++ b/gn/tools/gn/builder.cc
@@ -276,14 +276,13 @@ bool Builder::ToolchainDefined(BuilderRecord* record, Err* err) {
if (!AddDeps(record, toolchain->deps(), err))
return false;
- for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
- Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
- Tool* tool = toolchain->GetTool(tool_type);
- if (!tool || tool->pool().label.is_null())
+ for (const auto& tool : toolchain->tools()) {
+ if (tool.second->pool().label.is_null())
continue;
BuilderRecord* dep_record = GetOrCreateRecordOfType(
- tool->pool().label, tool->pool().origin, BuilderRecord::ITEM_POOL, err);
+ tool.second->pool().label, tool.second->pool().origin,
+ BuilderRecord::ITEM_POOL, err);
if (!dep_record)
return false;
record->AddDep(dep_record);
@@ -565,23 +564,21 @@ bool Builder::ResolveActionValues(ActionValues* action_values, Err* err) {
}
bool Builder::ResolvePools(Toolchain* toolchain, Err* err) {
- for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
- Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
- Tool* tool = toolchain->GetTool(tool_type);
- if (!tool || tool->pool().label.is_null())
+ for (const auto& tool : toolchain->tools()) {
+ if (tool.second->pool().label.is_null())
continue;
- BuilderRecord* record =
- GetResolvedRecordOfType(tool->pool().label, toolchain->defined_from(),
- BuilderRecord::ITEM_POOL, err);
+ BuilderRecord* record = GetResolvedRecordOfType(
+ tool.second->pool().label, toolchain->defined_from(),
+ BuilderRecord::ITEM_POOL, err);
if (!record) {
- *err = Err(tool->pool().origin, "Pool for tool not defined.",
+ *err = Err(tool.second->pool().origin, "Pool for tool not defined.",
"I was hoping to find a pool " +
- tool->pool().label.GetUserVisibleName(false));
+ tool.second->pool().label.GetUserVisibleName(false));
return false;
}
- tool->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
+ tool.second->set_pool(LabelPtrPair<Pool>(record->item()->AsPool()));
}
return true;
diff --git a/gn/tools/gn/builder_record.cc b/gn/tools/gn/builder_record.cc
index 36fd465c484..14e87b6709f 100644
--- a/gn/tools/gn/builder_record.cc
+++ b/gn/tools/gn/builder_record.cc
@@ -7,13 +7,7 @@
#include "tools/gn/item.h"
BuilderRecord::BuilderRecord(ItemType type, const Label& label)
- : type_(type),
- label_(label),
- originally_referenced_from_(nullptr),
- should_generate_(false),
- resolved_(false) {}
-
-BuilderRecord::~BuilderRecord() = default;
+ : type_(type), label_(label) {}
// static
const char* BuilderRecord::GetNameForType(ItemType type) {
diff --git a/gn/tools/gn/builder_record.h b/gn/tools/gn/builder_record.h
index dc96f7f56d4..9dcc3664f63 100644
--- a/gn/tools/gn/builder_record.h
+++ b/gn/tools/gn/builder_record.h
@@ -40,7 +40,6 @@ class BuilderRecord {
};
BuilderRecord(ItemType type, const Label& label);
- ~BuilderRecord();
ItemType type() const { return type_; }
const Label& label() const { return label_; }
@@ -97,9 +96,9 @@ class BuilderRecord {
ItemType type_;
Label label_;
std::unique_ptr<Item> item_;
- const ParseNode* originally_referenced_from_;
- bool should_generate_;
- bool resolved_;
+ const ParseNode* originally_referenced_from_ = nullptr;
+ bool should_generate_ = false;
+ bool resolved_ = false;
BuilderRecordSet all_deps_;
BuilderRecordSet unresolved_deps_;
diff --git a/gn/tools/gn/bundle_data.cc b/gn/tools/gn/bundle_data.cc
index 095e98935c1..4251c8fd641 100644
--- a/gn/tools/gn/bundle_data.cc
+++ b/gn/tools/gn/bundle_data.cc
@@ -34,13 +34,13 @@ bool IsSourceFileFromAssetsCatalog(base::StringPiece source,
if (source.ends_with("/Contents.json") && dir.ends_with(".xcassets")) {
is_file_from_asset_catalog = true;
} else if (dir.ends_with(".appiconset") || dir.ends_with(".imageset") ||
- dir.ends_with(".launchimage")) {
+ dir.ends_with(".launchimage") || dir.ends_with(".colorset")) {
dir = FindDirNoTrailingSeparator(dir);
is_file_from_asset_catalog = dir.ends_with(".xcassets");
}
if (is_file_from_asset_catalog && asset_catalog) {
std::string asset_catalog_path = dir.as_string();
- *asset_catalog = SourceFile(SourceFile::SWAP_IN, &asset_catalog_path);
+ *asset_catalog = SourceFile(std::move(asset_catalog_path));
}
return is_file_from_asset_catalog;
}
@@ -162,7 +162,7 @@ bool BundleData::GetOutputsAsSourceFiles(const Settings* settings,
SourceFile BundleData::GetCompiledAssetCatalogPath() const {
DCHECK(!assets_catalog_sources_.empty());
std::string assets_car_path = resources_dir_.value() + "/Assets.car";
- return SourceFile(SourceFile::SWAP_IN, &assets_car_path);
+ return SourceFile(std::move(assets_car_path));
}
SourceFile BundleData::GetBundleRootDirOutput(const Settings* settings) const {
@@ -171,7 +171,7 @@ SourceFile BundleData::GetBundleRootDirOutput(const Settings* settings) const {
if (last_separator != std::string::npos)
root_dir_value = root_dir_value.substr(0, last_separator);
- return SourceFile(SourceFile::SWAP_IN, &root_dir_value);
+ return SourceFile(std::move(root_dir_value));
}
SourceDir BundleData::GetBundleRootDirOutputAsDir(
diff --git a/gn/tools/gn/bundle_data_target_generator.cc b/gn/tools/gn/bundle_data_target_generator.cc
index 23f26fd679d..cfd29031a10 100644
--- a/gn/tools/gn/bundle_data_target_generator.cc
+++ b/gn/tools/gn/bundle_data_target_generator.cc
@@ -55,10 +55,10 @@ bool BundleDataTargetGenerator::FillOutputs() {
return false;
// Check the substitutions used are valid for this purpose.
- for (SubstitutionType type : outputs.required_types()) {
+ for (const Substitution* type : outputs.required_types()) {
if (!IsValidBundleDataSubstitution(type)) {
*err_ = Err(value->origin(), "Invalid substitution type.",
- "The substitution " + std::string(kSubstitutionNames[type]) +
+ "The substitution " + std::string(type->name) +
" isn't valid for something\n"
"operating on a bundle_data file such as this.");
return false;
diff --git a/gn/tools/gn/bundle_file_rule.cc b/gn/tools/gn/bundle_file_rule.cc
index 5dffb07e929..2b81cdfb008 100644
--- a/gn/tools/gn/bundle_file_rule.cc
+++ b/gn/tools/gn/bundle_file_rule.cc
@@ -51,50 +51,43 @@ bool BundleFileRule::ApplyPatternToSource(const Settings* settings,
Err* err) const {
std::string output_path;
for (const auto& subrange : pattern_.ranges()) {
- switch (subrange.type) {
- case SUBSTITUTION_LITERAL:
- output_path.append(subrange.literal);
- break;
- case SUBSTITUTION_BUNDLE_ROOT_DIR:
- if (bundle_data.contents_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleRootDir);
- return false;
- }
- output_path.append(bundle_data.root_dir().value());
- break;
- case SUBSTITUTION_BUNDLE_CONTENTS_DIR:
- if (bundle_data.contents_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleContentsDir);
- return false;
- }
- output_path.append(bundle_data.contents_dir().value());
- break;
- case SUBSTITUTION_BUNDLE_RESOURCES_DIR:
- if (bundle_data.resources_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(settings, target, this,
- variables::kBundleResourcesDir);
- return false;
- }
- output_path.append(bundle_data.resources_dir().value());
- break;
- case SUBSTITUTION_BUNDLE_EXECUTABLE_DIR:
- if (bundle_data.executable_dir().is_null()) {
- *err = ErrMissingPropertyForExpansion(
- settings, target, this, variables::kBundleExecutableDir);
- return false;
- }
- output_path.append(bundle_data.executable_dir().value());
- break;
- default:
- output_path.append(SubstitutionWriter::GetSourceSubstitution(
- target_, target_->settings(), source_file, subrange.type,
- SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
- break;
+ if (subrange.type == &SubstitutionLiteral) {
+ output_path.append(subrange.literal);
+ } else if (subrange.type == &SubstitutionBundleRootDir) {
+ if (bundle_data.contents_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleRootDir);
+ return false;
+ }
+ output_path.append(bundle_data.root_dir().value());
+ } else if (subrange.type == &SubstitutionBundleContentsDir) {
+ if (bundle_data.contents_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleContentsDir);
+ return false;
+ }
+ output_path.append(bundle_data.contents_dir().value());
+ } else if (subrange.type == &SubstitutionBundleResourcesDir) {
+ if (bundle_data.resources_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleResourcesDir);
+ return false;
+ }
+ output_path.append(bundle_data.resources_dir().value());
+ } else if (subrange.type == &SubstitutionBundleExecutableDir) {
+ if (bundle_data.executable_dir().is_null()) {
+ *err = ErrMissingPropertyForExpansion(settings, target, this,
+ variables::kBundleExecutableDir);
+ return false;
+ }
+ output_path.append(bundle_data.executable_dir().value());
+ } else {
+ output_path.append(SubstitutionWriter::GetSourceSubstitution(
+ target_, target_->settings(), source_file, subrange.type,
+ SubstitutionWriter::OUTPUT_ABSOLUTE, SourceDir()));
}
}
- *expanded_source_file = SourceFile(SourceFile::SWAP_IN, &output_path);
+ *expanded_source_file = SourceFile(std::move(output_path));
return true;
}
diff --git a/gn/tools/gn/c_include_iterator.cc b/gn/tools/gn/c_include_iterator.cc
index 0c5476daaf7..540d1189217 100644
--- a/gn/tools/gn/c_include_iterator.cc
+++ b/gn/tools/gn/c_include_iterator.cc
@@ -120,11 +120,7 @@ bool HasNoCheckAnnotation(const base::StringPiece& line) {
const int CIncludeIterator::kMaxNonIncludeLines = 10;
CIncludeIterator::CIncludeIterator(const InputFile* input)
- : input_file_(input),
- file_(input->contents()),
- offset_(0),
- line_number_(0),
- lines_since_last_include_(0) {}
+ : input_file_(input), file_(input->contents()) {}
CIncludeIterator::~CIncludeIterator() = default;
diff --git a/gn/tools/gn/c_include_iterator.h b/gn/tools/gn/c_include_iterator.h
index e81a60c9383..86a0954a43c 100644
--- a/gn/tools/gn/c_include_iterator.h
+++ b/gn/tools/gn/c_include_iterator.h
@@ -43,13 +43,13 @@ class CIncludeIterator {
base::StringPiece file_;
// 0-based offset into the file.
- size_t offset_;
+ size_t offset_ = 0;
- int line_number_; // One-based. Indicates the last line we read.
+ int line_number_ = 0; // One-based. Indicates the last line we read.
// Number of lines we've processed since seeing the last include (or the
// beginning of the file) with some exceptions.
- int lines_since_last_include_;
+ int lines_since_last_include_ = 0;
DISALLOW_COPY_AND_ASSIGN(CIncludeIterator);
};
diff --git a/gn/tools/gn/c_substitution_type.cc b/gn/tools/gn/c_substitution_type.cc
new file mode 100644
index 00000000000..eec8c66350a
--- /dev/null
+++ b/gn/tools/gn/c_substitution_type.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 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 "tools/gn/c_substitution_type.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "tools/gn/err.h"
+
+const SubstitutionTypes CSubstitutions = {
+ &CSubstitutionAsmFlags, &CSubstitutionCFlags,
+ &CSubstitutionCFlagsC, &CSubstitutionCFlagsCc,
+ &CSubstitutionCFlagsObjC, &CSubstitutionCFlagsObjCc,
+ &CSubstitutionDefines, &CSubstitutionIncludeDirs,
+
+ &CSubstitutionLinkerInputs, &CSubstitutionLinkerInputsNewline,
+ &CSubstitutionLdFlags, &CSubstitutionLibs,
+ &CSubstitutionOutputExtension,
+ &CSubstitutionSoLibs,
+
+ &CSubstitutionArFlags,
+};
+
+// Valid for compiler tools.
+const Substitution CSubstitutionAsmFlags = {"{{asmflags}}", "asmflags"};
+const Substitution CSubstitutionCFlags = {"{{cflags}}", "cflags"};
+const Substitution CSubstitutionCFlagsC = {"{{cflags_c}}", "cflags_c"};
+const Substitution CSubstitutionCFlagsCc = {"{{cflags_cc}}", "cflags_cc"};
+const Substitution CSubstitutionCFlagsObjC = {"{{cflags_objc}}", "cflags_objc"};
+const Substitution CSubstitutionCFlagsObjCc = {"{{cflags_objcc}}",
+ "cflags_objcc"};
+const Substitution CSubstitutionDefines = {"{{defines}}", "defines"};
+const Substitution CSubstitutionIncludeDirs = {"{{include_dirs}}",
+ "include_dirs"};
+
+// Valid for linker tools.
+const Substitution CSubstitutionLinkerInputs = {"{{inputs}}", "in"};
+const Substitution CSubstitutionLinkerInputsNewline = {"{{inputs_newline}}",
+ "in_newline"};
+const Substitution CSubstitutionLdFlags = {"{{ldflags}}", "ldflags"};
+const Substitution CSubstitutionLibs = {"{{libs}}", "libs"};
+const Substitution CSubstitutionOutputExtension = {"{{output_extension}}",
+ "output_extension"};
+const Substitution CSubstitutionSoLibs = {"{{solibs}}", "solibs"};
+
+// Valid for alink only.
+const Substitution CSubstitutionArFlags = {"{{arflags}}", "arflags"};
+
+bool IsValidCompilerSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
+ type == &SubstitutionSource || type == &CSubstitutionAsmFlags ||
+ type == &CSubstitutionCFlags || type == &CSubstitutionCFlagsC ||
+ type == &CSubstitutionCFlagsCc || type == &CSubstitutionCFlagsObjC ||
+ type == &CSubstitutionCFlagsObjCc || type == &CSubstitutionDefines ||
+ type == &CSubstitutionIncludeDirs;
+}
+
+bool IsValidCompilerOutputsSubstitution(const Substitution* type) {
+ // All tool types except "output" (which would be infinitely recursive).
+ return (IsValidToolSubstitution(type) && type != &SubstitutionOutput) ||
+ IsValidSourceSubstitution(type);
+}
+
+bool IsValidLinkerSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &SubstitutionOutputDir ||
+ type == &CSubstitutionLinkerInputs ||
+ type == &CSubstitutionLinkerInputsNewline ||
+ type == &CSubstitutionLdFlags || type == &CSubstitutionLibs ||
+ type == &CSubstitutionOutputExtension || type == &CSubstitutionSoLibs;
+}
+
+bool IsValidLinkerOutputsSubstitution(const Substitution* type) {
+ // All valid compiler outputs plus the output extension.
+ return IsValidCompilerOutputsSubstitution(type) ||
+ type == &SubstitutionOutputDir || type == &CSubstitutionOutputExtension;
+}
+
+bool IsValidALinkSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &SubstitutionOutputDir ||
+ type == &CSubstitutionLinkerInputs ||
+ type == &CSubstitutionLinkerInputsNewline ||
+ type == &CSubstitutionArFlags || type == &CSubstitutionOutputExtension;
+}
diff --git a/gn/tools/gn/c_substitution_type.h b/gn/tools/gn/c_substitution_type.h
new file mode 100644
index 00000000000..ed543f0ad48
--- /dev/null
+++ b/gn/tools/gn/c_substitution_type.h
@@ -0,0 +1,44 @@
+// Copyright 2019 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_C_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_C_SUBSTITUTION_TYPE_H_
+
+#include <set>
+#include <vector>
+
+#include "tools/gn/substitution_type.h"
+
+// The set of substitutions available to all tools.
+extern const SubstitutionTypes CSubstitutions;
+
+// Valid for compiler tools.
+extern const Substitution CSubstitutionAsmFlags;
+extern const Substitution CSubstitutionCFlags;
+extern const Substitution CSubstitutionCFlagsC;
+extern const Substitution CSubstitutionCFlagsCc;
+extern const Substitution CSubstitutionCFlagsObjC;
+extern const Substitution CSubstitutionCFlagsObjCc;
+extern const Substitution CSubstitutionDefines;
+extern const Substitution CSubstitutionIncludeDirs;
+
+// Valid for linker tools.
+extern const Substitution CSubstitutionLinkerInputs;
+extern const Substitution CSubstitutionLinkerInputsNewline;
+extern const Substitution CSubstitutionLdFlags;
+extern const Substitution CSubstitutionLibs;
+extern const Substitution CSubstitutionOutputExtension;
+extern const Substitution CSubstitutionSoLibs;
+
+// Valid for alink only.
+extern const Substitution CSubstitutionArFlags;
+
+// Both compiler and linker tools.
+bool IsValidCompilerSubstitution(const Substitution* type);
+bool IsValidCompilerOutputsSubstitution(const Substitution* type);
+bool IsValidLinkerSubstitution(const Substitution* type);
+bool IsValidLinkerOutputsSubstitution(const Substitution* type);
+bool IsValidALinkSubstitution(const Substitution* type);
+
+#endif // TOOLS_GN_C_SUBSTITUTION_TYPE_H_
diff --git a/gn/tools/gn/c_tool.cc b/gn/tools/gn/c_tool.cc
new file mode 100644
index 00000000000..59452fbe839
--- /dev/null
+++ b/gn/tools/gn/c_tool.cc
@@ -0,0 +1,232 @@
+// Copyright 2019 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 "tools/gn/c_tool.h"
+#include "tools/gn/c_substitution_type.h"
+#include "tools/gn/target.h"
+
+const char* CTool::kCToolCc = "cc";
+const char* CTool::kCToolCxx = "cxx";
+const char* CTool::kCToolObjC = "objc";
+const char* CTool::kCToolObjCxx = "objcxx";
+const char* CTool::kCToolRc = "rc";
+const char* CTool::kCToolAsm = "asm";
+const char* CTool::kCToolAlink = "alink";
+const char* CTool::kCToolSolink = "solink";
+const char* CTool::kCToolSolinkModule = "solink_module";
+const char* CTool::kCToolLink = "link";
+
+CTool::CTool(const char* n)
+ : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
+ CHECK(ValidateName(n));
+}
+
+CTool::~CTool() = default;
+
+CTool* CTool::AsC() {
+ return this;
+}
+const CTool* CTool::AsC() const {
+ return this;
+}
+
+bool CTool::ValidateName(const char* name) const {
+ return name == kCToolCc || name == kCToolCxx || name == kCToolObjC ||
+ name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm ||
+ name == kCToolAlink || name == kCToolSolink ||
+ name == kCToolSolinkModule || name == kCToolLink;
+}
+
+void CTool::SetComplete() {
+ SetToolComplete();
+ link_output_.FillRequiredTypes(&substitution_bits_);
+ depend_output_.FillRequiredTypes(&substitution_bits_);
+}
+
+bool CTool::ValidateRuntimeOutputs(Err* err) {
+ if (runtime_outputs().list().empty())
+ return true; // Empty is always OK.
+
+ if (name_ != kCToolSolink && name_ != kCToolSolinkModule &&
+ name_ != kCToolLink) {
+ *err = Err(defined_from(), "This tool specifies runtime_outputs.",
+ "This is only valid for linker tools (alink doesn't count).");
+ return false;
+ }
+
+ for (const SubstitutionPattern& pattern : runtime_outputs().list()) {
+ if (!IsPatternInOutputList(outputs(), pattern)) {
+ *err = Err(defined_from(), "This tool's runtime_outputs is bad.",
+ "It must be a subset of the outputs. The bad one is:\n " +
+ pattern.AsString());
+ return false;
+ }
+ }
+ return true;
+}
+
+// Validates either link_output or depend_output. To generalize to either, pass
+// the associated pattern, and the variable name that should appear in error
+// messages.
+bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+ const char* variable_name,
+ Err* err) {
+ if (pattern.empty())
+ return true; // Empty is always OK.
+
+ // It should only be specified for certain tool types.
+ if (name_ != kCToolSolink && name_ != kCToolSolinkModule) {
+ *err = Err(defined_from(),
+ "This tool specifies a " + std::string(variable_name) + ".",
+ "This is only valid for solink and solink_module tools.");
+ return false;
+ }
+
+ if (!IsPatternInOutputList(outputs(), pattern)) {
+ *err = Err(defined_from(), "This tool's link_output is bad.",
+ "It must match one of the outputs.");
+ return false;
+ }
+
+ return true;
+}
+
+bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) {
+ const Value* value = scope->GetValue("precompiled_header_type", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Accept empty string, do nothing (default is "no PCH").
+
+ if (value->string_value() == "gcc") {
+ set_precompiled_header_type(PCH_GCC);
+ return true;
+ } else if (value->string_value() == "msvc") {
+ set_precompiled_header_type(PCH_MSVC);
+ return true;
+ }
+ *err = Err(*value, "Invalid precompiled_header_type",
+ "Must either be empty, \"gcc\", or \"msvc\".");
+ return false;
+}
+
+bool CTool::ReadDepsFormat(Scope* scope, Err* err) {
+ const Value* value = scope->GetValue("depsformat", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value() == "gcc") {
+ set_depsformat(DEPS_GCC);
+ } else if (value->string_value() == "msvc") {
+ set_depsformat(DEPS_MSVC);
+ } else {
+ *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
+ return false;
+ }
+ return true;
+}
+
+bool CTool::ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (list.list().empty()) {
+ *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
+ return false;
+ }
+
+ for (const auto& cur_type : list.required_types()) {
+ if (!ValidateOutputSubstitution(cur_type)) {
+ *err = Err(*value, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+
+ *field = std::move(list);
+ return true;
+}
+
+bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ if (!Tool::InitTool(scope, toolchain, err)) {
+ return false;
+ }
+
+ // All C tools should have outputs.
+ if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
+ return false;
+ }
+
+ if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
+ !ReadString(scope, "lib_switch", &lib_switch_, err) ||
+ !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
+ !ReadPattern(scope, "link_output", &link_output_, err) ||
+ !ReadPattern(scope, "depend_output", &depend_output_, err)) {
+ return false;
+ }
+
+ // Validate link_output and depend_output.
+ if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) {
+ return false;
+ }
+ if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) {
+ return false;
+ }
+ if ((!link_output().empty() && depend_output().empty()) ||
+ (link_output().empty() && !depend_output().empty())) {
+ *err = Err(defined_from(),
+ "Both link_output and depend_output should either "
+ "be specified or they should both be empty.");
+ return false;
+ }
+
+ if (!ValidateRuntimeOutputs(err)) {
+ return false;
+ }
+ return true;
+}
+
+bool CTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
+ name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+ return IsValidCompilerSubstitution(sub_type);
+ else if (name_ == kCToolAlink)
+ return IsValidALinkSubstitution(sub_type);
+ else if (name_ == kCToolSolink || name_ == kCToolSolinkModule ||
+ name_ == kCToolLink)
+ return IsValidLinkerSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
+
+bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const {
+ if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
+ name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
+ return IsValidCompilerOutputsSubstitution(sub_type);
+ // ALink uses the standard output file patterns as other linker tools.
+ else if (name_ == kCToolAlink || name_ == kCToolSolink ||
+ name_ == kCToolSolinkModule || name_ == kCToolLink)
+ return IsValidLinkerOutputsSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/tools/gn/c_tool.h b/gn/tools/gn/c_tool.h
new file mode 100644
index 00000000000..07dfcbe26e3
--- /dev/null
+++ b/gn/tools/gn/c_tool.h
@@ -0,0 +1,137 @@
+// Copyright 2019 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_C_TOOL_H_
+#define TOOLS_GN_C_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/tool.h"
+#include "tools/gn/toolchain.h"
+
+class CTool : public Tool {
+ public:
+ // C compiler tools
+ static const char* kCToolCc;
+ static const char* kCToolCxx;
+ static const char* kCToolObjC;
+ static const char* kCToolObjCxx;
+ static const char* kCToolRc;
+ static const char* kCToolAsm;
+
+ // C linker tools
+ static const char* kCToolAlink;
+ static const char* kCToolSolink;
+ static const char* kCToolSolinkModule;
+ static const char* kCToolLink;
+
+ enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+
+ enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+
+ CTool(const char* n);
+ ~CTool();
+
+ // 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;
+
+ CTool* AsC() override;
+ const CTool* AsC() const override;
+
+ // Getters/setters ----------------------------------------------------------
+ //
+ // After the tool has had its attributes set, the caller must call
+ // SetComplete(), at which point no other changes can be made.
+
+ DepsFormat depsformat() const { return depsformat_; }
+ void set_depsformat(DepsFormat f) {
+ DCHECK(!complete_);
+ depsformat_ = f;
+ }
+
+ PrecompiledHeaderType precompiled_header_type() const {
+ return precompiled_header_type_;
+ }
+ void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
+ DCHECK(!complete_);
+ precompiled_header_type_ = pch_type;
+ }
+
+ const std::string& lib_switch() const { return lib_switch_; }
+ void set_lib_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_switch_ = std::move(s);
+ }
+
+ const std::string& lib_dir_switch() const { return lib_dir_switch_; }
+ void set_lib_dir_switch(std::string s) {
+ DCHECK(!complete_);
+ lib_dir_switch_ = std::move(s);
+ }
+
+ // Should match files in the outputs() if nonempty.
+ const SubstitutionPattern& link_output() const { return link_output_; }
+ void set_link_output(SubstitutionPattern link_out) {
+ DCHECK(!complete_);
+ link_output_ = std::move(link_out);
+ }
+
+ const SubstitutionPattern& depend_output() const { return depend_output_; }
+ void set_depend_output(SubstitutionPattern dep_out) {
+ DCHECK(!complete_);
+ depend_output_ = std::move(dep_out);
+ }
+
+ // Other functions ----------------------------------------------------------
+
+ // Returns true if this tool has separate outputs for dependency tracking
+ // and linking.
+ bool has_separate_solink_files() const {
+ return !link_output_.empty() || !depend_output_.empty();
+ }
+
+ private:
+ // Initialization functions -------------------------------------------------
+ //
+ // Initialization methods used by InitTool(). If successful, will set the
+ // field and return true, otherwise will return false. Must be called before
+ // SetComplete().
+ bool ValidateOutputSubstitution(const Substitution* sub_type) const;
+ bool ValidateRuntimeOutputs(Err* err);
+ // Validates either link_output or depend_output. To generalize to either,
+ // pass
+ // the associated pattern, and the variable name that should appear in error
+ // messages.
+ bool ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
+ const char* variable_name,
+ Err* err);
+ bool ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err);
+ bool ReadPrecompiledHeaderType(Scope* scope, Err* err);
+ bool ReadDepsFormat(Scope* scope, Err* err);
+
+ DepsFormat depsformat_;
+ PrecompiledHeaderType precompiled_header_type_;
+ std::string lib_switch_;
+ std::string lib_dir_switch_;
+ SubstitutionPattern link_output_;
+ SubstitutionPattern depend_output_;
+
+ DISALLOW_COPY_AND_ASSIGN(CTool);
+};
+
+#endif // TOOLS_GN_C_TOOL_H_
diff --git a/gn/tools/gn/command_desc.cc b/gn/tools/gn/command_desc.cc
index a499384c559..6531a8e94a2 100644
--- a/gn/tools/gn/command_desc.cc
+++ b/gn/tools/gn/command_desc.cc
@@ -229,20 +229,26 @@ void DepsHandler(const std::string& name,
}
// Outputs need special processing when output patterns are present.
-void ProcessOutputs(base::DictionaryValue* target) {
+void ProcessOutputs(base::DictionaryValue* target, bool files_only) {
base::ListValue* patterns = nullptr;
base::ListValue* outputs = nullptr;
target->GetList("output_patterns", &patterns);
target->GetList(variables::kOutputs, &outputs);
+ int indent = 0;
if (outputs || patterns) {
- OutputString("\noutputs\n");
- int indent = 1;
+ if (!files_only) {
+ OutputString("\noutputs\n");
+ indent = 1;
+ }
if (patterns) {
- OutputString(" Output patterns\n");
- indent = 2;
+ if (!files_only) {
+ OutputString(" Output patterns\n");
+ indent = 2;
+ }
PrintValue(patterns, indent);
- OutputString("\n Resolved output file list\n");
+ if (!files_only)
+ OutputString("\n Resolved output file list\n");
}
if (outputs)
PrintValue(outputs, indent);
@@ -323,6 +329,10 @@ bool PrintTarget(const Target* target,
}
// Print single value
if (!what.empty() && dict->size() == 1 && single_target) {
+ if (what == variables::kOutputs) {
+ ProcessOutputs(dict.get(), true);
+ return true;
+ }
base::DictionaryValue::Iterator iter(*dict);
auto pair = handler_map.find(what);
if (pair != handler_map.end())
@@ -352,7 +362,7 @@ bool PrintTarget(const Target* target,
HandleProperty(variables::kScript, handler_map, v, dict);
HandleProperty(variables::kArgs, handler_map, v, dict);
HandleProperty(variables::kDepfile, handler_map, v, dict);
- ProcessOutputs(dict.get());
+ ProcessOutputs(dict.get(), false);
HandleProperty("bundle_data", handler_map, v, dict);
HandleProperty(variables::kArflags, handler_map, v, dict);
HandleProperty(variables::kAsmflags, handler_map, v, dict);
diff --git a/gn/tools/gn/command_format.cc b/gn/tools/gn/command_format.cc
index 5b9580bdbe4..4249c2f91f1 100644
--- a/gn/tools/gn/command_format.cc
+++ b/gn/tools/gn/command_format.cc
@@ -21,6 +21,7 @@
#include "tools/gn/scheduler.h"
#include "tools/gn/setup.h"
#include "tools/gn/source_file.h"
+#include "tools/gn/switches.h"
#include "tools/gn/tokenizer.h"
namespace commands {
@@ -1168,6 +1169,9 @@ int RunFormat(const std::vector<std::string>& args) {
from_stdin = false;
}
+ bool quiet =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet);
+
if (from_stdin) {
if (args.size() != 0) {
Err(Location(), "Expecting no arguments when reading from stdin.\n")
@@ -1227,7 +1231,10 @@ int RunFormat(const std::vector<std::string>& args) {
.PrintToStdout();
return 1;
}
- printf("Wrote formatted to '%s'.\n", FilePathToUTF8(to_write).c_str());
+ if (!quiet) {
+ printf("Wrote formatted to '%s'.\n",
+ FilePathToUTF8(to_write).c_str());
+ }
}
}
}
diff --git a/gn/tools/gn/command_help.cc b/gn/tools/gn/command_help.cc
index 543f5d19e8d..7145da408c9 100644
--- a/gn/tools/gn/command_help.cc
+++ b/gn/tools/gn/command_help.cc
@@ -13,6 +13,7 @@
#include "tools/gn/input_conversion.h"
#include "tools/gn/label.h"
#include "tools/gn/label_pattern.h"
+#include "tools/gn/metadata.h"
#include "tools/gn/ninja_build_writer.h"
#include "tools/gn/output_conversion.h"
#include "tools/gn/parser.h"
@@ -79,21 +80,24 @@ void PrintToplevelHelp() {
"grammar");
PrintShortHelp(
"input_conversion: Processing input from exec_script and read_file.",
- "input_conversion");
+ "io_conversion");
PrintShortHelp("label_pattern: Matching more than one label.",
"label_pattern");
PrintShortHelp("labels: About labels.", "labels");
+ PrintShortHelp("metadata_collection: About metadata and its collection.",
+ "metadata_collection");
PrintShortHelp("ninja_rules: How Ninja build rules are named.",
"ninja_rules");
PrintShortHelp("nogncheck: Annotating includes for checking.", "nogncheck");
PrintShortHelp(
"output_conversion: Specifies how to transform a value to output.",
- "output_conversion");
+ "io_conversion");
PrintShortHelp("runtime_deps: How runtime dependency computation works.",
"runtime_deps");
PrintShortHelp("source_expansion: Map sources to outputs for scripts.",
"source_expansion");
- PrintShortHelp("switches: Show available command-line switches.", "switch_list");
+ PrintShortHelp("switches: Show available command-line switches.",
+ "switch_list");
}
void PrintSwitchHelp() {
@@ -108,7 +112,8 @@ void PrintSwitchHelp() {
Do "gn help --the_switch_you_want_help_on" for more. Individual commands may
take command-specific switches not listed here. See the help on your specific
command for more.
-)", "switch_list");
+)",
+ "switch_list");
if (is_markdown)
OutputString("```\n", DECORATION_NONE);
@@ -195,6 +200,7 @@ void PrintAllHelp() {
PrintLongHelp(kInputOutputConversion_Help, "io_conversion");
PrintLongHelp(kLabelPattern_Help, "label_pattern");
PrintLongHelp(kLabels_Help, "labels");
+ PrintLongHelp(kMetadata_Help, "metadata_collection");
PrintLongHelp(kNinjaRules_Help, "ninja_rules");
PrintLongHelp(kNoGnCheck_Help, "nogncheck");
PrintLongHelp(kRuntimeDeps_Help, "runtime_deps");
@@ -335,6 +341,9 @@ int RunHelp(const std::vector<std::string>& args) {
};
random_topics["label_pattern"] = []() { PrintLongHelp(kLabelPattern_Help); };
random_topics["labels"] = []() { PrintLongHelp(kLabels_Help); };
+ random_topics["metadata_collection"] = []() {
+ PrintLongHelp(kMetadata_Help);
+ };
random_topics["ninja_rules"] = []() { PrintLongHelp(kNinjaRules_Help); };
random_topics["nogncheck"] = []() { PrintLongHelp(kNoGnCheck_Help); };
random_topics["runtime_deps"] = []() { PrintLongHelp(kRuntimeDeps_Help); };
diff --git a/gn/tools/gn/command_meta.cc b/gn/tools/gn/command_meta.cc
index 060373170a1..bb689e901e8 100644
--- a/gn/tools/gn/command_meta.cc
+++ b/gn/tools/gn/command_meta.cc
@@ -114,8 +114,15 @@ int RunMeta(const std::vector<std::string>& args) {
walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
Err err;
std::set<const Target*> targets_walked;
+ SourceDir rebase_source_dir(rebase_dir);
+ // When SourceDir constructor is supplied with an empty string,
+ // a trailing slash will be added. This prevent SourceDir::is_null()
+ // from returning true. Explicitly remove this traling slash here.
+ if (rebase_dir.empty()) {
+ rebase_source_dir = SourceDir();
+ }
std::vector<Value> result =
- WalkMetadata(targets, data_keys, walk_keys, SourceDir(rebase_dir),
+ WalkMetadata(targets, data_keys, walk_keys, rebase_source_dir,
&targets_walked, &err);
if (err.has_error()) {
err.PrintToStdout();
diff --git a/gn/tools/gn/commands.cc b/gn/tools/gn/commands.cc
index b7cd87131d8..cf387258dd7 100644
--- a/gn/tools/gn/commands.cc
+++ b/gn/tools/gn/commands.cc
@@ -238,7 +238,7 @@ bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
return false;
}
- // Filter into a copy of the vector, then swap to output.
+ // Filter into a copy of the vector, then replace the output.
std::vector<const Target*> result;
result.reserve(targets->size());
@@ -247,7 +247,7 @@ bool ApplyTestonlyFilter(std::vector<const Target*>* targets) {
result.push_back(target);
}
- targets->swap(result);
+ *targets = std::move(result);
return true;
}
@@ -260,7 +260,7 @@ bool ApplyTypeFilter(std::vector<const Target*>* targets) {
if (targets->empty() || type == Target::UNKNOWN)
return true; // Nothing to filter out.
- // Filter into a copy of the vector, then swap to output.
+ // Filter into a copy of the vector, then replace the output.
std::vector<const Target*> result;
result.reserve(targets->size());
@@ -272,7 +272,7 @@ bool ApplyTypeFilter(std::vector<const Target*>* targets) {
result.push_back(target);
}
- targets->swap(result);
+ *targets = std::move(result);
return true;
}
diff --git a/gn/tools/gn/compile_commands_writer.cc b/gn/tools/gn/compile_commands_writer.cc
index 00eb00a3298..208952d253c 100644
--- a/gn/tools/gn/compile_commands_writer.cc
+++ b/gn/tools/gn/compile_commands_writer.cc
@@ -7,9 +7,11 @@
#include <sstream>
#include "base/json/string_escape.h"
-#include "base/strings/stringprintf.h"
#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
#include "tools/gn/builder.h"
+#include "tools/gn/c_substitution_type.h"
+#include "tools/gn/c_tool.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/deps_iterator.h"
#include "tools/gn/escape.h"
@@ -67,33 +69,33 @@ void SetupCompileFlags(const Target* target,
base::EscapeJSONString(includes_out.str(), false, &flags.includes);
std::ostringstream cflags_out;
- WriteOneFlag(target, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
+ WriteOneFlag(target, &CSubstitutionCFlags, false, Tool::kToolNone,
&ConfigValues::cflags, opts, path_output, cflags_out,
/*write_substitution=*/false);
base::EscapeJSONString(cflags_out.str(), false, &flags.cflags);
std::ostringstream cflags_c_out;
- WriteOneFlag(target, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
- Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts, path_output,
+ WriteOneFlag(target, &CSubstitutionCFlagsC, has_precompiled_headers,
+ CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output,
cflags_c_out, /*write_substitution=*/false);
base::EscapeJSONString(cflags_c_out.str(), false, &flags.cflags_c);
std::ostringstream cflags_cc_out;
- WriteOneFlag(target, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
- Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts, path_output,
+ WriteOneFlag(target, &CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output,
cflags_cc_out, /*write_substitution=*/false);
base::EscapeJSONString(cflags_cc_out.str(), false, &flags.cflags_cc);
std::ostringstream cflags_objc_out;
- WriteOneFlag(target, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
- Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts,
- path_output, cflags_objc_out,
+ WriteOneFlag(target, &CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc, opts, path_output,
+ cflags_objc_out,
/*write_substitution=*/false);
base::EscapeJSONString(cflags_objc_out.str(), false, &flags.cflags_objc);
std::ostringstream cflags_objcc_out;
- WriteOneFlag(target, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
- Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts,
+ WriteOneFlag(target, &CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
path_output, cflags_objcc_out, /*write_substitution=*/false);
base::EscapeJSONString(cflags_objcc_out.str(), false, &flags.cflags_objcc);
}
@@ -120,76 +122,64 @@ void WriteCommand(const Target* target,
const CompileFlags& flags,
std::vector<OutputFile>& tool_outputs,
PathOutput& path_output,
- SourceFileType source_type,
- Toolchain::ToolType tool_type,
+ SourceFile::Type source_type,
+ const char* tool_name,
EscapeOptions opts,
std::string* compile_commands) {
EscapeOptions no_quoting(opts);
no_quoting.inhibit_quoting = true;
- const Tool* tool = target->toolchain()->GetTool(tool_type);
+ const Tool* tool = target->toolchain()->GetTool(tool_name);
std::ostringstream command_out;
for (const auto& range : tool->command().ranges()) {
// TODO: this is emitting a bonus space prior to each substitution.
- switch (range.type) {
- case SUBSTITUTION_LITERAL:
- EscapeStringToStream(command_out, range.literal, no_quoting);
- break;
- case SUBSTITUTION_OUTPUT:
- path_output.WriteFiles(command_out, tool_outputs);
- break;
- case SUBSTITUTION_DEFINES:
- command_out << flags.defines;
- break;
- case SUBSTITUTION_INCLUDE_DIRS:
- command_out << flags.includes;
- break;
- case SUBSTITUTION_CFLAGS:
- command_out << flags.cflags;
- break;
- case SUBSTITUTION_CFLAGS_C:
- if (source_type == SOURCE_C)
- command_out << flags.cflags_c;
- break;
- case SUBSTITUTION_CFLAGS_CC:
- if (source_type == SOURCE_CPP)
- command_out << flags.cflags_cc;
- break;
- case SUBSTITUTION_CFLAGS_OBJC:
- if (source_type == SOURCE_M)
- command_out << flags.cflags_objc;
- break;
- case SUBSTITUTION_CFLAGS_OBJCC:
- if (source_type == SOURCE_MM)
- command_out << flags.cflags_objcc;
- break;
- case SUBSTITUTION_LABEL:
- case SUBSTITUTION_LABEL_NAME:
- case SUBSTITUTION_ROOT_GEN_DIR:
- case SUBSTITUTION_ROOT_OUT_DIR:
- case SUBSTITUTION_TARGET_GEN_DIR:
- case SUBSTITUTION_TARGET_OUT_DIR:
- case SUBSTITUTION_TARGET_OUTPUT_NAME:
- case SUBSTITUTION_SOURCE:
- case SUBSTITUTION_SOURCE_NAME_PART:
- case SUBSTITUTION_SOURCE_FILE_PART:
- case SUBSTITUTION_SOURCE_DIR:
- case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
- case SUBSTITUTION_SOURCE_GEN_DIR:
- case SUBSTITUTION_SOURCE_OUT_DIR:
- case SUBSTITUTION_SOURCE_TARGET_RELATIVE:
- EscapeStringToStream(command_out,
- SubstitutionWriter::GetCompilerSubstitution(
- target, source, range.type),
- opts);
- break;
-
+ if (range.type == &SubstitutionLiteral) {
+ EscapeStringToStream(command_out, range.literal, no_quoting);
+ } else if (range.type == &SubstitutionOutput) {
+ path_output.WriteFiles(command_out, tool_outputs);
+ } else if (range.type == &CSubstitutionDefines) {
+ command_out << flags.defines;
+ } else if (range.type == &CSubstitutionIncludeDirs) {
+ command_out << flags.includes;
+ } else if (range.type == &CSubstitutionCFlags) {
+ command_out << flags.cflags;
+ } else if (range.type == &CSubstitutionCFlagsC) {
+ if (source_type == SourceFile::SOURCE_C)
+ command_out << flags.cflags_c;
+ } else if (range.type == &CSubstitutionCFlagsCc) {
+ if (source_type == SourceFile::SOURCE_CPP)
+ command_out << flags.cflags_cc;
+ } else if (range.type == &CSubstitutionCFlagsObjC) {
+ if (source_type == SourceFile::SOURCE_M)
+ command_out << flags.cflags_objc;
+ } else if (range.type == &CSubstitutionCFlagsObjCc) {
+ if (source_type == SourceFile::SOURCE_MM)
+ command_out << flags.cflags_objcc;
+ } else if (range.type == &SubstitutionLabel ||
+ range.type == &SubstitutionLabelName ||
+ range.type == &SubstitutionRootGenDir ||
+ range.type == &SubstitutionRootOutDir ||
+ range.type == &SubstitutionTargetGenDir ||
+ range.type == &SubstitutionTargetOutDir ||
+ range.type == &SubstitutionTargetOutputName ||
+ range.type == &SubstitutionSource ||
+ range.type == &SubstitutionSourceNamePart ||
+ range.type == &SubstitutionSourceFilePart ||
+ range.type == &SubstitutionSourceDir ||
+ range.type == &SubstitutionSourceRootRelativeDir ||
+ range.type == &SubstitutionSourceGenDir ||
+ range.type == &SubstitutionSourceOutDir ||
+ range.type == &SubstitutionSourceTargetRelative) {
+ EscapeStringToStream(command_out,
+ SubstitutionWriter::GetCompilerSubstitution(
+ target, source, range.type),
+ opts);
+ } else {
// Other flags shouldn't be relevant to compiling C/C++/ObjC/ObjC++
// source files.
- default:
- NOTREACHED() << "Unsupported substitution for this type of target : "
- << kSubstitutionNames[range.type];
- continue;
+ NOTREACHED() << "Unsupported substitution for this type of target : "
+ << range.type->name;
+ continue;
}
}
compile_commands->append(kPrettyPrintLineEnding);
@@ -232,13 +222,15 @@ void CompileCommandsWriter::RenderJSON(const BuildSettings* build_settings,
for (const auto& source : target->sources()) {
// If this source is not a C/C++/ObjC/ObjC++ source (not header) file,
// continue as it does not belong in the compilation database.
- SourceFileType source_type = GetSourceFileType(source);
- if (source_type != SOURCE_CPP && source_type != SOURCE_C &&
- source_type != SOURCE_M && source_type != SOURCE_MM)
+ SourceFile::Type source_type = source.type();
+ if (source_type != SourceFile::SOURCE_CPP &&
+ source_type != SourceFile::SOURCE_C &&
+ source_type != SourceFile::SOURCE_M &&
+ source_type != SourceFile::SOURCE_MM)
continue;
- Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
- if (!target->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
+ const char* tool_name = Tool::kToolNone;
+ if (!target->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
continue;
if (!first) {
@@ -253,7 +245,7 @@ void CompileCommandsWriter::RenderJSON(const BuildSettings* build_settings,
WriteDirectory(base::StringPrintf("%" PRIsFP, build_dir.value().c_str()),
compile_commands);
WriteCommand(target, source, flags, tool_outputs, path_output,
- source_type, tool_type, opts, compile_commands);
+ source_type, tool_name, opts, compile_commands);
compile_commands->append("\"");
compile_commands->append(kPrettyPrintLineEnding);
compile_commands->append(" }");
@@ -299,7 +291,6 @@ bool CompileCommandsWriter::RunAndWriteFiles(
if (!WriteFileIfChanged(output_path, json, err))
return false;
return true;
-
}
std::vector<const Target*> CompileCommandsWriter::FilterTargets(
@@ -333,4 +324,4 @@ void CompileCommandsWriter::VisitDeps(const Target* target,
VisitDeps(pair.ptr, visited);
}
}
-} \ No newline at end of file
+}
diff --git a/gn/tools/gn/compile_commands_writer_unittest.cc b/gn/tools/gn/compile_commands_writer_unittest.cc
index 90429a47155..a3e3fb5f53b 100644
--- a/gn/tools/gn/compile_commands_writer_unittest.cc
+++ b/gn/tools/gn/compile_commands_writer_unittest.cc
@@ -264,26 +264,28 @@ TEST_F(CompileCommandsTest, WinPrecompiledHeaders) {
pch_settings.set_default_toolchain_label(toolchain()->label());
// Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
TestWithScope::SetCommandForTool(
"c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
"-o {{output}}",
- cxx_tool.get());
+ cxx_tool);
cxx_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(Tool::PCH_MSVC);
- pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cxx));
// Add a C compiler as well.
- std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
TestWithScope::SetCommandForTool(
"cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
"-o {{output}}",
- cc_tool.get());
+ cc_tool);
cc_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
- pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cc));
pch_toolchain.ToolchainSetupComplete();
// This target doesn't specify precompiled headers.
@@ -412,27 +414,29 @@ TEST_F(CompileCommandsTest, GCCPrecompiledHeaders) {
pch_settings.set_default_toolchain_label(toolchain()->label());
// Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cxx = Tool::CreateTool(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
TestWithScope::SetCommandForTool(
"c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
"-o {{output}}",
- cxx_tool.get());
+ cxx_tool);
cxx_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(Tool::PCH_GCC);
- pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cxx));
pch_toolchain.ToolchainSetupComplete();
// Add a C compiler as well.
- std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cc = Tool::CreateTool(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
TestWithScope::SetCommandForTool(
"cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
"-o {{output}}",
- cc_tool.get());
+ cc_tool);
cc_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
- pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cc));
pch_toolchain.ToolchainSetupComplete();
// This target doesn't specify precompiled headers.
diff --git a/gn/tools/gn/config.cc b/gn/tools/gn/config.cc
index e021fe40b9d..e28fd30ed68 100644
--- a/gn/tools/gn/config.cc
+++ b/gn/tools/gn/config.cc
@@ -11,7 +11,7 @@
Config::Config(const Settings* settings,
const Label& label,
const std::set<SourceFile>& build_dependency_files)
- : Item(settings, label, build_dependency_files), resolved_(false) {}
+ : Item(settings, label, build_dependency_files) {}
Config::~Config() = default;
diff --git a/gn/tools/gn/config.h b/gn/tools/gn/config.h
index ad049737330..040d54efd7d 100644
--- a/gn/tools/gn/config.h
+++ b/gn/tools/gn/config.h
@@ -60,7 +60,7 @@ class Config : public Item {
// Contains the own_values combined with sub-configs. Most configs don't have
// sub-configs. So as an optimization, this is not populated if there are no
// items in configs_. The resolved_values() getter handles this.
- bool resolved_;
+ bool resolved_ = false;
ConfigValues composite_values_;
UniqueVector<LabelConfigPair> configs_;
diff --git a/gn/tools/gn/config_values.cc b/gn/tools/gn/config_values.cc
index 5cdfa7a8175..85de7e4b6e4 100644
--- a/gn/tools/gn/config_values.cc
+++ b/gn/tools/gn/config_values.cc
@@ -34,6 +34,8 @@ void ConfigValues::AppendValues(const ConfigValues& append) {
VectorAppend(&ldflags_, append.ldflags_);
VectorAppend(&lib_dirs_, append.lib_dirs_);
VectorAppend(&libs_, append.libs_);
+ VectorAppend(&rustflags_, append.rustflags_);
+ VectorAppend(&rustenv_, append.rustenv_);
// Only append precompiled header if there isn't one. It might be nice to
// throw an error if there are conflicting precompiled headers, but that
diff --git a/gn/tools/gn/config_values.h b/gn/tools/gn/config_values.h
index bcab53c5245..86eea6cf3bc 100644
--- a/gn/tools/gn/config_values.h
+++ b/gn/tools/gn/config_values.h
@@ -45,6 +45,8 @@ class ConfigValues {
DIR_VALUES_ACCESSOR(include_dirs)
STRING_VALUES_ACCESSOR(ldflags)
DIR_VALUES_ACCESSOR(lib_dirs)
+ STRING_VALUES_ACCESSOR(rustflags)
+ STRING_VALUES_ACCESSOR(rustenv)
// =================================================================
// IMPORTANT: If you add a new one, be sure to update AppendValues()
// and command_desc.cc.
@@ -81,6 +83,8 @@ class ConfigValues {
std::vector<std::string> ldflags_;
std::vector<SourceDir> lib_dirs_;
std::vector<LibFile> libs_;
+ std::vector<std::string> rustflags_;
+ std::vector<std::string> rustenv_;
// If you add a new one, be sure to update AppendValues().
std::string precompiled_header_;
diff --git a/gn/tools/gn/config_values_extractors.h b/gn/tools/gn/config_values_extractors.h
index 5bb42296b1e..cbcbed5d728 100644
--- a/gn/tools/gn/config_values_extractors.h
+++ b/gn/tools/gn/config_values_extractors.h
@@ -30,8 +30,7 @@ struct EscapeOptions;
// DoSomething(iter.cur());
class ConfigValuesIterator {
public:
- explicit ConfigValuesIterator(const Target* target)
- : target_(target), cur_index_(-1) {}
+ explicit ConfigValuesIterator(const Target* target) : target_(target) {}
bool done() const {
return cur_index_ >= static_cast<int>(target_->configs().size());
@@ -66,7 +65,7 @@ class ConfigValuesIterator {
// Represents an index into the target_'s configs() or, when -1, the config
// values on the target itself.
- int cur_index_;
+ int cur_index_ = -1;
};
template <typename T, class Writer>
diff --git a/gn/tools/gn/config_values_generator.cc b/gn/tools/gn/config_values_generator.cc
index ff4e4dfe252..5947274bbaa 100644
--- a/gn/tools/gn/config_values_generator.cc
+++ b/gn/tools/gn/config_values_generator.cc
@@ -73,6 +73,8 @@ void ConfigValuesGenerator::Run() {
FILL_DIR_CONFIG_VALUE(include_dirs)
FILL_STRING_CONFIG_VALUE(ldflags)
FILL_DIR_CONFIG_VALUE(lib_dirs)
+ FILL_STRING_CONFIG_VALUE(rustflags)
+ FILL_STRING_CONFIG_VALUE(rustenv)
#undef FILL_STRING_CONFIG_VALUE
#undef FILL_DIR_CONFIG_VALUE
diff --git a/gn/tools/gn/config_values_generator.h b/gn/tools/gn/config_values_generator.h
index 165f41a9532..e6639912db0 100644
--- a/gn/tools/gn/config_values_generator.h
+++ b/gn/tools/gn/config_values_generator.h
@@ -40,6 +40,7 @@ class ConfigValuesGenerator {
#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\n"
+ " libs, precompiled_header, precompiled_source, rustflags,\n" \
+ " rustenv\n"
#endif // TOOLS_GN_CONFIG_VALUES_GENERATOR_H_
diff --git a/gn/tools/gn/create_bundle_target_generator.cc b/gn/tools/gn/create_bundle_target_generator.cc
index 3556b5d86c8..a54d17104aa 100644
--- a/gn/tools/gn/create_bundle_target_generator.cc
+++ b/gn/tools/gn/create_bundle_target_generator.cc
@@ -98,7 +98,7 @@ bool CreateBundleTargetGenerator::FillBundleDir(
str + "\".");
return false;
}
- bundle_dir->SwapValue(&str);
+ *bundle_dir = SourceDir(std::move(str));
return true;
}
@@ -128,7 +128,8 @@ bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
std::make_pair(iter.first.as_string(), iter.second.string_value()));
}
- target_->bundle_data().xcode_extra_attributes().swap(xcode_extra_attributes);
+ target_->bundle_data().xcode_extra_attributes() =
+ std::move(xcode_extra_attributes);
return true;
}
@@ -217,7 +218,7 @@ bool CreateBundleTargetGenerator::FillCodeSigningSources() {
err_))
return false;
- target_->bundle_data().code_signing_sources().swap(script_sources);
+ target_->bundle_data().code_signing_sources() = std::move(script_sources);
return true;
}
diff --git a/gn/tools/gn/desc_builder.cc b/gn/tools/gn/desc_builder.cc
index 57f27fdbfa4..6ede896d61d 100644
--- a/gn/tools/gn/desc_builder.cc
+++ b/gn/tools/gn/desc_builder.cc
@@ -55,6 +55,7 @@
// "walk_keys" : [ list of target walk keys ]
// "rebase" : true or false
// "output_conversion" : "string for output conversion"
+// "response_file_contents": [ list of response file contents entries ]
// }
//
// Optionally, if "what" is specified while generating description, two other
@@ -416,6 +417,16 @@ class TargetDescBuilder : public BaseDescBuilder {
res->SetWithoutPathExpansion(variables::kArgs, std::move(args));
}
+ if (what(variables::kResponseFileContents) &&
+ !target_->action_values().rsp_file_contents().list().empty()) {
+ auto rsp_file_contents = std::make_unique<base::ListValue>();
+ for (const auto& elem :
+ target_->action_values().rsp_file_contents().list())
+ rsp_file_contents->AppendString(elem.AsString());
+
+ res->SetWithoutPathExpansion(variables::kResponseFileContents,
+ std::move(rsp_file_contents));
+ }
if (what(variables::kDepfile) &&
!target_->action_values().depfile().empty()) {
res->SetKey(variables::kDepfile,
@@ -634,8 +645,8 @@ class TargetDescBuilder : public BaseDescBuilder {
auto dict = std::make_unique<base::DictionaryValue>();
for (const auto& source : target_->sources()) {
std::vector<OutputFile> outputs;
- Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
- if (target_->GetOutputFilesForSource(source, &tool_type, &outputs)) {
+ const char* tool_name = Tool::kToolNone;
+ if (target_->GetOutputFilesForSource(source, &tool_name, &outputs)) {
auto list = std::make_unique<base::ListValue>();
for (const auto& output : outputs)
list->AppendString(output.value());
diff --git a/gn/tools/gn/escape.cc b/gn/tools/gn/escape.cc
index aa6b967eb98..d5e33c0c7d4 100644
--- a/gn/tools/gn/escape.cc
+++ b/gn/tools/gn/escape.cc
@@ -75,6 +75,24 @@ size_t EscapeStringToString_Ninja(const base::StringPiece& str,
return i;
}
+size_t EscapeStringToString_Depfile(const base::StringPiece& str,
+ const EscapeOptions& options,
+ char* dest,
+ bool* needed_quoting) {
+ size_t i = 0;
+ for (const auto& elem : str) {
+ // Escape all characters that ninja depfile parser can recognize as escaped,
+ // even if some of them can work without escaping.
+ if (elem == ' ' || elem == '\\' || elem == '#' || elem == '*' ||
+ elem == '[' || elem == '|' || elem == ']')
+ dest[i++] = '\\';
+ else if (elem == '$') // Extra rule for $$
+ dest[i++] = '$';
+ dest[i++] = elem;
+ }
+ return i;
+}
+
size_t EscapeStringToString_NinjaPreformatted(const base::StringPiece& str,
char* dest) {
// Only Ninja-escape $.
@@ -188,6 +206,8 @@ size_t EscapeStringToString(const base::StringPiece& str,
return str.size();
case ESCAPE_NINJA:
return EscapeStringToString_Ninja(str, options, dest, needed_quoting);
+ case ESCAPE_DEPFILE:
+ return EscapeStringToString_Depfile(str, options, dest, needed_quoting);
case ESCAPE_NINJA_COMMAND:
switch (options.platform) {
case ESCAPE_PLATFORM_CURRENT:
diff --git a/gn/tools/gn/escape.h b/gn/tools/gn/escape.h
index d59a8eb2620..af5f8b8febb 100644
--- a/gn/tools/gn/escape.h
+++ b/gn/tools/gn/escape.h
@@ -16,6 +16,9 @@ enum EscapingMode {
// Ninja string escaping.
ESCAPE_NINJA,
+ // Ninja/makefile depfile string escaping.
+ ESCAPE_DEPFILE,
+
// For writing commands to ninja files. This assumes the output is "one
// thing" like a filename, so will escape or quote spaces as necessary for
// both Ninja and the shell to keep that thing together.
@@ -23,8 +26,8 @@ enum EscapingMode {
// For writing preformatted shell commands to Ninja files. This assumes the
// output already has the proper quoting and may include special shell
- // shell characters which we want to pass to the shell (like when writing
- // tool commands). Only Ninja "$" are escaped.
+ // characters which we want to pass to the shell (like when writing tool
+ // commands). Only Ninja "$" are escaped.
ESCAPE_NINJA_PREFORMATTED_COMMAND,
};
@@ -38,16 +41,11 @@ enum EscapingPlatform {
};
struct EscapeOptions {
- EscapeOptions()
- : mode(ESCAPE_NONE),
- platform(ESCAPE_PLATFORM_CURRENT),
- inhibit_quoting(false) {}
-
- EscapingMode mode;
+ EscapingMode mode = ESCAPE_NONE;
// Controls how "fork" escaping is done. You will generally want to keep the
// default "current" platform.
- EscapingPlatform platform;
+ EscapingPlatform platform = ESCAPE_PLATFORM_CURRENT;
// When the escaping mode is ESCAPE_SHELL, the escaper will normally put
// quotes around things with spaces. If this value is set to true, we'll
@@ -57,7 +55,7 @@ struct EscapeOptions {
// false. Note that Windows has strange behavior where the meaning of the
// backslashes changes according to if it is followed by a quote. The
// escaping rules assume that a double-quote will be appended to the result.
- bool inhibit_quoting;
+ bool inhibit_quoting = false;
};
// Escapes the given input, returnining the result.
diff --git a/gn/tools/gn/escape_unittest.cc b/gn/tools/gn/escape_unittest.cc
index 0aa8adc45d3..d7ff23fda47 100644
--- a/gn/tools/gn/escape_unittest.cc
+++ b/gn/tools/gn/escape_unittest.cc
@@ -12,6 +12,13 @@ TEST(Escape, Ninja) {
EXPECT_EQ("asdf$:$ \"$$\\bar", result);
}
+TEST(Escape, Depfile) {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_DEPFILE;
+ std::string result = EscapeString("asdf:$ \\#*[|]bar", opts, nullptr);
+ EXPECT_EQ("asdf:$$\\ \\\\\\#\\*\\[\\|\\]bar", result);
+}
+
TEST(Escape, WindowsCommand) {
EscapeOptions opts;
opts.mode = ESCAPE_NINJA_COMMAND;
diff --git a/gn/tools/gn/exec_process_unittest.cc b/gn/tools/gn/exec_process_unittest.cc
index 647bf42029e..993f2d73b70 100644
--- a/gn/tools/gn/exec_process_unittest.cc
+++ b/gn/tools/gn/exec_process_unittest.cc
@@ -70,7 +70,7 @@ TEST(ExecProcessTest, TestLargeOutput) {
std::string std_out, std_err;
int exit_code;
- ASSERT_TRUE(ExecPython("import sys; print 'o' * 1000000", &std_out, &std_err,
+ ASSERT_TRUE(ExecPython("import sys; print('o' * 1000000)", &std_out, &std_err,
&exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(1000001u, std_out.size());
@@ -80,18 +80,20 @@ TEST(ExecProcessTest, TestStdoutAndStderrOutput) {
std::string std_out, std_err;
int exit_code;
- ASSERT_TRUE(ExecPython(
- "import sys; print 'o' * 10000; print >>sys.stderr, 'e' * 10000",
- &std_out, &std_err, &exit_code));
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; print('o' "
+ "* 10000); print('e' * 10000, file=sys.stderr)",
+ &std_out, &std_err, &exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(10001u, std_out.size());
EXPECT_EQ(10001u, std_err.size());
std_out.clear();
std_err.clear();
- ASSERT_TRUE(ExecPython(
- "import sys; print >>sys.stderr, 'e' * 10000; print 'o' * 10000",
- &std_out, &std_err, &exit_code));
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; print('e' "
+ "* 10000, file=sys.stderr); print('o' * 10000)",
+ &std_out, &std_err, &exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(10001u, std_out.size());
EXPECT_EQ(10001u, std_err.size());
@@ -101,7 +103,7 @@ TEST(ExecProcessTest, TestOneOutputClosed) {
std::string std_out, std_err;
int exit_code;
- ASSERT_TRUE(ExecPython("import sys; sys.stderr.close(); print 'o' * 10000",
+ ASSERT_TRUE(ExecPython("import sys; sys.stderr.close(); print('o' * 10000)",
&std_out, &std_err, &exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(10001u, std_out.size());
@@ -109,9 +111,10 @@ TEST(ExecProcessTest, TestOneOutputClosed) {
std_out.clear();
std_err.clear();
- ASSERT_TRUE(ExecPython(
- "import sys; sys.stdout.close(); print >>sys.stderr, 'e' * 10000",
- &std_out, &std_err, &exit_code));
+ ASSERT_TRUE(
+ ExecPython("from __future__ import print_function; import sys; "
+ "sys.stdout.close(); print('e' * 10000, file=sys.stderr)",
+ &std_out, &std_err, &exit_code));
EXPECT_EQ(0, exit_code);
EXPECT_EQ(0u, std_out.size());
EXPECT_EQ(10001u, std_err.size());
diff --git a/gn/tools/gn/filesystem_utils.cc b/gn/tools/gn/filesystem_utils.cc
index 2fc4b24af7d..c378233b925 100644
--- a/gn/tools/gn/filesystem_utils.cc
+++ b/gn/tools/gn/filesystem_utils.cc
@@ -198,6 +198,18 @@ void AppendFixedAbsolutePathSuffix(const BuildSettings* build_settings,
}
}
+size_t AbsPathLenWithNoTrailingSlash(const base::StringPiece& path) {
+ size_t len = path.size();
+#if defined(OS_WIN)
+ size_t min_len = 3;
+#else
+ // On posix system. The minimal abs path is "/".
+ size_t min_len = 1;
+#endif
+ for (; len > min_len && IsSlash(path[len - 1]); len--)
+ ;
+ return len;
+}
} // namespace
std::string FilePathToUTF8(const base::FilePath::StringType& str) {
@@ -353,9 +365,13 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
dest->clear();
- if (source_root.size() > path.size())
- return false; // The source root is longer: the path can never be inside.
+ // There is no specification of how many slashes may be at the end of
+ // source_root or path. Trim them off for easier string manipulation.
+ size_t path_len = AbsPathLenWithNoTrailingSlash(path);
+ size_t source_root_len = AbsPathLenWithNoTrailingSlash(source_root);
+ if (source_root_len > path_len)
+ return false; // The source root is longer: the path can never be inside.
#if defined(OS_WIN)
// Source root should be canonical on Windows. Note that the initial slash
// must be forward slash, but that the other ones can be either forward or
@@ -366,19 +382,29 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
size_t after_common_index = std::string::npos;
if (DoesBeginWindowsDriveLetter(path)) {
// Handle "C:\foo"
- if (AreAbsoluteWindowsPathsEqual(source_root,
- path.substr(0, source_root.size())))
- after_common_index = source_root.size();
- else
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(0, source_root_len))) {
+ after_common_index = source_root_len;
+ if (path_len == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
return false;
- } else if (path[0] == '/' && source_root.size() <= path.size() - 1 &&
+ }
+ } else if (path[0] == '/' && source_root_len <= path_len - 1 &&
DoesBeginWindowsDriveLetter(path.substr(1))) {
// Handle "/C:/foo"
- if (AreAbsoluteWindowsPathsEqual(source_root,
- path.substr(1, source_root.size())))
- after_common_index = source_root.size() + 1;
- else
+ if (AreAbsoluteWindowsPathsEqual(source_root.substr(0, source_root_len),
+ path.substr(1, source_root_len))) {
+ after_common_index = source_root_len + 1;
+ if (path_len + 1 == source_root_len) {
+ *dest = "//";
+ return true;
+ }
+ } else {
return false;
+ }
} else {
return false;
}
@@ -386,12 +412,15 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
// If we get here, there's a match and after_common_index identifies the
// part after it.
- // The base may or may not have a trailing slash, so skip all slashes from
- // the path after our prefix match.
- size_t first_after_slash = after_common_index;
- while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
+ if (!IsSlash(path[after_common_index])) {
+ // path is ${source-root}SUFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = after_common_index + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
first_after_slash++;
-
dest->assign("//"); // Result is source root relative.
dest->append(&path.data()[first_after_slash],
path.size() - first_after_slash);
@@ -401,11 +430,21 @@ bool MakeAbsolutePathRelativeIfPossible(const base::StringPiece& source_root,
// On non-Windows this is easy. Since we know both are absolute, just do a
// prefix check.
- if (path.substr(0, source_root.size()) == source_root) {
- // The base may or may not have a trailing slash, so skip all slashes from
- // the path after our prefix match.
- size_t first_after_slash = source_root.size();
- while (first_after_slash < path.size() && IsSlash(path[first_after_slash]))
+
+ if (path.substr(0, source_root_len) ==
+ source_root.substr(0, source_root_len)) {
+ if (path_len == source_root_len) {
+ // path is equivalent to source_root.
+ *dest = "//";
+ return true;
+ } else if (!IsSlash(path[source_root_len])) {
+ // path is ${source-root}SUFIX/...
+ return false;
+ }
+ // A source-root relative path, The input may have an unknown number of
+ // slashes after the previous match. Skip over them.
+ size_t first_after_slash = source_root_len + 1;
+ while (first_after_slash < path_len && IsSlash(path[first_after_slash]))
first_after_slash++;
dest->assign("//"); // Result is source root relative.
@@ -869,7 +908,7 @@ SourceDir SourceDirForPath(const base::FilePath& source_root,
result_str.append(FilePathToUTF8(path_comp[i]));
result_str.push_back('/');
}
- return SourceDir(result_str);
+ return SourceDir(std::move(result_str));
}
SourceDir SourceDirForCurrentDirectory(const base::FilePath& source_root) {
diff --git a/gn/tools/gn/filesystem_utils_unittest.cc b/gn/tools/gn/filesystem_utils_unittest.cc
index dcaa09914fa..433ea367381 100644
--- a/gn/tools/gn/filesystem_utils_unittest.cc
+++ b/gn/tools/gn/filesystem_utils_unittest.cc
@@ -848,3 +848,21 @@ TEST(FilesystemUtils, GetDirForEmptyBuildDir) {
EXPECT_EQ("obj/",
GetBuildDirAsOutputFile(context, BuildDirType::OBJ).value());
}
+
+TEST(FilesystemUtils, ResolveRelativeTest) {
+ std::string result;
+#ifndef OS_WIN
+ EXPECT_TRUE(
+ MakeAbsolutePathRelativeIfPossible("/some/dir", "/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "/some/dir", "/some/dir-sufix/a", &result));
+#else
+ EXPECT_TRUE(MakeAbsolutePathRelativeIfPossible("C:/some/dir",
+ "/C:/some/dir/a", &result));
+ EXPECT_EQ(result, "//a");
+ EXPECT_FALSE(MakeAbsolutePathRelativeIfPossible(
+ "C:/some/dir", "C:/some/dir-sufix/a", &result));
+#endif
+}
diff --git a/gn/tools/gn/function_exec_script.cc b/gn/tools/gn/function_exec_script.cc
index 89570db9c2b..3ef7646a859 100644
--- a/gn/tools/gn/function_exec_script.cc
+++ b/gn/tools/gn/function_exec_script.cc
@@ -95,7 +95,7 @@ Arguments:
unspecified or the empty list which means no arguments.
input_conversion:
- Controls how the file is read and parsed. See "gn help input_conversion".
+ Controls how the file is read and parsed. See "gn help io_conversion".
If unspecified, defaults to the empty string which causes the script
result to be discarded. exec script will return None.
diff --git a/gn/tools/gn/function_get_target_outputs.cc b/gn/tools/gn/function_get_target_outputs.cc
index f5d4ef6cbd3..83e7356fb3d 100644
--- a/gn/tools/gn/function_get_target_outputs.cc
+++ b/gn/tools/gn/function_get_target_outputs.cc
@@ -26,18 +26,18 @@ const char kGetTargetOutputs_Help[] =
defined execution order, and it obviously can't reference targets that are
defined after the function call).
- Only copy and action targets are supported. The outputs from binary targets
- will depend on the toolchain definition which won't necessarily have been
- loaded by the time a given line of code has run, and source sets and groups
- have no useful output file.
+ Only copy, generated_file, and action targets are supported. The outputs from
+ binary targets will depend on the toolchain definition which won't
+ necessarily have been loaded by the time a given line of code has run, and
+ source sets and groups have no useful output file.
Return value
The names in the resulting list will be absolute file paths (normally like
"//out/Debug/bar.exe", depending on the build directory).
- action targets: this will just return the files specified in the "outputs"
- variable of the target.
+ action, copy, and generated_file targets: this will just return the files
+ specified in the "outputs" variable of the target.
action_foreach targets: this will return the result of applying the output
template to the sources (see "gn help source_expansion"). This will be the
@@ -45,12 +45,6 @@ Return value
process_file_template will return for those inputs (see "gn help
process_file_template").
- binary targets (executables, libraries): this will return a list of the
- resulting binary file(s). The "main output" (the actual binary or library)
- will always be the 0th element in the result. Depending on the platform and
- output type, there may be other output files as well (like import libraries)
- which will follow.
-
source sets and groups: this will return a list containing the path of the
"stamp" file that Ninja will produce once all outputs are generated. This
probably isn't very useful.
@@ -121,12 +115,15 @@ Value RunGetTargetOutputs(Scope* scope,
std::vector<SourceFile> files;
if (target->output_type() == Target::ACTION ||
target->output_type() == Target::COPY_FILES ||
- target->output_type() == Target::ACTION_FOREACH) {
+ target->output_type() == Target::ACTION_FOREACH ||
+ target->output_type() == Target::GENERATED_FILE) {
target->action_values().GetOutputsAsSourceFiles(target, &files);
} else {
// Other types of targets are not supported.
- *err = Err(args[0], "Target is not an action, action_foreach, or copy.",
- "Only these target types are supported by get_target_outputs.");
+ *err =
+ Err(args[0],
+ "Target is not an action, action_foreach, generated_file, or copy.",
+ "Only these target types are supported by get_target_outputs.");
return Value();
}
diff --git a/gn/tools/gn/function_process_file_template.cc b/gn/tools/gn/function_process_file_template.cc
index 47de6407f1a..7035503380d 100644
--- a/gn/tools/gn/function_process_file_template.cc
+++ b/gn/tools/gn/function_process_file_template.cc
@@ -95,7 +95,7 @@ Value RunProcessFileTemplate(Scope* scope,
}
auto& types = subst.required_types();
- if (base::ContainsValue(types, SUBSTITUTION_SOURCE_TARGET_RELATIVE)) {
+ if (base::ContainsValue(types, &SubstitutionSourceTargetRelative)) {
*err = Err(template_arg, "Not a valid substitution type for the function.");
return Value();
}
diff --git a/gn/tools/gn/function_read_file.cc b/gn/tools/gn/function_read_file.cc
index 1cc29f5b7bc..8f34483492d 100644
--- a/gn/tools/gn/function_read_file.cc
+++ b/gn/tools/gn/function_read_file.cc
@@ -32,7 +32,7 @@ Arguments
Filename to read, relative to the build file.
input_conversion
- Controls how the file is read and parsed. See "gn help input_conversion".
+ Controls how the file is read and parsed. See "gn help io_conversion".
Example
diff --git a/gn/tools/gn/function_rebase_path_unittest.cc b/gn/tools/gn/function_rebase_path_unittest.cc
index 718ecb1867d..fdd09dc9eec 100644
--- a/gn/tools/gn/function_rebase_path_unittest.cc
+++ b/gn/tools/gn/function_rebase_path_unittest.cc
@@ -121,6 +121,9 @@ TEST(RebasePath, StringsSystemPaths) {
RebaseOne(scope, "foo/", "C:/ssd/out/Debug", "//"));
#else
setup.build_settings()->SetBuildDir(SourceDir("/ssd/out/Debug"));
+ setup.build_settings()->SetRootPath(base::FilePath("/ssd/out/Debug"));
+ EXPECT_EQ("../Debug-suffix/a", RebaseOne(scope, "/ssd/out/Debug-suffix/a",
+ "/ssd/out/Debug", "/ssd/out/Debug"));
setup.build_settings()->SetRootPath(base::FilePath("/hdd/src"));
// Test system absolute to-dir.
diff --git a/gn/tools/gn/function_toolchain.cc b/gn/tools/gn/function_toolchain.cc
index cf45c677e24..cb686121a6f 100644
--- a/gn/tools/gn/function_toolchain.cc
+++ b/gn/tools/gn/function_toolchain.cc
@@ -7,8 +7,10 @@
#include <memory>
#include <utility>
+#include "tools/gn/c_tool.h"
#include "tools/gn/err.h"
#include "tools/gn/functions.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/label.h"
#include "tools/gn/label_ptr.h"
#include "tools/gn/parse_tree.h"
@@ -28,269 +30,6 @@ namespace {
// the toolchain property on a scope.
const int kToolchainPropertyKey = 0;
-bool ReadBool(Scope* scope,
- const char* var,
- Tool* tool,
- void (Tool::*set)(bool),
- Err* err) {
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
- if (!v->VerifyTypeIs(Value::BOOLEAN, err))
- return false;
-
- (tool->*set)(v->boolean_value());
- return true;
-}
-
-// Reads the given string from the scope (if present) and puts the result into
-// dest. If the value is not a string, sets the error and returns false.
-bool ReadString(Scope* scope,
- const char* var,
- Tool* tool,
- void (Tool::*set)(std::string),
- Err* err) {
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
- if (!v->VerifyTypeIs(Value::STRING, err))
- return false;
-
- (tool->*set)(v->string_value());
- return true;
-}
-
-// Reads the given label from the scope (if present) and puts the result into
-// dest. If the value is not a label, sets the error and returns false.
-bool ReadLabel(Scope* scope,
- const char* var,
- Tool* tool,
- const Label& current_toolchain,
- void (Tool::*set)(LabelPtrPair<Pool>),
- Err* err) {
- const Value* v = scope->GetValue(var, true);
- if (!v)
- return true; // Not present is fine.
-
- Label label =
- Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
- if (err->has_error())
- return false;
-
- LabelPtrPair<Pool> pair(label);
- pair.origin = tool->defined_from();
-
- (tool->*set)(std::move(pair));
- return true;
-}
-
-// Calls the given validate function on each type in the list. On failure,
-// sets the error, blame the value, and return false.
-bool ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
- bool (*validate)(SubstitutionType),
- const Value* origin,
- Err* err) {
- for (const auto& cur_type : list) {
- if (!validate(cur_type)) {
- *err = Err(*origin, "Pattern not valid here.",
- "You used the pattern " +
- std::string(kSubstitutionNames[cur_type]) +
- " which is not valid\nfor this variable.");
- return false;
- }
- }
- return true;
-}
-
-bool ReadPattern(Scope* scope,
- const char* name,
- bool (*validate)(SubstitutionType),
- Tool* tool,
- void (Tool::*set)(SubstitutionPattern),
- Err* err) {
- const Value* value = scope->GetValue(name, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- SubstitutionPattern pattern;
- if (!pattern.Parse(*value, err))
- return false;
- if (!ValidateSubstitutionList(pattern.required_types(), validate, value, err))
- return false;
-
- (tool->*set)(std::move(pattern));
- return true;
-}
-
-bool ReadPatternList(Scope* scope,
- const char* name,
- bool (*validate)(SubstitutionType),
- Tool* tool,
- void (Tool::*set)(SubstitutionList),
- Err* err) {
- const Value* value = scope->GetValue(name, true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::LIST, err))
- return false;
-
- SubstitutionList list;
- if (!list.Parse(*value, err))
- return false;
-
- // Validate the right kinds of patterns are used.
- if (!ValidateSubstitutionList(list.required_types(), validate, value, err))
- return false;
-
- (tool->*set)(std::move(list));
- return true;
-}
-
-bool ReadOutputExtension(Scope* scope, Tool* tool, Err* err) {
- const Value* value = scope->GetValue("default_output_extension", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value().empty())
- return true; // Accept empty string.
-
- if (value->string_value()[0] != '.') {
- *err = Err(*value, "default_output_extension must begin with a '.'");
- return false;
- }
-
- tool->set_default_output_extension(value->string_value());
- return true;
-}
-
-bool ReadPrecompiledHeaderType(Scope* scope, Tool* tool, Err* err) {
- const Value* value = scope->GetValue("precompiled_header_type", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value().empty())
- return true; // Accept empty string, do nothing (default is "no PCH").
-
- if (value->string_value() == "gcc") {
- tool->set_precompiled_header_type(Tool::PCH_GCC);
- return true;
- } else if (value->string_value() == "msvc") {
- tool->set_precompiled_header_type(Tool::PCH_MSVC);
- return true;
- }
- *err = Err(*value, "Invalid precompiled_header_type",
- "Must either be empty, \"gcc\", or \"msvc\".");
- return false;
-}
-
-bool ReadDepsFormat(Scope* scope, Tool* tool, Err* err) {
- const Value* value = scope->GetValue("depsformat", true);
- if (!value)
- return true; // Not present is fine.
- if (!value->VerifyTypeIs(Value::STRING, err))
- return false;
-
- if (value->string_value() == "gcc") {
- tool->set_depsformat(Tool::DEPS_GCC);
- } else if (value->string_value() == "msvc") {
- tool->set_depsformat(Tool::DEPS_MSVC);
- } else {
- *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
- return false;
- }
- return true;
-}
-
-bool IsCompilerTool(Toolchain::ToolType type) {
- return type == Toolchain::TYPE_CC || type == Toolchain::TYPE_CXX ||
- type == Toolchain::TYPE_OBJC || type == Toolchain::TYPE_OBJCXX ||
- type == Toolchain::TYPE_RC || type == Toolchain::TYPE_ASM;
-}
-
-bool IsLinkerTool(Toolchain::ToolType type) {
- // "alink" is not counted as in the generic "linker" tool list.
- return type == Toolchain::TYPE_SOLINK ||
- type == Toolchain::TYPE_SOLINK_MODULE || type == Toolchain::TYPE_LINK;
-}
-
-bool IsPatternInOutputList(const SubstitutionList& output_list,
- const SubstitutionPattern& pattern) {
- for (const auto& cur : output_list.list()) {
- if (pattern.ranges().size() == cur.ranges().size() &&
- std::equal(pattern.ranges().begin(), pattern.ranges().end(),
- cur.ranges().begin()))
- return true;
- }
- return false;
-}
-
-bool ValidateOutputs(const Tool* tool, Err* err) {
- if (tool->outputs().list().empty()) {
- *err = Err(tool->defined_from(),
- "\"outputs\" must be specified for this tool.");
- return false;
- }
- return true;
-}
-
-// Validates either link_output or depend_output. To generalize to either, pass
-// the associated pattern, and the variable name that should appear in error
-// messages.
-bool ValidateLinkAndDependOutput(const Tool* tool,
- Toolchain::ToolType tool_type,
- const SubstitutionPattern& pattern,
- const char* variable_name,
- Err* err) {
- if (pattern.empty())
- return true; // Empty is always OK.
-
- // It should only be specified for certain tool types.
- if (tool_type != Toolchain::TYPE_SOLINK &&
- tool_type != Toolchain::TYPE_SOLINK_MODULE) {
- *err = Err(tool->defined_from(),
- "This tool specifies a " + std::string(variable_name) + ".",
- "This is only valid for solink and solink_module tools.");
- return false;
- }
-
- if (!IsPatternInOutputList(tool->outputs(), pattern)) {
- *err = Err(tool->defined_from(), "This tool's link_output is bad.",
- "It must match one of the outputs.");
- return false;
- }
-
- return true;
-}
-
-bool ValidateRuntimeOutputs(const Tool* tool,
- Toolchain::ToolType tool_type,
- Err* err) {
- if (tool->runtime_outputs().list().empty())
- return true; // Empty is always OK.
-
- if (!IsLinkerTool(tool_type)) {
- *err = Err(tool->defined_from(), "This tool specifies runtime_outputs.",
- "This is only valid for linker tools (alink doesn't count).");
- return false;
- }
-
- for (const SubstitutionPattern& pattern : tool->runtime_outputs().list()) {
- if (!IsPatternInOutputList(tool->outputs(), pattern)) {
- *err = Err(tool->defined_from(), "This tool's runtime_outputs is bad.",
- "It must be a subset of the outputs. The bad one is:\n " +
- pattern.AsString());
- return false;
- }
- }
- return true;
-}
-
} // namespace
// toolchain -------------------------------------------------------------------
@@ -560,6 +299,9 @@ Tool types
"copy_bundle_data": [iOS, macOS] Tool to copy files in a bundle.
"compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
+ Rust tools:
+ "rustc": Rust compiler and linker
+
Tool variables
command [string with substitutions]
@@ -567,6 +309,15 @@ Tool variables
The command to run.
+ command_launcher [string]
+ Valid for: all tools except "action" (optional)
+
+ The prefix with which to launch the command (e.g. the path to a Goma or
+ CCache compiler launcher).
+
+ Note that this prefix will not be included in the compilation database or
+ IDE files generated from the build.
+
default_output_dir [string with substitutions]
Valid for: linker tools
@@ -620,6 +371,20 @@ Tool variables
Example: description = "Compiling {{source}}"
+ exe_output_extension [string, optional, rust tools only]
+ rlib_output_extension [string, optional, rust tools only]
+ dylib_output_extension [string, optional, rust tools only]
+ cdylib_output_extension [string, optional, rust tools only]
+ proc_macro_output_extension [string, optional, rust tools only]
+ Valid for: Rust tools
+
+ These specify the default tool output for each of the crate types.
+ The default is empty for executables, shared, and static libraries and
+ ".rlib" for rlibs. Note that the Rust compiler complains with an error
+ if external crates do not take the form `lib<name>.rlib` or
+ `lib<name>.<shared_extension>`, where `<shared_extension>` is `.so`,
+ `.dylib`, or `.dll` as appropriate for the platform.
+
lib_switch [string, optional, link tools only]
lib_dir_switch [string, optional, link tools only]
Valid for: Linker tools except "alink"
@@ -761,7 +526,8 @@ Tool variables
added to runtime deps (see "gn help runtime_deps"). By default (if
runtime_outputs is empty or unspecified), it will be the link_output.
-Expansions for tool variables
+)" // String break to prevent overflowing the 16K max VC string length.
+ R"(Expansions for tool variables
All paths are relative to the root build directory, which is the current
directory for running all tools. These expansions are available to all tools:
@@ -797,8 +563,7 @@ Expansions for tool variables
Example: "libfoo" for the target named "foo" and an output prefix for
the linker tool of "lib".
-)" // String break to prevent overflowing the 16K max VC string length.
- R"( Compiler tools have the notion of a single input and a single output, along
+ Compiler tools have the notion of a single input and a single output, along
with a set of compiler-specific flags. The following expansions are
available:
@@ -934,6 +699,39 @@ Expansions for tool variables
assets catalog compiler. Usually based on the target_name of
the create_bundle target.
+ Rust tools have the notion of a single input and a single output, along
+ with a set of compiler-specific flags. The following expansions are
+ available:
+
+ {{crate_name}}
+ Expands to the string representing the crate name of target under
+ compilation.
+
+ {{crate_type}}
+ Expands to the string representing the type of crate for the target
+ under compilation.
+
+ {{externs}}
+ Expands to the list of --extern flags needed to include addition Rust
+ libraries in this target. Includes any specified renamed dependencies.
+
+ {{rustc_output_extension}}
+ Expands to the output extension for this target's crate type.
+
+ {{rustc_output_prefix}}
+ Expands to the prefix for shared and static libraries. This should
+ generally be "lib". Empty for executable targets.
+
+ {{rustdeps}}
+ Expands to the list of -Ldependency=<path> strings needed to compile
+ this target.
+
+ {{rustenv}}
+ Expands to the list of environment variables.
+
+ {{rustflags}}
+ Expands to the list of strings representing Rust compiler flags.
+
Separate linking and dependencies for shared libraries
Shared libraries are special in that not all changes to them require that
@@ -1006,11 +804,6 @@ Value RunTool(Scope* scope,
if (!EnsureSingleStringArg(function, args, err))
return Value();
const std::string& tool_name = args[0].string_value();
- Toolchain::ToolType tool_type = Toolchain::ToolNameToType(tool_name);
- if (tool_type == Toolchain::TYPE_NONE) {
- *err = Err(args[0], "Unknown tool type");
- return Value();
- }
// Run the tool block.
Scope block_scope(scope);
@@ -1018,103 +811,20 @@ Value RunTool(Scope* scope,
if (err->has_error())
return Value();
- // Figure out which validator to use for the substitution pattern for this
- // tool type. There are different validators for the "outputs" than for the
- // rest of the strings.
- bool (*subst_validator)(SubstitutionType) = nullptr;
- bool (*subst_output_validator)(SubstitutionType) = nullptr;
- if (IsCompilerTool(tool_type)) {
- subst_validator = &IsValidCompilerSubstitution;
- subst_output_validator = &IsValidCompilerOutputsSubstitution;
- } else if (IsLinkerTool(tool_type)) {
- subst_validator = &IsValidLinkerSubstitution;
- subst_output_validator = &IsValidLinkerOutputsSubstitution;
- } else if (tool_type == Toolchain::TYPE_ALINK) {
- subst_validator = &IsValidALinkSubstitution;
- // ALink uses the standard output file patterns as other linker tools.
- subst_output_validator = &IsValidLinkerOutputsSubstitution;
- } else if (tool_type == Toolchain::TYPE_COPY ||
- tool_type == Toolchain::TYPE_COPY_BUNDLE_DATA) {
- subst_validator = &IsValidCopySubstitution;
- subst_output_validator = &IsValidCopySubstitution;
- } else if (tool_type == Toolchain::TYPE_COMPILE_XCASSETS) {
- subst_validator = &IsValidCompileXCassetsSubstitution;
- subst_output_validator = &IsValidCompileXCassetsSubstitution;
- } else {
- subst_validator = &IsValidToolSubstitution;
- subst_output_validator = &IsValidToolSubstitution;
- }
+ std::unique_ptr<Tool> tool =
+ Tool::CreateTool(function, tool_name, &block_scope, toolchain, err);
- std::unique_ptr<Tool> tool = std::make_unique<Tool>();
- tool->set_defined_from(function);
-
- if (!ReadPattern(&block_scope, "command", subst_validator, tool.get(),
- &Tool::set_command, err) ||
- !ReadOutputExtension(&block_scope, tool.get(), err) ||
- !ReadPattern(&block_scope, "depfile", subst_validator, tool.get(),
- &Tool::set_depfile, err) ||
- !ReadDepsFormat(&block_scope, tool.get(), err) ||
- !ReadPattern(&block_scope, "description", subst_validator, tool.get(),
- &Tool::set_description, err) ||
- !ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
- err) ||
- !ReadString(&block_scope, "lib_dir_switch", tool.get(),
- &Tool::set_lib_dir_switch, err) ||
- !ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
- &Tool::set_link_output, err) ||
- !ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
- &Tool::set_depend_output, err) ||
- !ReadPatternList(&block_scope, "runtime_outputs", subst_validator,
- tool.get(), &Tool::set_runtime_outputs, err) ||
- !ReadString(&block_scope, "output_prefix", tool.get(),
- &Tool::set_output_prefix, err) ||
- !ReadPattern(&block_scope, "default_output_dir", subst_validator,
- tool.get(), &Tool::set_default_output_dir, err) ||
- !ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
- !ReadBool(&block_scope, "restat", tool.get(), &Tool::set_restat, err) ||
- !ReadPattern(&block_scope, "rspfile", subst_validator, tool.get(),
- &Tool::set_rspfile, err) ||
- !ReadPattern(&block_scope, "rspfile_content", subst_validator, tool.get(),
- &Tool::set_rspfile_content, err) ||
- !ReadLabel(&block_scope, "pool", tool.get(), toolchain->label(),
- &Tool::set_pool, err)) {
+ if (!tool) {
return Value();
}
- if (tool_type != Toolchain::TYPE_COPY && tool_type != Toolchain::TYPE_STAMP &&
- tool_type != Toolchain::TYPE_COPY_BUNDLE_DATA &&
- tool_type != Toolchain::TYPE_COMPILE_XCASSETS &&
- tool_type != Toolchain::TYPE_ACTION) {
- // All tools should have outputs, except the copy, stamp, copy_bundle_data
- // compile_xcassets and action tools that generate their outputs internally.
- if (!ReadPatternList(&block_scope, "outputs", subst_output_validator,
- tool.get(), &Tool::set_outputs, err) ||
- !ValidateOutputs(tool.get(), err))
- return Value();
- }
- if (!ValidateRuntimeOutputs(tool.get(), tool_type, err))
- return Value();
-
- // Validate link_output and depend_output.
- if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->link_output(),
- "link_output", err))
- return Value();
- if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->depend_output(),
- "depend_output", err))
- return Value();
- if ((!tool->link_output().empty() && tool->depend_output().empty()) ||
- (tool->link_output().empty() && !tool->depend_output().empty())) {
- *err = Err(function,
- "Both link_output and depend_output should either "
- "be specified or they should both be empty.");
- return Value();
- }
+ tool->set_defined_from(function);
+ toolchain->SetTool(std::move(tool));
// Make sure there weren't any vars set in this tool that were unused.
if (!block_scope.CheckForUnusedVars(err))
return Value();
- toolchain->SetTool(tool_type, std::move(tool));
return Value();
}
diff --git a/gn/tools/gn/function_toolchain_unittest.cc b/gn/tools/gn/function_toolchain_unittest.cc
index 599d7a1b67d..baf2e26ce13 100644
--- a/gn/tools/gn/function_toolchain_unittest.cc
+++ b/gn/tools/gn/function_toolchain_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "tools/gn/functions.h"
+#include "tools/gn/rust_tool.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/test_with_scheduler.h"
#include "tools/gn/test_with_scope.h"
@@ -34,7 +35,7 @@ TEST_F(FunctionToolchain, RuntimeOutputs) {
ASSERT_TRUE(toolchain);
// The toolchain should have a link tool with the two outputs.
- const Tool* link = toolchain->GetTool(Toolchain::TYPE_LINK);
+ const Tool* link = toolchain->GetTool(CTool::kCToolLink);
ASSERT_TRUE(link);
ASSERT_EQ(1u, link->outputs().list().size());
EXPECT_EQ("foo", link->outputs().list()[0].AsString());
@@ -58,3 +59,61 @@ TEST_F(FunctionToolchain, RuntimeOutputs) {
ASSERT_TRUE(err.has_error()) << err.message();
}
}
+
+TEST_F(FunctionToolchain, Rust) {
+ TestWithScope setup;
+
+ // These runtime outputs are a subset of the outputs so are OK.
+ {
+ TestParseInput input(
+ R"(toolchain("rust") {
+ tool("rustc") {
+ command = "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin {{rustflags}} -o {{output}} {{externs}} {{source}}"
+ description = "RUST {{output}}"
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // It should have generated a toolchain.
+ ASSERT_EQ(1u, setup.items().size());
+ const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+ ASSERT_TRUE(toolchain);
+
+ const Tool* rust = toolchain->GetTool(RustTool::kRsToolRustc);
+ ASSERT_TRUE(rust);
+ ASSERT_EQ(rust->command().AsString(),
+ "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin "
+ "{{rustflags}} -o {{output}} {{externs}} {{source}}");
+ ASSERT_EQ(rust->description().AsString(), "RUST {{output}}");
+ }
+}
+
+TEST_F(FunctionToolchain, CommandLauncher) {
+ TestWithScope setup;
+
+ TestParseInput input(
+ R"(toolchain("good") {
+ tool("cxx") {
+ command_launcher = "/usr/goma/gomacc"
+ }
+ })");
+ ASSERT_FALSE(input.has_error());
+
+ Err err;
+ input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ // It should have generated a toolchain.
+ ASSERT_EQ(1u, setup.items().size());
+ const Toolchain* toolchain = setup.items()[0]->AsToolchain();
+ ASSERT_TRUE(toolchain);
+
+ // The toolchain should have a link tool with the two outputs.
+ const Tool* link = toolchain->GetTool(CTool::kCToolCxx);
+ ASSERT_TRUE(link);
+ EXPECT_EQ("/usr/goma/gomacc", link->command_launcher());
+} \ No newline at end of file
diff --git a/gn/tools/gn/function_write_file.cc b/gn/tools/gn/function_write_file.cc
index 44e6b163d80..d56724d440f 100644
--- a/gn/tools/gn/function_write_file.cc
+++ b/gn/tools/gn/function_write_file.cc
@@ -47,7 +47,7 @@ Arguments
The list or string to write.
output_conversion
- Controls how the output is written. See "gn help output_conversion".
+ Controls how the output is written. See "gn help io_conversion".
)";
Value RunWriteFile(Scope* scope,
diff --git a/gn/tools/gn/functions.cc b/gn/tools/gn/functions.cc
index 7c54d17539b..05033c82008 100644
--- a/gn/tools/gn/functions.cc
+++ b/gn/tools/gn/functions.cc
@@ -328,7 +328,7 @@ Variables on a target used to apply configs
Example
config("myconfig") {
- includes = [ "include/common" ]
+ include_dirs = [ "include/common" ]
defines = [ "ENABLE_DOOM_MELON" ]
}
@@ -911,7 +911,7 @@ Example
toolchain("toolchain") {
tool("link") {
command = "..."
- pool = ":link_pool($default_toolchain)")
+ pool = ":link_pool($default_toolchain)"
}
}
)*";
@@ -1269,6 +1269,7 @@ struct FunctionInfoInitializer {
INSERT_FUNCTION(StaticLibrary, true)
INSERT_FUNCTION(Target, true)
INSERT_FUNCTION(GeneratedFile, true)
+ INSERT_FUNCTION(RustLibrary, true)
INSERT_FUNCTION(Assert, false)
INSERT_FUNCTION(Config, false)
diff --git a/gn/tools/gn/functions.h b/gn/tools/gn/functions.h
index 97f5b0d825a..9dc2cdc52fd 100644
--- a/gn/tools/gn/functions.h
+++ b/gn/tools/gn/functions.h
@@ -278,6 +278,15 @@ Value RunRebasePath(Scope* scope,
const std::vector<Value>& args,
Err* err);
+extern const char kRustLibrary[];
+extern const char kRustLibrary_HelpShort[];
+extern const char kRustLibrary_Help[];
+Value RunRustLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err);
+
extern const char kSetDefaults[];
extern const char kSetDefaults_HelpShort[];
extern const char kSetDefaults_Help[];
diff --git a/gn/tools/gn/functions_target.cc b/gn/tools/gn/functions_target.cc
index be02fc420cb..c20c1e681e6 100644
--- a/gn/tools/gn/functions_target.cc
+++ b/gn/tools/gn/functions_target.cc
@@ -20,6 +20,11 @@
" General: check_includes, configs, data, friend, inputs, metadata,\n" \
" output_name, output_extension, public, sources, testonly,\n" \
" visibility\n"
+#define RUST_VARS \
+ " Rust variables: aliased_deps, crate_root, crate_name, edition\n"
+#define RUST_SHARED_VARS \
+ " Rust variables: aliased_deps, crate_root, crate_name, crate_type, " \
+ "edition\n"
namespace functions {
@@ -87,6 +92,15 @@ Value ExecuteGenericTarget(const char* target_type,
" action are started. This can give additional parallelism in the build\n" \
" for runtime-only dependencies.\n"
+// Common help paragraph on targets that can use different languages.
+#define LANGUAGE_HELP \
+ "\n" \
+ " The tools and commands used to create this target type will be\n" \
+ " determined by the source files in its sources. Targets containing\n" \
+ " multiple compiler-incompatible languages are not allowed (e.g. a\n" \
+ " target containing both C and C++ sources is acceptable, but a\n" \
+ " target containing C and Rust sources is not).\n"
+
const char kAction[] = "action";
const char kAction_HelpShort[] =
"action: Declare a target that runs a script a single time.";
@@ -221,7 +235,7 @@ Example
script = "idl_processor.py"
sources = [ "foo.idl", "bar.idl" ]
- # Our script reads this file each time, so we need to list is as a
+ # Our script reads this file each time, so we need to list it as a
# dependency so we can rebuild if it changes.
inputs = [ "my_configuration.txt" ]
@@ -532,9 +546,14 @@ const char kExecutable_HelpShort[] =
const char kExecutable_Help[] =
R"(executable: Declare an executable target.
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
Variables
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS;
Value RunExecutable(Scope* scope,
const FunctionCallNode* function,
@@ -596,9 +615,14 @@ const char kLoadableModule_Help[] =
to dynamically load the library at runtime), then you should use a
"shared_library" target type instead.
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
Variables
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_SHARED_VARS;
Value RunLoadableModule(Scope* scope,
const FunctionCallNode* function,
@@ -609,6 +633,35 @@ Value RunLoadableModule(Scope* scope,
block, err);
}
+// rust_library ----------------------------------------------------------------
+
+const char kRustLibrary[] = "rust_library";
+const char kRustLibrary_HelpShort[] =
+ "rust_library: Declare a Rust library target.";
+const char kRustLibrary_Help[] =
+ R"(rust_library: Declare a Rust library target.
+
+ A Rust library is an archive containing additional rust-c provided metadata.
+ These are the files produced by the rustc compiler with the `.rlib`
+ extension, and are the intermediate step for most Rust-based binaries.
+
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
+Variables
+
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS;
+Value RunRustLibrary(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ return ExecuteGenericTarget(functions::kRustLibrary, scope, function, args,
+ block, err);
+}
+
// shared_library --------------------------------------------------------------
const char kSharedLibrary[] = "shared_library";
@@ -623,9 +676,14 @@ const char kSharedLibrary_Help[] =
via "data_deps" or, on Darwin platforms, use a "loadable_module" target type
instead.
+Language and compilation
+)" LANGUAGE_HELP
+ R"(
+
Variables
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_SHARED_VARS;
Value RunSharedLibrary(Scope* scope,
const FunctionCallNode* function,
@@ -643,6 +701,11 @@ const char kSourceSet_HelpShort[] = "source_set: Declare a source set target.";
const char kSourceSet_Help[] =
R"(source_set: Declare a source set target.
+ The language of a source_set target is determined by the extensions present
+ in its sources.
+
+C-language source_sets
+
A source set is a collection of sources that get compiled, but are not linked
to produce any kind of library. Instead, the resulting object files are
implicitly added to the linker line of all targets that depend on the source
@@ -664,6 +727,13 @@ const char kSourceSet_Help[] =
not from the intermediate targets." There is no way to express this concept
when linking multiple static libraries into a shared library.
+Rust-language source_sets
+
+ A Rust source set is a collection of sources that get passed along to the
+ final target that depends on it. No compilation is performed, and the source
+ files are simply added as dependencies on the eventual rustc invocation that
+ would produce a binary.
+
Variables
)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
@@ -694,7 +764,8 @@ const char kStaticLibrary_Help[] =
Variables
complete_static_lib
-)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS;
+)" CONFIG_VALUES_VARS_HELP DEPS_VARS DEPENDENT_CONFIG_VARS GENERAL_TARGET_VARS
+ RUST_VARS LANGUAGE_HELP;
Value RunStaticLibrary(Scope* scope,
const FunctionCallNode* function,
diff --git a/gn/tools/gn/functions_target_rust_unittest.cc b/gn/tools/gn/functions_target_rust_unittest.cc
new file mode 100644
index 00000000000..37bd8b1f3ea
--- /dev/null
+++ b/gn/tools/gn/functions_target_rust_unittest.cc
@@ -0,0 +1,373 @@
+// Copyright 2019 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 "tools/gn/config.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/test_with_scheduler.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/test/test.h"
+
+using RustFunctionsTarget = TestWithScheduler;
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateName) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " crate_name = \"foo_crate\"\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+ "foo_crate");
+
+ TestParseInput lib_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"lib.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ err = Err();
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_name(),
+ "foo")
+ << item_collector.back()->AsTarget()->rust_values().crate_name();
+}
+
+// Checks that the appropriate crate root is found.
+TEST_F(RustFunctionsTarget, CrateRootFind) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput normal_input(
+ "executable(\"foo\") {\n"
+ " crate_root = \"foo.rs\""
+ " sources = [ \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(normal_input.has_error());
+ Err err;
+ normal_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/foo.rs");
+
+ TestParseInput normal_shlib_input(
+ "shared_library(\"foo\") {\n"
+ " crate_root = \"foo.rs\""
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(normal_shlib_input.has_error());
+ err = Err();
+ normal_shlib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/foo.rs");
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ err = Err();
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/main.rs");
+
+ TestParseInput lib_input(
+ "rust_library(\"libfoo\") {\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ err = Err();
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/lib.rs");
+
+ TestParseInput singlesource_input(
+ "executable(\"bar\") {\n"
+ " sources = [ \"bar.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(singlesource_input.has_error());
+ err = Err();
+ singlesource_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/bar.rs");
+
+ TestParseInput error_input(
+ "rust_library(\"foo\") {\n"
+ " sources = [ \"foo.rs\", \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(error_input.has_error());
+ err = Err();
+ error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Missing \"crate_root\" and missing \"lib.rs\" in sources.",
+ err.message());
+
+ TestParseInput nosources_input(
+ "executable(\"bar\") {\n"
+ " crate_root = \"bar.rs\"\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(nosources_input.has_error());
+ err = Err();
+ nosources_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(
+ item_collector.back()->AsTarget()->rust_values().crate_root().value(),
+ "/bar.rs");
+}
+
+// Checks that the appropriate crate type is used.
+TEST_F(RustFunctionsTarget, CrateTypeSelection) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput lib_input(
+ "shared_library(\"libfoo\") {\n"
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ Err err;
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
+ RustValues::CRATE_DYLIB);
+
+ TestParseInput exe_non_default_input(
+ "executable(\"foo\") {\n"
+ " crate_type = \"rlib\"\n"
+ " sources = [ \"main.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(exe_non_default_input.has_error());
+ err = Err();
+ exe_non_default_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().crate_type(),
+ RustValues::CRATE_RLIB);
+
+ TestParseInput lib_error_input(
+ "shared_library(\"foo\") {\n"
+ " crate_type = \"bad\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_error_input.has_error());
+ err = Err();
+ lib_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Inadmissible crate type \"bad\".", err.message()) << err.message();
+
+ TestParseInput lib_missing_error_input(
+ "shared_library(\"foo\") {\n"
+ " sources = [ \"lib.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_missing_error_input.has_error());
+ err = Err();
+ lib_missing_error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Must set \"crate_type\" on a Rust \"shared_library\".",
+ err.message());
+}
+
+// Checks that the appropriate config values are propagated.
+TEST_F(RustFunctionsTarget, ConfigValues) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"foo\") {\n"
+ " rustflags = [ \"-Cdebuginfo=2\" ]\n"
+ " rustenv = [ \"RUST_BACKTRACE=1\" ]"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags().size(),
+ 1U);
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustflags()[0],
+ "-Cdebuginfo=2");
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv().size(),
+ 1U);
+ EXPECT_EQ(item_collector.back()->AsConfig()->own_values().rustenv()[0],
+ "RUST_BACKTRACE=1");
+}
+
+// Checks that set_defaults works properly.
+TEST_F(RustFunctionsTarget, SetDefaults) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"foo\") {\n"
+ " rustflags = [ \"-Cdebuginfo=2\" ]\n"
+ " rustenv = [ \"RUST_BACKTRACE=1\" ]"
+ "}\n"
+ "set_defaults(\"rust_library\") {\n"
+ " configs = [ \":foo\" ]\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message() << err.message();
+
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->type(),
+ Value::LIST);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()
+ .size(),
+ 1U);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()[0]
+ .type(),
+ Value::STRING);
+ EXPECT_EQ(setup.scope()
+ ->GetTargetDefaults("rust_library")
+ ->GetValue("configs")
+ ->list_value()[0]
+ .string_value(),
+ ":foo");
+}
+
+// Checks that the dition gets propagated correctly.
+TEST_F(RustFunctionsTarget, Edition) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput lib_input(
+ "shared_library(\"libfoo\") {\n"
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ " edition = \"2018\""
+ "}\n");
+ ASSERT_FALSE(lib_input.has_error());
+ Err err;
+ lib_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+ ASSERT_EQ(item_collector.back()->AsTarget()->rust_values().edition(), "2018");
+
+ TestParseInput error_input(
+ "shared_library(\"foo\") {\n"
+ " crate_type = \"dylib\"\n"
+ " sources = [ \"lib.rs\" ]\n"
+ "}\n");
+ ASSERT_FALSE(error_input.has_error());
+ err = Err();
+ error_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Missing \"edition\" in Rust target.", err.message())
+ << err.message();
+}
+
+// Checks aliased_deps parsing.
+TEST_F(RustFunctionsTarget, AliasedDeps) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "executable(\"foo\") {\n"
+ " sources = [ \"main.rs\" ]\n"
+ " deps = [ \"//bar\", \"//baz\" ]\n"
+ " edition = \"2018\""
+ " aliased_deps = {\n"
+ " bar_renamed = \"//bar\"\n"
+ " baz_renamed = \"//baz:baz\"\n"
+ " }\n"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+
+ EXPECT_EQ(
+ item_collector.back()->AsTarget()->rust_values().aliased_deps().size(),
+ 2U);
+}
+
+TEST_F(RustFunctionsTarget, PublicConfigs) {
+ TestWithScope setup;
+
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+ setup.scope()->set_source_dir(SourceDir("/"));
+
+ TestParseInput exe_input(
+ "config(\"bar\") {\n"
+ " defines = [ \"DOOM_MELON\" ]"
+ "}\n"
+ "executable(\"foo\") {\n"
+ " crate_name = \"foo_crate\"\n"
+ " sources = [ \"foo.rs\", \"lib.rs\", \"main.rs\" ]\n"
+ " edition = \"2018\""
+ " public_configs = [ \":bar\" ]"
+ "}\n");
+ ASSERT_FALSE(exe_input.has_error());
+ Err err;
+ exe_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error()) << err.message();
+}
diff --git a/gn/tools/gn/functions_target_unittest.cc b/gn/tools/gn/functions_target_unittest.cc
index 642b8ce456f..40c6fde852e 100644
--- a/gn/tools/gn/functions_target_unittest.cc
+++ b/gn/tools/gn/functions_target_unittest.cc
@@ -174,3 +174,23 @@ TEST_F(FunctionsTarget, TemplateDefaults) {
good_input.parsed()->Execute(setup.scope(), &err);
ASSERT_FALSE(err.has_error()) << err.message();
}
+
+// Checks that we find unused identifiers in targets.
+TEST_F(FunctionsTarget, MixedSourceError) {
+ TestWithScope setup;
+
+ // The target generator needs a place to put the targets or it will fail.
+ Scope::ItemVector item_collector;
+ setup.scope()->set_item_collector(&item_collector);
+
+ // Test a good one first.
+ TestParseInput good_input(
+ "source_set(\"foo\") {\n"
+ " sources = [ \"cpp.cc\", \"rust.rs\" ]"
+ "}\n");
+ ASSERT_FALSE(good_input.has_error());
+ Err err;
+ good_input.parsed()->Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ ASSERT_EQ(err.message(), "More than one language used in target sources.");
+} \ No newline at end of file
diff --git a/gn/tools/gn/general_tool.cc b/gn/tools/gn/general_tool.cc
new file mode 100644
index 00000000000..a0f285ddd91
--- /dev/null
+++ b/gn/tools/gn/general_tool.cc
@@ -0,0 +1,51 @@
+// Copyright 2019 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 "tools/gn/general_tool.h"
+#include "tools/gn/target.h"
+
+const char* GeneralTool::kGeneralToolStamp = "stamp";
+const char* GeneralTool::kGeneralToolCopy = "copy";
+const char* GeneralTool::kGeneralToolCopyBundleData = "copy_bundle_data";
+const char* GeneralTool::kGeneralToolCompileXCAssets = "compile_xcassets";
+const char* GeneralTool::kGeneralToolAction = "action";
+
+GeneralTool::GeneralTool(const char* n) : Tool(n) {
+ CHECK(ValidateName(n));
+}
+
+GeneralTool::~GeneralTool() = default;
+
+GeneralTool* GeneralTool::AsGeneral() {
+ return this;
+}
+const GeneralTool* GeneralTool::AsGeneral() const {
+ return this;
+}
+
+bool GeneralTool::ValidateName(const char* name) const {
+ return name == kGeneralToolStamp || name == kGeneralToolCopy ||
+ name == kGeneralToolCopyBundleData ||
+ name == kGeneralToolCompileXCAssets || name == kGeneralToolAction;
+}
+
+void GeneralTool::SetComplete() {
+ SetToolComplete();
+}
+
+bool GeneralTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ return Tool::InitTool(scope, toolchain, err);
+}
+
+bool GeneralTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kGeneralToolStamp || name_ == kGeneralToolAction)
+ return IsValidToolSubstitution(sub_type);
+ else if (name_ == kGeneralToolCopy || name_ == kGeneralToolCopyBundleData)
+ return IsValidCopySubstitution(sub_type);
+ else if (name_ == kGeneralToolCompileXCAssets)
+ return IsValidCompileXCassetsSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
diff --git a/gn/tools/gn/general_tool.h b/gn/tools/gn/general_tool.h
new file mode 100644
index 00000000000..827a48dca06
--- /dev/null
+++ b/gn/tools/gn/general_tool.h
@@ -0,0 +1,46 @@
+// Copyright 2019 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_GENERAL_TOOL_H_
+#define TOOLS_GN_GENERAL_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/tool.h"
+
+class GeneralTool : public Tool {
+ public:
+ // General tools
+ static const char* kGeneralToolStamp;
+ static const char* kGeneralToolCopy;
+ static const char* kGeneralToolAction;
+
+ // Platform-specific tools
+ static const char* kGeneralToolCopyBundleData;
+ static const char* kGeneralToolCompileXCAssets;
+
+ GeneralTool(const char* n);
+ ~GeneralTool();
+
+ // 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;
+
+ GeneralTool* AsGeneral() override;
+ const GeneralTool* AsGeneral() const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GeneralTool);
+};
+
+#endif // TOOLS_GN_GENERAL_TOOL_H_
diff --git a/gn/tools/gn/generated_file_target_generator.cc b/gn/tools/gn/generated_file_target_generator.cc
index 1854d5bfb3b..29712216cf5 100644
--- a/gn/tools/gn/generated_file_target_generator.cc
+++ b/gn/tools/gn/generated_file_target_generator.cc
@@ -17,10 +17,7 @@ GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
const FunctionCallNode* function_call,
Target::OutputType type,
Err* err)
- : TargetGenerator(target, scope, function_call, err),
- output_type_(type),
- contents_defined_(false),
- data_keys_defined_(false) {}
+ : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
diff --git a/gn/tools/gn/generated_file_target_generator.h b/gn/tools/gn/generated_file_target_generator.h
index fb2523d4b07..206555f9f8d 100644
--- a/gn/tools/gn/generated_file_target_generator.h
+++ b/gn/tools/gn/generated_file_target_generator.h
@@ -38,8 +38,8 @@ class GeneratedFileTargetGenerator : public TargetGenerator {
bool IsMetadataCollectionTarget(const base::StringPiece& variable,
const ParseNode* origin);
- bool contents_defined_;
- bool data_keys_defined_;
+ bool contents_defined_ = false;
+ bool data_keys_defined_ = false;
Target::OutputType output_type_;
diff --git a/gn/tools/gn/header_checker.cc b/gn/tools/gn/header_checker.cc
index 8407bd0f043..dca7302d679 100644
--- a/gn/tools/gn/header_checker.cc
+++ b/gn/tools/gn/header_checker.cc
@@ -18,7 +18,6 @@
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/scheduler.h"
-#include "tools/gn/source_file_type.h"
#include "tools/gn/target.h"
#include "tools/gn/trace.h"
#include "util/worker_pool.h"
@@ -152,9 +151,10 @@ void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
for (const auto& file : files) {
// Only check C-like source files (RC files also have includes).
- SourceFileType type = GetSourceFileType(file.first);
- if (type != SOURCE_CPP && type != SOURCE_H && type != SOURCE_C &&
- type != SOURCE_M && type != SOURCE_MM && type != SOURCE_RC)
+ SourceFile::Type type = file.first.type();
+ if (type != SourceFile::SOURCE_CPP && type != SourceFile::SOURCE_H &&
+ type != SourceFile::SOURCE_C && type != SourceFile::SOURCE_M &&
+ type != SourceFile::SOURCE_MM && type != SourceFile::SOURCE_RC)
continue;
if (!check_generated_) {
diff --git a/gn/tools/gn/import_manager.cc b/gn/tools/gn/import_manager.cc
index 36c70881a80..9339ff56584 100644
--- a/gn/tools/gn/import_manager.cc
+++ b/gn/tools/gn/import_manager.cc
@@ -21,6 +21,7 @@ std::unique_ptr<Scope> UncachedImport(const Settings* settings,
const ParseNode* node_for_err,
Err* err) {
ScopedTrace load_trace(TraceItem::TRACE_IMPORT_LOAD, file.value());
+ load_trace.SetToolchain(settings->toolchain_label());
const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
node_for_err->GetRange(), settings->build_settings(), file, err);
@@ -129,6 +130,8 @@ bool ImportManager::DoImport(const SourceFile& file,
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);
}
}
diff --git a/gn/tools/gn/input_file.cc b/gn/tools/gn/input_file.cc
index 438ab388d34..dc5ca37fe40 100644
--- a/gn/tools/gn/input_file.cc
+++ b/gn/tools/gn/input_file.cc
@@ -7,7 +7,7 @@
#include "base/files/file_util.h"
InputFile::InputFile(const SourceFile& name)
- : name_(name), dir_(name_.GetDir()), contents_loaded_(false) {}
+ : name_(name), dir_(name_.GetDir()) {}
InputFile::~InputFile() = default;
diff --git a/gn/tools/gn/input_file.h b/gn/tools/gn/input_file.h
index 49e5f855741..7a64c658065 100644
--- a/gn/tools/gn/input_file.h
+++ b/gn/tools/gn/input_file.h
@@ -56,7 +56,7 @@ class InputFile {
base::FilePath physical_name_;
std::string friendly_name_;
- bool contents_loaded_;
+ bool contents_loaded_ = false;
std::string contents_;
DISALLOW_COPY_AND_ASSIGN(InputFile);
diff --git a/gn/tools/gn/input_file_manager.cc b/gn/tools/gn/input_file_manager.cc
index 2290dc70447..aad8cc82294 100644
--- a/gn/tools/gn/input_file_manager.cc
+++ b/gn/tools/gn/input_file_manager.cc
@@ -298,7 +298,7 @@ bool InputFileManager::LoadFile(const LocationRange& origin,
InputFileData* data = input_files_[name].get();
data->loaded = true;
if (success) {
- data->tokens.swap(tokens);
+ data->tokens = std::move(tokens);
data->parsed_root = std::move(root);
} else {
data->parse_error = *err;
@@ -318,7 +318,7 @@ bool InputFileManager::LoadFile(const LocationRange& origin,
if (data->completion_event)
data->completion_event->Signal();
- callbacks.swap(data->scheduled_callbacks);
+ callbacks = std::move(data->scheduled_callbacks);
}
// Run pending invocations. Theoretically we could schedule each of these
diff --git a/gn/tools/gn/input_file_manager.h b/gn/tools/gn/input_file_manager.h
index 20874d1d9d2..c4f19e97ac2 100644
--- a/gn/tools/gn/input_file_manager.h
+++ b/gn/tools/gn/input_file_manager.h
@@ -38,7 +38,7 @@ class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
public:
// Callback issued when a file is laoded. On auccess, the parse node will
// refer to the root block of the file. On failure, this will be NULL.
- typedef base::Callback<void(const ParseNode*)> FileLoadCallback;
+ using FileLoadCallback = base::Callback<void(const ParseNode*)>;
InputFileManager();
@@ -137,8 +137,8 @@ class InputFileManager : public base::RefCountedThreadSafe<InputFileManager> {
mutable std::mutex lock_;
// Maps repo-relative filenames to the corresponding owned pointer.
- typedef std::unordered_map<SourceFile, std::unique_ptr<InputFileData>>
- InputFileMap;
+ using InputFileMap =
+ std::unordered_map<SourceFile, std::unique_ptr<InputFileData>>;
InputFileMap input_files_;
// Tracks all dynamic inputs. The data are holders for memory management
diff --git a/gn/tools/gn/json_project_writer.cc b/gn/tools/gn/json_project_writer.cc
index 4f4b0e7c41d..1b000865569 100644
--- a/gn/tools/gn/json_project_writer.cc
+++ b/gn/tools/gn/json_project_writer.cc
@@ -81,48 +81,6 @@ bool FilterTargets(const BuildSettings* build_settings,
return true;
}
-std::string RenderJSON(const BuildSettings* build_settings,
- const Builder& builder,
- std::vector<const Target*>& all_targets) {
- Label default_toolchain_label;
-
- auto targets = std::make_unique<base::DictionaryValue>();
- for (const auto* target : all_targets) {
- if (default_toolchain_label.is_null())
- default_toolchain_label = target->settings()->default_toolchain_label();
- auto description =
- DescBuilder::DescriptionForTarget(target, "", false, false, false);
- // Outputs need to be asked for separately.
- auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs",
- false, false, false);
- base::DictionaryValue* outputs_value = nullptr;
- if (outputs->GetDictionary("source_outputs", &outputs_value) &&
- !outputs_value->empty()) {
- description->MergeDictionary(outputs.get());
- }
- targets->SetWithoutPathExpansion(
- target->label().GetUserVisibleName(default_toolchain_label),
- std::move(description));
- }
-
- auto settings = std::make_unique<base::DictionaryValue>();
- settings->SetKey("root_path", base::Value(build_settings->root_path_utf8()));
- settings->SetKey("build_dir",
- base::Value(build_settings->build_dir().value()));
- settings->SetKey(
- "default_toolchain",
- base::Value(default_toolchain_label.GetUserVisibleName(false)));
-
- auto output = std::make_unique<base::DictionaryValue>();
- output->SetWithoutPathExpansion("targets", std::move(targets));
- output->SetWithoutPathExpansion("build_settings", std::move(settings));
-
- std::string s;
- base::JSONWriter::WriteWithOptions(
- *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
- return s;
-}
-
bool InvokePython(const BuildSettings* build_settings,
const base::FilePath& python_script_path,
const std::string& python_script_extra_args,
@@ -192,7 +150,7 @@ bool JSONProjectWriter::RunAndWriteFiles(
return false;
}
- std::string json = RenderJSON(build_settings, builder, targets);
+ std::string json = RenderJSON(build_settings, targets);
if (!ContentsEqual(output_path, json)) {
if (!WriteFileIfChanged(output_path, json, err)) {
return false;
@@ -218,3 +176,45 @@ bool JSONProjectWriter::RunAndWriteFiles(
return true;
}
+
+std::string JSONProjectWriter::RenderJSON(
+ const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets) {
+ Label default_toolchain_label;
+
+ auto targets = std::make_unique<base::DictionaryValue>();
+ for (const auto* target : all_targets) {
+ if (default_toolchain_label.is_null())
+ default_toolchain_label = target->settings()->default_toolchain_label();
+ auto description =
+ DescBuilder::DescriptionForTarget(target, "", false, false, false);
+ // Outputs need to be asked for separately.
+ auto outputs = DescBuilder::DescriptionForTarget(target, "source_outputs",
+ false, false, false);
+ base::DictionaryValue* outputs_value = nullptr;
+ if (outputs->GetDictionary("source_outputs", &outputs_value) &&
+ !outputs_value->empty()) {
+ description->MergeDictionary(outputs.get());
+ }
+ targets->SetWithoutPathExpansion(
+ target->label().GetUserVisibleName(default_toolchain_label),
+ std::move(description));
+ }
+
+ auto settings = std::make_unique<base::DictionaryValue>();
+ settings->SetKey("root_path", base::Value(build_settings->root_path_utf8()));
+ settings->SetKey("build_dir",
+ base::Value(build_settings->build_dir().value()));
+ settings->SetKey(
+ "default_toolchain",
+ base::Value(default_toolchain_label.GetUserVisibleName(false)));
+
+ auto output = std::make_unique<base::DictionaryValue>();
+ output->SetWithoutPathExpansion("targets", std::move(targets));
+ output->SetWithoutPathExpansion("build_settings", std::move(settings));
+
+ std::string s;
+ base::JSONWriter::WriteWithOptions(
+ *output.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &s);
+ return s;
+}
diff --git a/gn/tools/gn/json_project_writer.h b/gn/tools/gn/json_project_writer.h
index 8c293bfd93d..2c8a2b7102a 100644
--- a/gn/tools/gn/json_project_writer.h
+++ b/gn/tools/gn/json_project_writer.h
@@ -21,6 +21,13 @@ class JSONProjectWriter {
const std::string& dir_filter_string,
bool quiet,
Err* err);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ActionWithResponseFile);
+ FRIEND_TEST_ALL_PREFIXES(JSONProjectWriter, ForEachWithResponseFile);
+
+ static std::string RenderJSON(const BuildSettings* build_settings,
+ std::vector<const Target*>& all_targets);
};
#endif
diff --git a/gn/tools/gn/json_project_writer_unittest.cc b/gn/tools/gn/json_project_writer_unittest.cc
new file mode 100644
index 00000000000..9c933dd5ef0
--- /dev/null
+++ b/gn/tools/gn/json_project_writer_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 2019 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 "base/strings/string_util.h"
+#include "tools/gn/json_project_writer.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+TEST(JSONProjectWriter, ActionWithResponseFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION);
+
+ target.sources().push_back(SourceFile("//foo/source1.txt"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() =
+ SubstitutionList::MakeForTest("{{response_file_name}}");
+ target.action_values().rsp_file_contents() =
+ SubstitutionList::MakeForTest("-j", "3");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/output1.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ std::string out =
+ JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"build_settings\": {\n"
+ " \"build_dir\": \"//out/Debug/\",\n"
+ " \"default_toolchain\": \"//toolchain:default\",\n"
+ " \"root_path\": \"\"\n"
+ " },\n"
+ " \"targets\": {\n"
+ " \"//foo:bar()\": {\n"
+ " \"args\": [ \"{{response_file_name}}\" ],\n"
+ " \"deps\": [ ],\n"
+ " \"inputs\": [ \"//foo/input1.txt\" ],\n"
+ " \"metadata\": {\n"
+ "\n"
+ " },\n"
+ " \"outputs\": [ \"//out/Debug/output1.out\" ],\n"
+ " \"public\": \"*\",\n"
+ " \"response_file_contents\": [ \"-j\", \"3\" ],\n"
+ " \"script\": \"//foo/script.py\",\n"
+ " \"sources\": [ \"//foo/source1.txt\" ],\n"
+ " \"testonly\": false,\n"
+ " \"toolchain\": \"\",\n"
+ " \"type\": \"action\",\n"
+ " \"visibility\": [ ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ EXPECT_EQ(expected_json, out);
+}
+
+TEST(JSONProjectWriter, ForEachWithResponseFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::ACTION_FOREACH);
+
+ target.sources().push_back(SourceFile("//foo/input1.txt"));
+ target.action_values().set_script(SourceFile("//foo/script.py"));
+
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Make sure we get interesting substitutions for both the args and the
+ // response file contents.
+ target.action_values().args() = SubstitutionList::MakeForTest(
+ "{{source}}", "{{source_file_part}}", "{{response_file_name}}");
+ target.action_values().rsp_file_contents() =
+ SubstitutionList::MakeForTest("-j", "{{source_name_part}}");
+ target.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/{{source_name_part}}.out");
+
+ setup.build_settings()->set_python_path(
+ base::FilePath(FILE_PATH_LITERAL("/usr/bin/python")));
+ std::vector<const Target*> targets;
+ targets.push_back(&target);
+ std::string out =
+ JSONProjectWriter::RenderJSON(setup.build_settings(), targets);
+#if defined(OS_WIN)
+ base::ReplaceSubstringsAfterOffset(&out, 0, "\r\n", "\n");
+#endif
+ const char expected_json[] =
+ "{\n"
+ " \"build_settings\": {\n"
+ " \"build_dir\": \"//out/Debug/\",\n"
+ " \"default_toolchain\": \"//toolchain:default\",\n"
+ " \"root_path\": \"\"\n"
+ " },\n"
+ " \"targets\": {\n"
+ " \"//foo:bar()\": {\n"
+ " \"args\": [ \"{{source}}\", \"{{source_file_part}}\", "
+ "\"{{response_file_name}}\" ],\n"
+ " \"deps\": [ ],\n"
+ " \"metadata\": {\n"
+ "\n"
+ " },\n"
+ " \"output_patterns\": [ "
+ "\"//out/Debug/{{source_name_part}}.out\" ],\n"
+ " \"outputs\": [ \"//out/Debug/input1.out\" ],\n"
+ " \"public\": \"*\",\n"
+ " \"response_file_contents\": [ \"-j\", \"{{source_name_part}}\" "
+ "],\n"
+ " \"script\": \"//foo/script.py\",\n"
+ " \"sources\": [ \"//foo/input1.txt\" ],\n"
+ " \"testonly\": false,\n"
+ " \"toolchain\": \"\",\n"
+ " \"type\": \"action_foreach\",\n"
+ " \"visibility\": [ ]\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ EXPECT_EQ(expected_json, out);
+}
diff --git a/gn/tools/gn/label.cc b/gn/tools/gn/label.cc
index e8494bee82e..22b6f810a99 100644
--- a/gn/tools/gn/label.cc
+++ b/gn/tools/gn/label.cc
@@ -250,8 +250,6 @@ Implicit names
//tools/gn -> //tools/gn:gn
)*";
-Label::Label() = default;
-
Label::Label(const SourceDir& dir,
const base::StringPiece& name,
const SourceDir& toolchain_dir,
@@ -265,10 +263,6 @@ Label::Label(const SourceDir& dir, const base::StringPiece& name) : dir_(dir) {
name_.assign(name.data(), name.size());
}
-Label::Label(const Label& other) = default;
-
-Label::~Label() = default;
-
// static
Label Label::Resolve(const SourceDir& current_dir,
const Label& current_toolchain,
diff --git a/gn/tools/gn/label.h b/gn/tools/gn/label.h
index 3c3d85a4802..6606a6b5ad6 100644
--- a/gn/tools/gn/label.h
+++ b/gn/tools/gn/label.h
@@ -17,7 +17,7 @@ class Value;
// part, so it starts with a slash, and has one colon.
class Label {
public:
- Label();
+ Label() = default;
// Makes a label given an already-separated out path and name.
// See also Resolve().
@@ -28,8 +28,6 @@ class Label {
// Makes a label with an empty toolchain.
Label(const SourceDir& dir, const base::StringPiece& name);
- Label(const Label& other);
- ~Label();
// Resolves a string from a build file that may be relative to the
// current directory into a fully qualified label. On failure returns an
@@ -71,20 +69,9 @@ class Label {
}
bool operator!=(const Label& other) const { return !operator==(other); }
bool operator<(const Label& other) const {
- if (int c = dir_.value().compare(other.dir_.value()))
- return c < 0;
- if (int c = name_.compare(other.name_))
- return c < 0;
- if (int c = toolchain_dir_.value().compare(other.toolchain_dir_.value()))
- return c < 0;
- return toolchain_name_ < other.toolchain_name_;
- }
-
- void swap(Label& other) {
- dir_.swap(other.dir_);
- name_.swap(other.name_);
- toolchain_dir_.swap(other.toolchain_dir_);
- toolchain_name_.swap(other.toolchain_name_);
+ return std::tie(dir_, name_, toolchain_dir_, toolchain_name_) <
+ std::tie(other.dir_, other.name_, other.toolchain_dir_,
+ other.toolchain_name_);
}
// Returns true if the toolchain dir/name of this object matches some
@@ -117,10 +104,6 @@ struct hash<Label> {
} // namespace std
-inline void swap(Label& lhs, Label& rhs) {
- lhs.swap(rhs);
-}
-
extern const char kLabels_Help[];
#endif // TOOLS_GN_LABEL_H_
diff --git a/gn/tools/gn/lib_file.cc b/gn/tools/gn/lib_file.cc
index 9c55aaa8747..81e54fbf9a6 100644
--- a/gn/tools/gn/lib_file.cc
+++ b/gn/tools/gn/lib_file.cc
@@ -6,8 +6,6 @@
#include "base/logging.h"
-LibFile::LibFile() = default;
-
LibFile::LibFile(const SourceFile& source_file) : source_file_(source_file) {}
LibFile::LibFile(const base::StringPiece& lib_name)
@@ -15,11 +13,6 @@ LibFile::LibFile(const base::StringPiece& lib_name)
DCHECK(!lib_name.empty());
}
-void LibFile::Swap(LibFile* other) {
- name_.swap(other->name_);
- source_file_.swap(other->source_file_);
-}
-
const std::string& LibFile::value() const {
return is_source_file() ? source_file_.value() : name_;
}
diff --git a/gn/tools/gn/lib_file.h b/gn/tools/gn/lib_file.h
index f1f073ffc71..58621ebedf2 100644
--- a/gn/tools/gn/lib_file.h
+++ b/gn/tools/gn/lib_file.h
@@ -17,11 +17,11 @@
// a library name (a string).
class LibFile {
public:
- LibFile();
+ LibFile() = default;
+
explicit LibFile(const base::StringPiece& lib_name);
explicit LibFile(const SourceFile& source_file);
- void Swap(LibFile* other);
bool is_source_file() const { return name_.empty(); }
// Returns name, or source_file().value() (whichever is set).
@@ -51,8 +51,4 @@ struct hash<LibFile> {
} // namespace std
-inline void swap(LibFile& lhs, LibFile& rhs) {
- lhs.Swap(&rhs);
-}
-
#endif // TOOLS_GN_LIB_FILE_H_
diff --git a/gn/tools/gn/loader.cc b/gn/tools/gn/loader.cc
index a069fb83d2b..d1a2953bb83 100644
--- a/gn/tools/gn/loader.cc
+++ b/gn/tools/gn/loader.cc
@@ -120,7 +120,8 @@ void LoaderImpl::Load(const SourceFile& file,
std::unique_ptr<ToolchainRecord> new_record =
std::make_unique<ToolchainRecord>(build_settings_, Label(), Label());
ToolchainRecord* record = new_record.get();
- toolchain_records_[Label()] = std::move(new_record);
+ Label empty_label; // VS issues spurious warning using ...[Label()].
+ toolchain_records_[empty_label] = std::move(new_record);
// The default build config is no dependent on the toolchain definition,
// since we need to load the build config before we know what the default
@@ -308,9 +309,19 @@ void LoaderImpl::BackgroundLoadBuildConfig(
settings->build_settings()->build_config_file().value());
trace.SetToolchain(settings->toolchain_label());
+ // Run the BUILDCONFIG with its directory as the current one. We want
+ // BUILDCONFIG to modify the base_config so can't make a copy or a nested one.
+ base_config->set_source_dir(
+ settings->build_settings()->build_config_file().GetDir());
+
Err err;
root->Execute(base_config, &err);
+ // Put back the root as the default source dir. This probably isn't necessary
+ // as other scopes will set their directories to their own path, but it's a
+ // better default than the build config's directory.
+ base_config->set_source_dir(SourceDir("//"));
+
// Clear all private variables left in the scope. We want the root build
// config to be like a .gni file in that variables beginning with an
// underscore aren't exported.
@@ -383,7 +394,7 @@ void LoaderImpl::DidLoadBuildConfig(const Label& label) {
for (const auto& load : old_loads) {
if (load.toolchain_name.is_null()) {
// Fix up toolchain label
- invocations_.insert(LoadID(load.file, label));
+ invocations_.emplace(load.file, label);
} else {
// Can keep the old one.
invocations_.insert(load);
diff --git a/gn/tools/gn/location.cc b/gn/tools/gn/location.cc
index b9e4c221f0d..2e7b27b3d2e 100644
--- a/gn/tools/gn/location.cc
+++ b/gn/tools/gn/location.cc
@@ -10,7 +10,7 @@
#include "base/strings/string_number_conversions.h"
#include "tools/gn/input_file.h"
-Location::Location() : file_(nullptr), line_number_(-1), column_number_(-1) {}
+Location::Location() = default;
Location::Location(const InputFile* file,
int line_number,
diff --git a/gn/tools/gn/location.h b/gn/tools/gn/location.h
index de9b2fdbe6b..471391b374a 100644
--- a/gn/tools/gn/location.h
+++ b/gn/tools/gn/location.h
@@ -31,10 +31,10 @@ class Location {
std::string Describe(bool include_column_number) const;
private:
- const InputFile* file_; // Null when unset.
- int line_number_; // -1 when unset. 1-based.
- int column_number_; // -1 when unset. 1-based.
- int byte_; // Index into the buffer, 0-based.
+ const InputFile* file_ = nullptr; // Null when unset.
+ int line_number_ = -1; // -1 when unset. 1-based.
+ int column_number_ = -1; // -1 when unset. 1-based.
+ int byte_ = 0; // Index into the buffer, 0-based.
};
// Represents a range in a source file. Used for error reporting.
diff --git a/gn/tools/gn/metadata.cc b/gn/tools/gn/metadata.cc
index 3250b031327..bc4c272ea42 100644
--- a/gn/tools/gn/metadata.cc
+++ b/gn/tools/gn/metadata.cc
@@ -6,6 +6,143 @@
#include "tools/gn/filesystem_utils.h"
+const char kMetadata_Help[] =
+ R"(Metadata Collection
+
+ Metadata is information attached to targets throughout the dependency tree. GN
+ allows for the collection of this data into files written during the generation
+ step, enabing users to expose and aggregate this data based on the dependency
+ tree.
+
+generated_file targets
+
+ Similar to the write_file() function, the generated_file target type
+ creates a file in the specified location with the specified content. The
+ primary difference between the function and the target type is that the
+ write_file function does the file write at parse time, while the
+ generated_file target type writes at target resolution time. See
+ "gn help generated_file" for more detail.
+
+ When written at target resolution time, the generated_file enables GN to
+ collect and write aggregated metadata from dependents.
+
+ A generated_file target can declare either 'contents' (to write statically
+ known contents to a file) or 'data_keys'(to aggregate metadata and write the
+ result to a file). It can also specify 'walk_keys' (to restrict the metadata
+ collection), 'output_conversion', and 'rebase'.
+
+
+Collection and Aggregation
+
+ Targets can declare a 'metadata' variable containing a scope, and this
+ metadata is collected and written to file by generated_file aggregation
+ targets. The 'metadata' scope must contain only list values, since the
+ aggregation step collects a list of these values.
+
+ During the target resolution, generated_file targets will walk their
+ dependencies recursively, collecting metadata based on the specified
+ 'data_keys'. 'data_keys' is specified as a list of strings, used by the walk
+ to identify which variables in dependencies' 'metadata' scopes to collect.
+
+ The walk begins with the listed dependencies of the 'generated_file' target,
+ for each checking the "metadata" scope for any of the "data_keys". If
+ present, the data in those variables is appended to the aggregate list. Note
+ that this means that if more than one walk key is specified, the data in all
+ of them will be aggregated into one list. From there, the walk will then
+ recurse into the dependencies of each target it encounters, collecting the
+ specified metadata for each.
+
+ For example:
+
+ group("a") {
+ metadata = {
+ doom_melon = [ "enable" ]
+ my_files = [ "foo.cpp" ]
+ my_extra_files = [ "bar.cpp" ]
+ }
+
+ deps = [ ":b" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "baz.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files", "my_extra_files" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data:
+
+ foo.cpp
+ bar.cpp
+ baz.cpp
+
+ The dependency walk can be limited by using the "walk_keys". This is a list of
+ labels that should be included in the walk. All labels specified here should
+ also be in one of the deps lists. These keys act as barriers, where the walk
+ will only recurse into targets listed here. An empty list in all specified
+ barriers will end that portion of the walk.
+
+ group("a") {
+ metadata = {
+ my_files = [ "foo.cpp" ]
+ my_files_barrier [ ":b" ]
+ }
+
+ deps = [ ":b", ":c" ]
+ }
+
+ group("b") {
+ metadata = {
+ my_files = [ "bar.cpp" ]
+ }
+ }
+
+ group("c") {
+ metadata = {
+ my_files = [ "doom_melon.cpp" ]
+ }
+ }
+
+ generated_file("metadata") {
+ outputs = [ "$root_build_dir/my_files.json" ]
+ data_keys = [ "my_files", "my_extra_files" ]
+
+ deps = [ ":a" ]
+ }
+
+ The above will produce the following file data (note that `doom_melon.cpp` is
+ not included):
+
+ foo.cpp
+ bar.cpp
+
+ A common example of this sort of barrier is in builds that have host tools
+ built as part of the tree, but do not want the metadata from those host tools
+ to be collected with the target-side code.
+
+Common Uses
+
+ Metadata can be used to collect information about the different targets in the
+ build, and so a common use is to provide post-build tooling with a set of data
+ necessary to do aggregation tasks. For example, if each test target specifies
+ the output location of its binary to run in a metadata field, that can be
+ collected into a single file listing the locations of all tests in the
+ dependency tree. A local build tool (or continuous integration infrastructure)
+ can then use that file to know which tests exist, and where, and run them
+ accordingly.
+
+ Another use is in image creation, where a post-build image tool needs to know
+ various pieces of information about the components it should include in order
+ to put together the correct image.
+)";
+
bool Metadata::WalkStep(const BuildSettings* settings,
const std::vector<std::string>& keys_to_extract,
const std::vector<std::string>& keys_to_walk,
@@ -28,16 +165,11 @@ bool Metadata::WalkStep(const BuildSettings* settings,
if (!rebase_dir.is_null()) {
for (const auto& val : iter->second.list_value()) {
- if (!val.VerifyTypeIs(Value::STRING, err))
+ std::pair<Value, bool> pair =
+ this->RebaseValue(settings, rebase_dir, val, err);
+ if (!pair.second)
return false;
- std::string filename = source_dir_.ResolveRelativeAs(
- /*as_file = */ true, val, err, settings->root_path_utf8());
- if (err->has_error())
- return false;
-
- result->emplace_back(
- val.origin(),
- RebasePath(filename, rebase_dir, settings->root_path_utf8()));
+ result->push_back(pair.first);
}
} else {
result->insert(result->end(), iter->second.list_value().begin(),
@@ -67,3 +199,73 @@ bool Metadata::WalkStep(const BuildSettings* settings,
return true;
}
+
+std::pair<Value, bool> Metadata::RebaseValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ switch (value.type()) {
+ case Value::STRING:
+ return this->RebaseStringValue(settings, rebase_dir, value, err);
+ case Value::LIST:
+ return this->RebaseListValue(settings, rebase_dir, value, err);
+ case Value::SCOPE:
+ return this->RebaseScopeValue(settings, rebase_dir, value, err);
+ default:
+ return std::make_pair(value, true);
+ }
+}
+
+std::pair<Value, bool> Metadata::RebaseStringValue(
+ const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::STRING, err))
+ return std::make_pair(value, false);
+ std::string filename = source_dir_.ResolveRelativeAs(
+ /*as_file = */ true, value, err, settings->root_path_utf8());
+ if (err->has_error())
+ return std::make_pair(value, false);
+ Value rebased_value(value.origin(), RebasePath(filename, rebase_dir,
+ settings->root_path_utf8()));
+ return std::make_pair(rebased_value, true);
+}
+
+std::pair<Value, bool> Metadata::RebaseListValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::LIST, err))
+ return std::make_pair(value, false);
+
+ Value rebased_list_value(value.origin(), Value::LIST);
+ for (auto& val : value.list_value()) {
+ std::pair<Value, bool> pair = RebaseValue(settings, rebase_dir, val, err);
+ if (!pair.second)
+ return std::make_pair(value, false);
+ rebased_list_value.list_value().push_back(pair.first);
+ }
+ return std::make_pair(rebased_list_value, true);
+}
+
+std::pair<Value, bool> Metadata::RebaseScopeValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const {
+ if (!value.VerifyTypeIs(Value::SCOPE, err))
+ return std::make_pair(value, false);
+ Value rebased_scope_value(value);
+ Scope::KeyValueMap scope_values;
+ value.scope_value()->GetCurrentScopeValues(&scope_values);
+ for (auto& value_pair : scope_values) {
+ std::pair<Value, bool> pair =
+ RebaseValue(settings, rebase_dir, value_pair.second, err);
+ if (!pair.second)
+ return std::make_pair(value, false);
+
+ rebased_scope_value.scope_value()->SetValue(value_pair.first, pair.first,
+ value.origin());
+ }
+ return std::make_pair(rebased_scope_value, true);
+} \ No newline at end of file
diff --git a/gn/tools/gn/metadata.h b/gn/tools/gn/metadata.h
index da79fdf5dcb..7b6168a5e1a 100644
--- a/gn/tools/gn/metadata.h
+++ b/gn/tools/gn/metadata.h
@@ -11,6 +11,8 @@
#include "tools/gn/scope.h"
#include "tools/gn/source_dir.h"
+extern const char kMetadata_Help[];
+
// Metadata about a particular target.
//
// Metadata is a collection of keys and values relating to a particular target.
@@ -29,7 +31,6 @@ class Metadata {
public:
using Contents = Scope::KeyValueMap;
- // Members must be set explicitly.
Metadata() = default;
const ParseNode* origin() const { return origin_; }
@@ -38,7 +39,7 @@ class Metadata {
// The contents of this metadata varaiable.
const Contents& contents() const { return contents_; }
Contents& contents() { return contents_; }
- void set_contents(Contents&& contents) { contents_.swap(contents); }
+ void set_contents(Contents&& contents) { contents_ = std::move(contents); }
// The relative source directory to use when rebasing.
const SourceDir& source_dir() const { return source_dir_; }
@@ -59,10 +60,30 @@ class Metadata {
Err* err) const;
private:
- const ParseNode* origin_;
+ const ParseNode* origin_ = nullptr;
Contents contents_;
SourceDir source_dir_;
+ std::pair<Value, bool> RebaseValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseStringValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseListValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
+ std::pair<Value, bool> RebaseScopeValue(const BuildSettings* settings,
+ const SourceDir& rebase_dir,
+ const Value& value,
+ Err* err) const;
+
DISALLOW_COPY_AND_ASSIGN(Metadata);
};
diff --git a/gn/tools/gn/metadata_unittest.cc b/gn/tools/gn/metadata_unittest.cc
index b15937242bf..901e53995bb 100644
--- a/gn/tools/gn/metadata_unittest.cc
+++ b/gn/tools/gn/metadata_unittest.cc
@@ -99,29 +99,58 @@ TEST(MetadataTest, WalkWithRebase) {
EXPECT_EQ(results, expected);
}
-TEST(MetadataTest, WalkWithRebaseError) {
+TEST(MetadataTest, WalkWithRebaseNonString) {
TestWithScope setup;
Metadata metadata;
metadata.set_source_dir(SourceDir("/usr/home/files/"));
- Value a_expected(nullptr, Value::LIST);
- a_expected.list_value().push_back(Value(nullptr, "foo.cpp"));
- a_expected.list_value().push_back(Value(nullptr, true));
+ Value a(nullptr, Value::LIST);
+ Value inner_list(nullptr, Value::LIST);
+ Value inner_scope(nullptr, Value::SCOPE);
+ inner_list.list_value().push_back(Value(nullptr, "foo.cpp"));
+ inner_list.list_value().push_back(Value(nullptr, "foo/bar.h"));
+ a.list_value().push_back(inner_list);
- metadata.contents().insert(
- std::pair<base::StringPiece, Value>("a", a_expected));
+ std::unique_ptr<Scope> scope(new Scope(setup.settings()));
+ scope->SetValue("a1", Value(nullptr, "foo2.cpp"), nullptr);
+ scope->SetValue("a2", Value(nullptr, "foo/bar2.h"), nullptr);
+ inner_scope.SetScopeValue(std::move(scope));
+ a.list_value().push_back(inner_scope);
+ metadata.contents().insert(std::pair<base::StringPiece, Value>("a", a));
std::vector<std::string> data_keys;
data_keys.emplace_back("a");
std::vector<std::string> walk_keys;
std::vector<Value> next_walk_keys;
std::vector<Value> results;
+ std::vector<Value> expected;
+ Value inner_list_expected(nullptr, Value::LIST);
+ Value inner_scope_expected(nullptr, Value::SCOPE);
+ inner_list_expected.list_value().push_back(
+ Value(nullptr, "../home/files/foo.cpp"));
+ inner_list_expected.list_value().push_back(
+ Value(nullptr, "../home/files/foo/bar.h"));
+ expected.push_back(inner_list_expected);
+
+ std::unique_ptr<Scope> scope_expected(new Scope(setup.settings()));
+ scope_expected->SetValue("a1", Value(nullptr, "../home/files/foo2.cpp"),
+ nullptr);
+ scope_expected->SetValue("a2", Value(nullptr, "../home/files/foo/bar2.h"),
+ nullptr);
+ inner_scope_expected.SetScopeValue(std::move(scope_expected));
+ expected.push_back(inner_scope_expected);
+
+ std::vector<Value> expected_walk_keys;
+ expected_walk_keys.emplace_back(nullptr, "");
+
Err err;
- EXPECT_FALSE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
- walk_keys, SourceDir("/foo_dir/"),
- &next_walk_keys, &results, &err));
- EXPECT_TRUE(err.has_error());
+ EXPECT_TRUE(metadata.WalkStep(setup.settings()->build_settings(), data_keys,
+ walk_keys, SourceDir("/usr/foo_dir/"),
+ &next_walk_keys, &results, &err));
+ EXPECT_FALSE(err.has_error());
+ EXPECT_EQ(next_walk_keys, expected_walk_keys);
+ EXPECT_EQ(results, expected);
}
TEST(MetadataTest, WalkKeysToWalk) {
diff --git a/gn/tools/gn/misc/vim/gn-format.py b/gn/tools/gn/misc/vim/gn-format.py
index 561c76c828a..7e5d6a44a0f 100644
--- a/gn/tools/gn/misc/vim/gn-format.py
+++ b/gn/tools/gn/misc/vim/gn-format.py
@@ -53,7 +53,7 @@ def main():
lines = lines[:-1]
sequence = difflib.SequenceMatcher(None, vim.current.buffer, lines)
for op in reversed(sequence.get_opcodes()):
- if op[0] is not 'equal':
+ if op[0] != 'equal':
vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]]
main()
diff --git a/gn/tools/gn/ninja_action_target_writer.cc b/gn/tools/gn/ninja_action_target_writer.cc
index c2016dcf5e3..31f5261a35e 100644
--- a/gn/tools/gn/ninja_action_target_writer.cc
+++ b/gn/tools/gn/ninja_action_target_writer.cc
@@ -9,6 +9,7 @@
#include "base/strings/string_util.h"
#include "tools/gn/deps_iterator.h"
#include "tools/gn/err.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/pool.h"
#include "tools/gn/settings.h"
#include "tools/gn/string_utils.h"
@@ -145,7 +146,7 @@ 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(Toolchain::TYPE_ACTION);
+ const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
if (tool && tool->pool().ptr) {
out_ << " pool = ";
out_ << tool->pool().ptr->GetNinjaName(
diff --git a/gn/tools/gn/ninja_binary_target_writer.cc b/gn/tools/gn/ninja_binary_target_writer.cc
index 9e5632e7b25..3cf7c48c6cc 100644
--- a/gn/tools/gn/ninja_binary_target_writer.cc
+++ b/gn/tools/gn/ninja_binary_target_writer.cc
@@ -4,287 +4,44 @@
#include "tools/gn/ninja_binary_target_writer.h"
-#include <stddef.h>
-#include <string.h>
-
-#include <cstring>
-#include <set>
#include <sstream>
-#include <unordered_set>
#include "base/strings/string_util.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/deps_iterator.h"
-#include "tools/gn/err.h"
-#include "tools/gn/escape.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
+#include "tools/gn/ninja_c_binary_target_writer.h"
+#include "tools/gn/ninja_rust_binary_target_writer.h"
#include "tools/gn/ninja_utils.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/scheduler.h"
#include "tools/gn/settings.h"
-#include "tools/gn/source_file_type.h"
#include "tools/gn/string_utils.h"
#include "tools/gn/substitution_writer.h"
#include "tools/gn/target.h"
-// Represents a set of tool types. Must be first since it is also shared by
-// some helper functions in the anonymous namespace below.
-class NinjaBinaryTargetWriter::SourceFileTypeSet {
- public:
- SourceFileTypeSet() {
- memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES));
- }
-
- void Set(SourceFileType type) { flags_[static_cast<int>(type)] = true; }
- bool Get(SourceFileType type) const { return flags_[static_cast<int>(type)]; }
-
- private:
- bool flags_[static_cast<int>(SOURCE_NUMTYPES)];
-};
-
-namespace {
-
-// Returns the proper escape options for writing compiler and linker flags.
-EscapeOptions GetFlagOptions() {
- EscapeOptions opts;
- opts.mode = ESCAPE_NINJA_COMMAND;
- return opts;
-}
-
-// Returns the language-specific lang recognized by gcc’s -x flag for
-// precompiled header files.
-const char* GetPCHLangForToolType(Toolchain::ToolType type) {
- switch (type) {
- case Toolchain::TYPE_CC:
- return "c-header";
- case Toolchain::TYPE_CXX:
- return "c++-header";
- case Toolchain::TYPE_OBJC:
- return "objective-c-header";
- case Toolchain::TYPE_OBJCXX:
- return "objective-c++-header";
- default:
- NOTREACHED() << "Not a valid PCH tool type: " << type;
- return "";
- }
-}
-
-// Appends the object files generated by the given source set to the given
-// output vector.
-void AddSourceSetObjectFiles(const Target* source_set,
- UniqueVector<OutputFile>* obj_files) {
- std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
- NinjaBinaryTargetWriter::SourceFileTypeSet used_types;
-
- // Compute object files for all sources. Only link the first output from
- // the tool if there are more than one.
- for (const auto& source : source_set->sources()) {
- Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
- if (source_set->GetOutputFilesForSource(source, &tool_type, &tool_outputs))
- obj_files->push_back(tool_outputs[0]);
-
- used_types.Set(GetSourceFileType(source));
- }
-
- // Add MSVC precompiled header object files. GCC .gch files are not object
- // files so they are omitted.
- if (source_set->config_values().has_precompiled_headers()) {
- if (used_types.Get(SOURCE_C)) {
- const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CC);
- if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, Toolchain::TYPE_CC, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (used_types.Get(SOURCE_CPP)) {
- const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_CXX);
- if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (used_types.Get(SOURCE_M)) {
- const Tool* tool = source_set->toolchain()->GetTool(Toolchain::TYPE_OBJC);
- if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- if (used_types.Get(SOURCE_MM)) {
- const Tool* tool =
- source_set->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
- if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
- GetPCHOutputFiles(source_set, Toolchain::TYPE_OBJCXX, &tool_outputs);
- obj_files->Append(tool_outputs.begin(), tool_outputs.end());
- }
- }
- }
-}
-
-} // namespace
-
NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
std::ostream& out)
: NinjaTargetWriter(target, out),
- tool_(target->toolchain()->GetToolForTargetFinalOutput(target)),
rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) {}
NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() = default;
void NinjaBinaryTargetWriter::Run() {
- // Figure out what source types are needed.
- SourceFileTypeSet used_types;
- for (const auto& source : target_->sources())
- used_types.Set(GetSourceFileType(source));
-
- WriteCompilerVars(used_types);
-
- OutputFile input_dep = WriteInputsStampAndGetDep();
-
- // The input dependencies will be an order-only dependency. This will cause
- // Ninja to make sure the inputs are up to date before compiling this source,
- // but changes in the inputs deps won't cause the file to be recompiled.
- //
- // This is important to prevent changes in unrelated actions that are
- // upstream of this target from causing everything to be recompiled.
- //
- // Why can we get away with this rather than using implicit deps ("|", which
- // will force rebuilds when the inputs change)? For source code, the
- // computed dependencies of all headers will be computed by the compiler,
- // which will cause source rebuilds if any "real" upstream dependencies
- // change.
- //
- // If a .cc file is generated by an input dependency, Ninja will see the
- // input to the build rule doesn't exist, and that it is an output from a
- // previous step, and build the previous step first. This is a "real"
- // dependency and doesn't need | or || to express.
- //
- // The only case where this rule matters is for the first build where no .d
- // files exist, and Ninja doesn't know what that source file depends on. In
- // this case it's sufficient to ensure that the upstream dependencies are
- // built first. This is exactly what Ninja's order-only dependencies
- // expresses.
- //
- // The order only deps are referenced by each source file compile,
- // but also by PCH compiles. The latter are annoying to count, so omit
- // them here. This means that binary targets with a single source file
- // that also use PCH files won't have a stamp file even though having
- // one would make output ninja file size a bit lower. That's ok, binary
- // targets with a single source are rare.
- size_t num_stamp_uses = target_->sources().size();
- std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
- std::vector<const Target*>(), num_stamp_uses);
-
- // For GCC builds, the .gch files are not object files, but still need to be
- // added as explicit dependencies below. The .gch output files are placed in
- // |pch_other_files|. This is to prevent linking against them.
- std::vector<OutputFile> pch_obj_files;
- std::vector<OutputFile> pch_other_files;
- WritePCHCommands(used_types, input_dep, order_only_deps, &pch_obj_files,
- &pch_other_files);
- std::vector<OutputFile>* pch_files =
- !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
-
- // Treat all pch output files as explicit dependencies of all
- // compiles that support them. Some notes:
- //
- // - On Windows, the .pch file is the input to the compile, not the
- // precompiled header's corresponding object file that we're using here.
- // But Ninja's depslog doesn't support multiple outputs from the
- // precompiled header compile step (it outputs both the .pch file and a
- // corresponding .obj file). So we consistently list the .obj file and the
- // .pch file we really need comes along with it.
- //
- // - GCC .gch files are not object files, therefore they are not added to the
- // object file list.
- std::vector<OutputFile> obj_files;
- std::vector<SourceFile> other_files;
- WriteSources(*pch_files, input_dep, order_only_deps, &obj_files,
- &other_files);
-
- // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
- obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
- if (!CheckForDuplicateObjectFiles(obj_files))
+ if (target_->source_types_used().RustSourceUsed()) {
+ NinjaRustBinaryTargetWriter writer(target_, out_);
+ writer.Run();
return;
-
- if (target_->output_type() == Target::SOURCE_SET) {
- WriteSourceSetStamp(obj_files);
-#ifndef NDEBUG
- // Verify that the function that separately computes a source set's object
- // files match the object files just computed.
- UniqueVector<OutputFile> computed_obj;
- AddSourceSetObjectFiles(target_, &computed_obj);
- DCHECK_EQ(obj_files.size(), computed_obj.size());
- for (const auto& obj : obj_files)
- DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
-#endif
- } else {
- WriteLinkerStuff(obj_files, other_files, input_dep);
- }
-}
-
-void NinjaBinaryTargetWriter::WriteCompilerVars(
- const SourceFileTypeSet& used_types) {
- const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
-
- // Defines.
- if (subst.used[SUBSTITUTION_DEFINES]) {
- out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
- RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
- DefineWriter(), out_);
- out_ << std::endl;
- }
-
- // Include directories.
- if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
- out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
- PathOutput include_path_output(
- path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND);
- RecursiveTargetConfigToStream<SourceDir>(
- target_, &ConfigValues::include_dirs,
- IncludeWriter(include_path_output), out_);
- out_ << std::endl;
- }
-
- bool has_precompiled_headers =
- target_->config_values().has_precompiled_headers();
-
- EscapeOptions opts = GetFlagOptions();
- if (used_types.Get(SOURCE_S) || used_types.Get(SOURCE_ASM)) {
- WriteOneFlag(target_, SUBSTITUTION_ASMFLAGS, false, Toolchain::TYPE_NONE,
- &ConfigValues::asmflags, opts, path_output_, out_);
- }
- if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) ||
- used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM)) {
- WriteOneFlag(target_, SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE,
- &ConfigValues::cflags, opts, path_output_, out_);
- }
- if (used_types.Get(SOURCE_C)) {
- WriteOneFlag(target_, SUBSTITUTION_CFLAGS_C, has_precompiled_headers,
- Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts, path_output_, out_);
- }
- if (used_types.Get(SOURCE_CPP)) {
- WriteOneFlag(target_, SUBSTITUTION_CFLAGS_CC, has_precompiled_headers,
- Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts, path_output_, out_);
- }
- if (used_types.Get(SOURCE_M)) {
- WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers,
- Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts, path_output_, out_);
- }
- if (used_types.Get(SOURCE_MM)) {
- WriteOneFlag(target_, SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers,
- Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts, path_output_, out_);
}
- WriteSharedVars(subst);
+ NinjaCBinaryTargetWriter writer(target_, out_);
+ writer.Run();
}
OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
CHECK(target_->toolchain()) << "Toolchain not set on target "
<< target_->label().GetUserVisibleName(true);
- std::vector<const SourceFile*> inputs;
+ UniqueVector<const SourceFile*> inputs;
for (ConfigValuesIterator iter(target_); !iter.done(); iter.Next()) {
for (const auto& input : iter.cur().inputs()) {
inputs.push_back(&input);
@@ -300,15 +57,15 @@ OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
return OutputFile(settings_->build_settings(), *inputs[0]);
// Make a stamp file.
- OutputFile input_stamp_file =
+ OutputFile stamp_file =
GetBuildDirForTargetAsOutputFile(target_, BuildDirType::OBJ);
- input_stamp_file.value().append(target_->label().name());
- input_stamp_file.value().append(".inputs.stamp");
+ stamp_file.value().append(target_->label().name());
+ stamp_file.value().append(".inputs.stamp");
out_ << "build ";
- path_output_.WriteFile(out_, input_stamp_file);
+ path_output_.WriteFile(out_, stamp_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
// File inputs.
for (const auto* input : inputs) {
@@ -316,444 +73,8 @@ OutputFile NinjaBinaryTargetWriter::WriteInputsStampAndGetDep() const {
path_output_.WriteFile(out_, *input);
}
- out_ << "\n";
- return input_stamp_file;
-}
-
-void NinjaBinaryTargetWriter::WritePCHCommands(
- const SourceFileTypeSet& used_types,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files) {
- if (!target_->config_values().has_precompiled_headers())
- return;
-
- const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC);
- if (tool_c && tool_c->precompiled_header_type() != Tool::PCH_NONE &&
- used_types.Get(SOURCE_C)) {
- WritePCHCommand(SUBSTITUTION_CFLAGS_C, Toolchain::TYPE_CC,
- tool_c->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
- const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX);
- if (tool_cxx && tool_cxx->precompiled_header_type() != Tool::PCH_NONE &&
- used_types.Get(SOURCE_CPP)) {
- WritePCHCommand(SUBSTITUTION_CFLAGS_CC, Toolchain::TYPE_CXX,
- tool_cxx->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-
- const Tool* tool_objc = target_->toolchain()->GetTool(Toolchain::TYPE_OBJC);
- if (tool_objc && tool_objc->precompiled_header_type() == Tool::PCH_GCC &&
- used_types.Get(SOURCE_M)) {
- WritePCHCommand(SUBSTITUTION_CFLAGS_OBJC, Toolchain::TYPE_OBJC,
- tool_objc->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-
- const Tool* tool_objcxx =
- target_->toolchain()->GetTool(Toolchain::TYPE_OBJCXX);
- if (tool_objcxx && tool_objcxx->precompiled_header_type() == Tool::PCH_GCC &&
- used_types.Get(SOURCE_MM)) {
- WritePCHCommand(SUBSTITUTION_CFLAGS_OBJCC, Toolchain::TYPE_OBJCXX,
- tool_objcxx->precompiled_header_type(), input_dep,
- order_only_deps, object_files, other_files);
- }
-}
-
-void NinjaBinaryTargetWriter::WritePCHCommand(
- SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- Tool::PrecompiledHeaderType header_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files) {
- switch (header_type) {
- case Tool::PCH_MSVC:
- WriteWindowsPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
- object_files);
- break;
- case Tool::PCH_GCC:
- WriteGCCPCHCommand(flag_type, tool_type, input_dep, order_only_deps,
- other_files);
- break;
- case Tool::PCH_NONE:
- NOTREACHED() << "Cannot write a PCH command with no PCH header type";
- break;
- }
-}
-
-void NinjaBinaryTargetWriter::WriteGCCPCHCommand(
- SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* gch_files) {
- // Compute the pch output file (it will be language-specific).
- std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target_, tool_type, &outputs);
- if (outputs.empty())
- return;
-
- gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
-
- std::vector<OutputFile> extra_deps;
- if (!input_dep.value().empty())
- extra_deps.push_back(input_dep);
-
- // Build line to compile the file.
- WriteCompilerBuildLine(target_->config_values().precompiled_source(),
- extra_deps, order_only_deps, tool_type, outputs);
-
- // This build line needs a custom language-specific flags value. Rule-specific
- // variables are just indented underneath the rule line.
- out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
-
- // Each substitution flag is overwritten in the target rule to replace the
- // implicitly generated -include flag with the -x <header lang> flag required
- // for .gch targets.
- EscapeOptions opts = GetFlagOptions();
- if (tool_type == Toolchain::TYPE_CC) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
- out_);
- } else if (tool_type == Toolchain::TYPE_CXX) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
- opts, out_);
- } else if (tool_type == Toolchain::TYPE_OBJC) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
- opts, out_);
- } else if (tool_type == Toolchain::TYPE_OBJCXX) {
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
- opts, out_);
- }
-
- // Append the command to specify the language of the .gch file.
- out_ << " -x " << GetPCHLangForToolType(tool_type);
-
- // Write two blank lines to help separate the PCH build lines from the
- // regular source build lines.
- out_ << std::endl << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteWindowsPCHCommand(
- SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files) {
- // Compute the pch output file (it will be language-specific).
- std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target_, tool_type, &outputs);
- if (outputs.empty())
- return;
-
- object_files->insert(object_files->end(), outputs.begin(), outputs.end());
-
- std::vector<OutputFile> extra_deps;
- if (!input_dep.value().empty())
- extra_deps.push_back(input_dep);
-
- // Build line to compile the file.
- WriteCompilerBuildLine(target_->config_values().precompiled_source(),
- extra_deps, order_only_deps, tool_type, outputs);
-
- // This build line needs a custom language-specific flags value. Rule-specific
- // variables are just indented underneath the rule line.
- out_ << " " << kSubstitutionNinjaNames[flag_type] << " =";
-
- // Append the command to generate the .pch file.
- // This adds the value to the existing flag instead of overwriting it.
- out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}";
- out_ << " /Yc" << target_->config_values().precompiled_header();
-
- // Write two blank lines to help separate the PCH build lines from the
- // regular source build lines.
- out_ << std::endl << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteSources(
- const std::vector<OutputFile>& pch_deps,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<SourceFile>* other_files) {
- object_files->reserve(object_files->size() + target_->sources().size());
-
- std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
- std::vector<OutputFile> deps;
- for (const auto& source : target_->sources()) {
- // Clear the vector but maintain the max capacity to prevent reallocations.
- deps.resize(0);
- Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
- if (!target_->GetOutputFilesForSource(source, &tool_type, &tool_outputs)) {
- if (GetSourceFileType(source) == SOURCE_DEF)
- other_files->push_back(source);
- continue; // No output for this source.
- }
-
- if (!input_dep.value().empty())
- deps.push_back(input_dep);
-
- if (tool_type != Toolchain::TYPE_NONE) {
- // Only include PCH deps that correspond to the tool type, for instance,
- // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
- // for the output of a C tool type.
- //
- // This makes the assumption that pch_deps only contains pch output files
- // with the naming scheme specified in GetWindowsPCHObjectExtension or
- // GetGCCPCHOutputExtension.
- const Tool* tool = target_->toolchain()->GetTool(tool_type);
- if (tool->precompiled_header_type() != Tool::PCH_NONE) {
- for (const auto& dep : pch_deps) {
- const std::string& output_value = dep.value();
- size_t extension_offset = FindExtensionOffset(output_value);
- if (extension_offset == std::string::npos)
- continue;
- std::string output_extension;
- if (tool->precompiled_header_type() == Tool::PCH_MSVC) {
- output_extension = GetWindowsPCHObjectExtension(
- tool_type, output_value.substr(extension_offset - 1));
- } else if (tool->precompiled_header_type() == Tool::PCH_GCC) {
- output_extension = GetGCCPCHOutputExtension(tool_type);
- }
- if (output_value.compare(
- output_value.size() - output_extension.size(),
- output_extension.size(), output_extension) == 0) {
- deps.push_back(dep);
- }
- }
- }
- WriteCompilerBuildLine(source, deps, order_only_deps, tool_type,
- tool_outputs);
- }
-
- // It's theoretically possible for a compiler to produce more than one
- // output, but we'll only link to the first output.
- object_files->push_back(tool_outputs[0]);
- }
- out_ << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
- const SourceFile& source,
- const std::vector<OutputFile>& extra_deps,
- const std::vector<OutputFile>& order_only_deps,
- Toolchain::ToolType tool_type,
- const std::vector<OutputFile>& outputs) {
- out_ << "build";
- path_output_.WriteFiles(out_, outputs);
-
- out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type);
- out_ << " ";
- path_output_.WriteFile(out_, source);
-
- if (!extra_deps.empty()) {
- out_ << " |";
- path_output_.WriteFiles(out_, extra_deps);
- }
-
- if (!order_only_deps.empty()) {
- out_ << " ||";
- path_output_.WriteFiles(out_, order_only_deps);
- }
- out_ << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteLinkerStuff(
- const std::vector<OutputFile>& object_files,
- const std::vector<SourceFile>& other_files,
- const OutputFile& input_dep) {
- std::vector<OutputFile> output_files;
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- target_, tool_, tool_->outputs(), &output_files);
-
- out_ << "build";
- path_output_.WriteFiles(out_, output_files);
-
- out_ << ": " << rule_prefix_
- << Toolchain::ToolTypeToName(
- target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
-
- UniqueVector<OutputFile> extra_object_files;
- UniqueVector<const Target*> linkable_deps;
- UniqueVector<const Target*> non_linkable_deps;
- GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
-
- // Object files.
- path_output_.WriteFiles(out_, object_files);
- path_output_.WriteFiles(out_, extra_object_files);
-
- // Dependencies.
- std::vector<OutputFile> implicit_deps;
- std::vector<OutputFile> solibs;
- for (const Target* cur : linkable_deps) {
- // All linkable deps should have a link output file.
- DCHECK(!cur->link_output_file().value().empty())
- << "No link output file for "
- << target_->label().GetUserVisibleName(false);
-
- if (cur->dependency_output_file().value() !=
- cur->link_output_file().value()) {
- // This is a shared library with separate link and deps files. Save for
- // later.
- implicit_deps.push_back(cur->dependency_output_file());
- solibs.push_back(cur->link_output_file());
- } else {
- // Normal case, just link to this target.
- out_ << " ";
- path_output_.WriteFile(out_, cur->link_output_file());
- }
- }
-
- const SourceFile* optional_def_file = nullptr;
- if (!other_files.empty()) {
- for (const SourceFile& src_file : other_files) {
- if (GetSourceFileType(src_file) == SOURCE_DEF) {
- optional_def_file = &src_file;
- implicit_deps.push_back(
- OutputFile(settings_->build_settings(), src_file));
- break; // Only one def file is allowed.
- }
- }
- }
-
- // Libraries specified by paths.
- const OrderedSet<LibFile>& libs = target_->all_libs();
- for (size_t i = 0; i < libs.size(); i++) {
- if (libs[i].is_source_file()) {
- implicit_deps.push_back(
- OutputFile(settings_->build_settings(), libs[i].source_file()));
- }
- }
-
- // The input dependency is only needed if there are no object files, as the
- // dependency is normally provided transitively by the source files.
- if (!input_dep.value().empty() && object_files.empty())
- implicit_deps.push_back(input_dep);
-
- // Append implicit dependencies collected above.
- if (!implicit_deps.empty()) {
- out_ << " |";
- path_output_.WriteFiles(out_, implicit_deps);
- }
-
- // Append data dependencies as order-only dependencies.
- //
- // This will include data dependencies and input dependencies (like when
- // this target depends on an action). Having the data dependencies in this
- // list ensures that the data is available at runtime when the user builds
- // this target.
- //
- // The action dependencies are not strictly necessary in this case. They
- // should also have been collected via the input deps stamp that each source
- // file has for an order-only dependency, and since this target depends on
- // the sources, there is already an implicit order-only dependency. However,
- // it's extra work to separate these out and there's no disadvantage to
- // listing them again.
- WriteOrderOnlyDependencies(non_linkable_deps);
-
- // End of the link "build" line.
- out_ << std::endl;
-
- // The remaining things go in the inner scope of the link line.
- if (target_->output_type() == Target::EXECUTABLE ||
- target_->output_type() == Target::SHARED_LIBRARY ||
- target_->output_type() == Target::LOADABLE_MODULE) {
- WriteLinkerFlags(optional_def_file);
- WriteLibs();
- } else if (target_->output_type() == Target::STATIC_LIBRARY) {
- out_ << " arflags =";
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
- GetFlagOptions(), out_);
- out_ << std::endl;
- }
- WriteOutputSubstitutions();
- WriteSolibs(solibs);
-}
-
-void NinjaBinaryTargetWriter::WriteLinkerFlags(
- const SourceFile* optional_def_file) {
- out_ << " ldflags =";
-
- // First the ldflags from the target and its config.
- RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
- GetFlagOptions(), out_);
-
- // Followed by library search paths that have been recursively pushed
- // through the dependency tree.
- const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
- if (!all_lib_dirs.empty()) {
- // Since we're passing these on the command line to the linker and not
- // to Ninja, we need to do shell escaping.
- PathOutput lib_path_output(path_output_.current_dir(),
- settings_->build_settings()->root_path_utf8(),
- ESCAPE_NINJA_COMMAND);
- for (size_t i = 0; i < all_lib_dirs.size(); i++) {
- out_ << " " << tool_->lib_dir_switch();
- lib_path_output.WriteDir(out_, all_lib_dirs[i],
- PathOutput::DIR_NO_LAST_SLASH);
- }
- }
-
- if (optional_def_file) {
- out_ << " /DEF:";
- path_output_.WriteFile(out_, *optional_def_file);
- }
-
- out_ << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteLibs() {
- out_ << " libs =";
-
- // Libraries that have been recursively pushed through the dependency tree.
- EscapeOptions lib_escape_opts;
- lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
- const OrderedSet<LibFile> all_libs = target_->all_libs();
- const std::string framework_ending(".framework");
- for (size_t i = 0; i < all_libs.size(); i++) {
- const LibFile& lib_file = all_libs[i];
- const std::string& lib_value = lib_file.value();
- if (lib_file.is_source_file()) {
- out_ << " ";
- path_output_.WriteFile(out_, lib_file.source_file());
- } else if (base::EndsWith(lib_value, framework_ending,
- base::CompareCase::INSENSITIVE_ASCII)) {
- // Special-case libraries ending in ".framework" to support Mac: Add the
- // -framework switch and don't add the extension to the output.
- out_ << " -framework ";
- EscapeStringToStream(
- out_, lib_value.substr(0, lib_value.size() - framework_ending.size()),
- lib_escape_opts);
- } else {
- out_ << " " << tool_->lib_switch();
- EscapeStringToStream(out_, lib_value, lib_escape_opts);
- }
- }
- out_ << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteOutputSubstitutions() {
- out_ << " output_extension = "
- << SubstitutionWriter::GetLinkerSubstitution(
- target_, tool_, SUBSTITUTION_OUTPUT_EXTENSION);
- out_ << std::endl;
- out_ << " output_dir = "
- << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
- SUBSTITUTION_OUTPUT_DIR);
- out_ << std::endl;
-}
-
-void NinjaBinaryTargetWriter::WriteSolibs(
- const std::vector<OutputFile>& solibs) {
- if (solibs.empty())
- return;
-
- out_ << " solibs =";
- path_output_.WriteFiles(out_, solibs);
out_ << std::endl;
+ return stamp_file;
}
void NinjaBinaryTargetWriter::WriteSourceSetStamp(
@@ -767,9 +88,9 @@ void NinjaBinaryTargetWriter::WriteSourceSetStamp(
UniqueVector<const Target*> non_linkable_deps;
GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
- // The classifier should never put extra object files in a source set:
- // any source sets that we depend on should appear in our non-linkable
- // deps instead.
+ // The classifier should never put extra object files in a source sets: any
+ // source sets that we depend on should appear in our non-linkable deps
+ // instead.
DCHECK(extra_object_files.empty());
std::vector<OutputFile> order_only_deps;
@@ -821,8 +142,8 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
// were a source set. This avoids problems with braindead tools such as
// ar which don't properly link dependent static libraries.
(target_->complete_static_lib() &&
- dep->output_type() == Target::STATIC_LIBRARY &&
- !dep->complete_static_lib())) {
+ (dep->output_type() == Target::STATIC_LIBRARY &&
+ !dep->complete_static_lib()))) {
// Source sets have their object files linked into final targets
// (shared libraries, executables, loadable modules, and complete static
// libraries). Intermediate static libraries and other source sets
@@ -830,7 +151,7 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
// set can easily get linked more than once which will cause
// multiple definition errors.
if (can_link_libs)
- AddSourceSetObjectFiles(dep, extra_object_files);
+ AddSourceSetFiles(dep, extra_object_files);
// Add the source set itself as a non-linkable dependency on the current
// target. This will make sure that anything the source set's stamp file
@@ -838,6 +159,11 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
// can be complete. Otherwise, these will be skipped since this target
// will depend only on the source set's object files.
non_linkable_deps->push_back(dep);
+ } else if (target_->output_type() == Target::RUST_LIBRARY &&
+ dep->IsLinkable()) {
+ // Rust libraries aren't final, but need to have the link lines of all
+ // transitive deps specified.
+ linkable_deps->push_back(dep);
} else if (target_->complete_static_lib() && dep->IsFinal()) {
non_linkable_deps->push_back(dep);
} else if (can_link_libs && dep->IsLinkable()) {
@@ -847,44 +173,36 @@ void NinjaBinaryTargetWriter::ClassifyDependency(
}
}
-void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies(
- const UniqueVector<const Target*>& non_linkable_deps) {
- if (!non_linkable_deps.empty()) {
- out_ << " ||";
-
- // Non-linkable targets.
- for (auto* non_linkable_dep : non_linkable_deps) {
- out_ << " ";
- path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
- }
+void NinjaBinaryTargetWriter::AddSourceSetFiles(
+ const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const {
+ // Just add all sources to the list.
+ for (const auto& source : source_set->sources()) {
+ obj_files->push_back(OutputFile(settings_->build_settings(), source));
}
}
-bool NinjaBinaryTargetWriter::CheckForDuplicateObjectFiles(
- const std::vector<OutputFile>& files) const {
- std::unordered_set<std::string> set;
- for (const auto& file : files) {
- if (!set.insert(file.value()).second) {
- Err err(
- target_->defined_from(), "Duplicate object file",
- "The target " + target_->label().GetUserVisibleName(false) +
- "\ngenerates two object files with the same name:\n " +
- file.value() +
- "\n"
- "\n"
- "It could be you accidentally have a file listed twice in the\n"
- "sources. Or, depending on how your toolchain maps sources to\n"
- "object files, two source files with the same name in different\n"
- "directories could map to the same object file.\n"
- "\n"
- "In the latter case, either rename one of the files or move one "
- "of\n"
- "the sources to a separate source_set to avoid them both being "
- "in\n"
- "the same target.");
- g_scheduler->FailWithError(err);
- return false;
- }
+void NinjaBinaryTargetWriter::WriteCompilerBuildLine(
+ const SourceFile& source,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const char* tool_name,
+ const std::vector<OutputFile>& outputs) {
+ out_ << "build";
+ path_output_.WriteFiles(out_, outputs);
+
+ out_ << ": " << rule_prefix_ << tool_name;
+ out_ << " ";
+ path_output_.WriteFile(out_, source);
+
+ if (!extra_deps.empty()) {
+ out_ << " |";
+ path_output_.WriteFiles(out_, extra_deps);
}
- return true;
-}
+
+ if (!order_only_deps.empty()) {
+ out_ << " ||";
+ path_output_.WriteFiles(out_, order_only_deps);
+ }
+ out_ << std::endl;
+} \ No newline at end of file
diff --git a/gn/tools/gn/ninja_binary_target_writer.h b/gn/tools/gn/ninja_binary_target_writer.h
index 5cb7ab5d889..3274867cad7 100644
--- a/gn/tools/gn/ninja_binary_target_writer.h
+++ b/gn/tools/gn/ninja_binary_target_writer.h
@@ -6,99 +6,30 @@
#define TOOLS_GN_NINJA_BINARY_TARGET_WRITER_H_
#include "base/macros.h"
+#include "tools/gn/c_tool.h"
#include "tools/gn/config_values.h"
#include "tools/gn/ninja_target_writer.h"
#include "tools/gn/toolchain.h"
#include "tools/gn/unique_vector.h"
struct EscapeOptions;
-class SourceFileTypeSet;
// Writes a .ninja file for a binary target type (an executable, a shared
// library, or a static library).
class NinjaBinaryTargetWriter : public NinjaTargetWriter {
public:
- class SourceFileTypeSet;
-
NinjaBinaryTargetWriter(const Target* target, std::ostream& out);
~NinjaBinaryTargetWriter() override;
void Run() override;
- private:
- typedef std::set<OutputFile> OutputFileSet;
-
- // Writes all flags for the compiler: includes, defines, cflags, etc.
- void WriteCompilerVars(const SourceFileTypeSet& used_types);
-
+ protected:
// Writes to the output stream a stamp rule for inputs, and
// returns the file to be appended to source rules that encodes the
// implicit dependencies for the current target. The returned OutputFile
// will be empty if there are no inputs.
OutputFile WriteInputsStampAndGetDep() const;
- // Writes build lines required for precompiled headers. Any generated
- // object files will be appended to the |object_files|. Any generated
- // non-object files (for instance, .gch files from a GCC toolchain, are
- // appended to |other_files|).
- //
- // input_dep is the stamp file collecting the dependencies required before
- // compiling this target. It will be empty if there are no input deps.
- void WritePCHCommands(const SourceFileTypeSet& used_types,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files);
-
- // Writes a .pch compile build line for a language type.
- void WritePCHCommand(SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- Tool::PrecompiledHeaderType header_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<OutputFile>* other_files);
-
- void WriteGCCPCHCommand(SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* gch_files);
-
- void WriteWindowsPCHCommand(SubstitutionType flag_type,
- Toolchain::ToolType tool_type,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files);
-
- // pch_deps are additional dependencies to run before the rule. They are
- // expected to abide by the naming conventions specified by GetPCHOutputFiles.
- //
- // order_only_dep are the dependencies that must be run before doing any
- // compiles.
- //
- // The files produced by the compiler will be added to two output vectors.
- void WriteSources(const std::vector<OutputFile>& pch_deps,
- const OutputFile& input_dep,
- const std::vector<OutputFile>& order_only_deps,
- std::vector<OutputFile>* object_files,
- std::vector<SourceFile>* other_files);
-
- // Writes a build line.
- void WriteCompilerBuildLine(const SourceFile& source,
- const std::vector<OutputFile>& extra_deps,
- const std::vector<OutputFile>& order_only_deps,
- Toolchain::ToolType tool_type,
- const std::vector<OutputFile>& outputs);
-
- void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
- const std::vector<SourceFile>& other_files,
- const OutputFile& input_dep);
- void WriteLinkerFlags(const SourceFile* optional_def_file);
- void WriteLibs();
- void WriteOutputSubstitutions();
- void WriteSolibs(const std::vector<OutputFile>& solibs);
-
// Writes the stamp line for a source set. These are not linked.
void WriteSourceSetStamp(const std::vector<OutputFile>& object_files);
@@ -117,24 +48,22 @@ class NinjaBinaryTargetWriter : public NinjaTargetWriter {
UniqueVector<const Target*>* linkable_deps,
UniqueVector<const Target*>* non_linkable_deps) const;
- // Writes the implicit dependencies for the link or stamp line. This is
- // the "||" and everything following it on the ninja line.
- //
- // The order-only dependencies are the non-linkable deps passed in as an
- // argument, plus the data file depdencies in the target.
- void WriteOrderOnlyDependencies(
- const UniqueVector<const Target*>& non_linkable_deps);
-
+ OutputFile WriteStampAndGetDep(const UniqueVector<const SourceFile*>& files,
+ const std::string& stamp_ext) const;
- // Checks for duplicates in the given list of output files. If any duplicates
- // are found, throws an error and return false.
- bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const;
+ void WriteCompilerBuildLine(const SourceFile& source,
+ const std::vector<OutputFile>& extra_deps,
+ const std::vector<OutputFile>& order_only_deps,
+ const char* tool_name,
+ const std::vector<OutputFile>& outputs);
- const Tool* tool_;
+ virtual void AddSourceSetFiles(const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const;
// Cached version of the prefix used for rule types for this toolchain.
std::string rule_prefix_;
+ private:
DISALLOW_COPY_AND_ASSIGN(NinjaBinaryTargetWriter);
};
diff --git a/gn/tools/gn/ninja_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_binary_target_writer_unittest.cc
index a0a7ce8fdca..7e92ad40518 100644
--- a/gn/tools/gn/ninja_binary_target_writer_unittest.cc
+++ b/gn/tools/gn/ninja_binary_target_writer_unittest.cc
@@ -4,22 +4,13 @@
#include "tools/gn/ninja_binary_target_writer.h"
-#include <memory>
-#include <sstream>
-#include <utility>
-
-#include "tools/gn/config.h"
-#include "tools/gn/ninja_target_command_util.h"
-#include "tools/gn/scheduler.h"
-#include "tools/gn/target.h"
#include "tools/gn/test_with_scheduler.h"
#include "tools/gn/test_with_scope.h"
-#include "util/build_config.h"
#include "util/test/test.h"
using NinjaBinaryTargetWriterTest = TestWithScheduler;
-TEST_F(NinjaBinaryTargetWriterTest, SourceSet) {
+TEST_F(NinjaBinaryTargetWriterTest, CSources) {
Err err;
TestWithScope setup;
@@ -32,278 +23,8 @@ TEST_F(NinjaBinaryTargetWriterTest, SourceSet) {
// dependents to link.
target.sources().push_back(SourceFile("//foo/input3.o"));
target.sources().push_back(SourceFile("//foo/input4.obj"));
- target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(target.OnResolved(&err));
-
- // Source set itself.
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter 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/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\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";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // A shared library that depends on the source set.
- Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- shlib_target.set_output_type(Target::SHARED_LIBRARY);
- shlib_target.public_deps().push_back(LabelTargetPair(&target));
- shlib_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(shlib_target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&shlib_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 = libshlib\n"
- "\n"
- "\n"
- // Ordering of the obj files here should come out in the order
- // specified, with the target's first, followed by the source set's, in
- // order.
- "build ./libshlib.so: solink obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
- "|| obj/foo/bar.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // A static library that depends on the source set (should not link it).
- Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
- stlib_target.set_output_type(Target::STATIC_LIBRARY);
- stlib_target.public_deps().push_back(LabelTargetPair(&target));
- stlib_target.SetToolchain(setup.toolchain());
- ASSERT_TRUE(stlib_target.OnResolved(&err));
-
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&stlib_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 = libstlib\n"
- "\n"
- "\n"
- // There are no sources so there are no params to alink. (In practice
- // this will probably fail in the archive tool.)
- "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
- " arflags =\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // Make the static library 'complete', which means it should be linked.
- stlib_target.set_complete_static_lib(true);
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&stlib_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 = libstlib\n"
- "\n"
- "\n"
- // Ordering of the obj files here should come out in the order
- // specified, with the target's first, followed by the source set's, in
- // order.
- "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
- "|| obj/foo/bar.stamp\n"
- " arflags =\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, EscapeDefines) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.config_values().defines().push_back("BOOL_DEF");
- target.config_values().defines().push_back("INT_DEF=123");
- target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- const char expectedSubstr[] =
-#if defined(OS_WIN)
- "defines = -DBOOL_DEF -DINT_DEF=123 \"-DSTR_DEF=\\\"ABCD-1\\\"\"";
-#else
- "defines = -DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\"ABCD-1\\\"";
-#endif
- std::string out_str = out.str();
- EXPECT_TRUE(out_str.find(out_str) != std::string::npos);
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, StaticLibrary) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.config_values().arflags().push_back("--asdf");
- ASSERT_TRUE(target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter 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/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, CompleteStaticLibrary) {
- TestWithScope setup;
- Err err;
-
- TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.config_values().arflags().push_back("--asdf");
- target.set_complete_static_lib(true);
-
- TestTarget baz(setup, "//foo:baz", Target::STATIC_LIBRARY);
- baz.sources().push_back(SourceFile("//foo/input2.cc"));
-
- target.public_deps().push_back(LabelTargetPair(&baz));
-
- ASSERT_TRUE(target.OnResolved(&err));
- ASSERT_TRUE(baz.OnResolved(&err));
-
- // A complete static library that depends on an incomplete static library
- // should link in the dependent object files as if the dependent target
- // were a source set.
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter 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/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
- "obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-
- // Make the dependent static library complete.
- baz.set_complete_static_lib(true);
-
- // Dependent complete static libraries should not be linked directly.
- {
- std::ostringstream out;
- NinjaBinaryTargetWriter 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/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
- "\n"
- "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
- "|| obj/foo/libbaz.a\n"
- " arflags = --asdf\n"
- " output_extension = \n"
- " output_dir = \n";
- std::string out_str = out.str();
- EXPECT_EQ(expected, out_str);
- }
-}
-
-// This tests that output extension and output dir overrides apply, and input
-// dependencies are applied.
-TEST_F(NinjaBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
- Err err;
- TestWithScope setup;
-
- // An action for our library to depend on.
- Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
- action.set_output_type(Target::ACTION_FOREACH);
- action.visibility().SetPublic();
- action.SetToolchain(setup.toolchain());
- ASSERT_TRUE(action.OnResolved(&err));
-
- // A shared library w/ the output_extension set to a custom value.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.set_output_extension(std::string("so.6"));
- target.set_output_dir(SourceDir("//out/Debug/foo/"));
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.public_deps().push_back(LabelTargetPair(&action));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_O);
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -318,163 +39,24 @@ TEST_F(NinjaBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
"cflags_cc =\n"
"root_out_dir = .\n"
"target_out_dir = obj/foo\n"
- "target_output_name = libshlib\n"
+ "target_output_name = bar\n"
"\n"
- "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
- " || obj/foo/action.stamp\n"
- "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
- " || obj/foo/action.stamp\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
"\n"
- "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
- // The order-only dependency here is stricly unnecessary since the
- // sources list this as an order-only dep. See discussion in the code
- // that writes this.
- "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so.6\n"
- " output_dir = foo\n";
-
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str);
}
-TEST_F(NinjaBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
- Err err;
- TestWithScope setup;
-
- SourceFile generated_file("//out/Debug/generated.cc");
-
- // An action does code generation.
- Target action(setup.settings(), Label(SourceDir("//foo/"), "generate"));
- action.set_output_type(Target::ACTION);
- action.visibility().SetPublic();
- action.SetToolchain(setup.toolchain());
- action.set_output_dir(SourceDir("//out/Debug/foo/"));
- action.action_values().outputs() =
- SubstitutionList::MakeForTest("//out/Debug/generated.cc");
- ASSERT_TRUE(action.OnResolved(&err));
-
- // A source set compiling geneated code, this target does not publicize any
- // headers.
- Target gen_obj(setup.settings(), Label(SourceDir("//foo/"), "gen_obj"));
- gen_obj.set_output_type(Target::SOURCE_SET);
- gen_obj.set_output_dir(SourceDir("//out/Debug/foo/"));
- gen_obj.sources().push_back(generated_file);
- gen_obj.visibility().SetPublic();
- gen_obj.private_deps().push_back(LabelTargetPair(&action));
- gen_obj.set_all_headers_public(false);
- gen_obj.SetToolchain(setup.toolchain());
- ASSERT_TRUE(gen_obj.OnResolved(&err));
-
- std::ostringstream obj_out;
- NinjaBinaryTargetWriter obj_writer(&gen_obj, obj_out);
- obj_writer.Run();
-
- const char obj_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = gen_obj\n"
- "\n"
- "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
- " || obj/foo/generate.stamp\n"
- "\n"
- "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/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);
-
- // A shared library depends on gen_obj, having corresponding header for
- // generated obj.
- Target gen_lib(setup.settings(), Label(SourceDir("//foo/"), "gen_lib"));
- gen_lib.set_output_type(Target::SHARED_LIBRARY);
- gen_lib.set_output_dir(SourceDir("//out/Debug/foo/"));
- gen_lib.sources().push_back(SourceFile("//foor/generated.h"));
- gen_lib.visibility().SetPublic();
- gen_lib.private_deps().push_back(LabelTargetPair(&gen_obj));
- gen_lib.SetToolchain(setup.toolchain());
- ASSERT_TRUE(gen_lib.OnResolved(&err));
-
- std::ostringstream lib_out;
- NinjaBinaryTargetWriter lib_writer(&gen_lib, lib_out);
- lib_writer.Run();
-
- const char lib_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libgen_lib\n"
- "\n"
- "\n"
- "build ./libgen_lib.so: solink obj/out/Debug/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
- " || obj/foo/gen_obj.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = foo\n";
-
- std::string lib_str = lib_out.str();
- EXPECT_EQ(lib_expected, lib_str);
-
- // An executable depends on gen_lib.
- Target executable(setup.settings(),
- Label(SourceDir("//foo/"), "final_target"));
- executable.set_output_type(Target::EXECUTABLE);
- executable.set_output_dir(SourceDir("//out/Debug/foo/"));
- executable.sources().push_back(SourceFile("//foo/main.cc"));
- executable.private_deps().push_back(LabelTargetPair(&gen_lib));
- executable.SetToolchain(setup.toolchain());
- ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
-
- std::ostringstream final_out;
- NinjaBinaryTargetWriter final_writer(&executable, final_out);
- final_writer.Run();
-
- // There is no order only dependency to action target.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = final_target\n"
- "\n"
- "build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
- "\n"
- "build ./final_target: link obj/foo/final_target.main.o"
- " ./libgen_lib.so\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = foo\n";
-
- std::string final_str = final_out.str();
- EXPECT_EQ(final_expected, final_str);
-}
-
-// Tests libs are applied.
-TEST_F(NinjaBinaryTargetWriterTest, LibsAndLibDirs) {
+TEST_F(NinjaBinaryTargetWriterTest, NoSourcesSourceSet) {
Err err;
TestWithScope setup;
- // A shared library w/ libs and lib_dirs.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.config_values().libs().push_back(LibFile(SourceFile("//foo/lib1.a")));
- target.config_values().libs().push_back(LibFile("foo"));
- target.config_values().lib_dirs().push_back(SourceDir("//foo/bar/"));
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -487,33 +69,21 @@ TEST_F(NinjaBinaryTargetWriterTest, LibsAndLibDirs) {
"include_dirs =\n"
"root_out_dir = .\n"
"target_out_dir = obj/foo\n"
- "target_output_name = libshlib\n"
+ "target_output_name = bar\n"
"\n"
"\n"
- "build ./libshlib.so: solink | ../../foo/lib1.a\n"
- " ldflags = -L../../foo/bar\n"
- " libs = ../../foo/lib1.a -lfoo\n"
- " output_extension = .so\n"
- " output_dir = \n";
-
+ "build obj/foo/bar.stamp: stamp\n";
std::string out_str = out.str();
EXPECT_EQ(expected, out_str);
}
-TEST_F(NinjaBinaryTargetWriterTest, EmptyOutputExtension) {
+TEST_F(NinjaBinaryTargetWriterTest, NoSourcesStaticLib) {
Err err;
TestWithScope setup;
- // This test is the same as OutputExtensionAndInputDeps, except that we call
- // set_output_extension("") and ensure that we get an empty one and override
- // the output prefix so that the name matches the target exactly.
- Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.set_output_prefix_override(true);
- target.set_output_extension(std::string());
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
-
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::STATIC_LIBRARY);
+ target.visibility().SetPublic();
target.SetToolchain(setup.toolchain());
ASSERT_TRUE(target.OnResolved(&err));
@@ -524,632 +94,15 @@ TEST_F(NinjaBinaryTargetWriterTest, EmptyOutputExtension) {
const char expected[] =
"defines =\n"
"include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
"root_out_dir = .\n"
"target_out_dir = obj/foo\n"
- "target_output_name = shlib\n"
+ "target_output_name = libbar\n"
"\n"
- "build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
- "build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
"\n"
- "build ./shlib: solink obj/foo/shlib.input1.o "
- "obj/foo/shlib.input2.o\n"
- " ldflags =\n"
- " libs =\n"
+ "build obj/foo/libbar.a: alink\n"
+ " arflags =\n"
" output_extension = \n"
" output_dir = \n";
-
std::string out_str = out.str();
EXPECT_EQ(expected, out_str);
}
-
-TEST_F(NinjaBinaryTargetWriterTest, SourceSetDataDeps) {
- Err err;
- TestWithScope setup;
-
- // This target is a data (runtime) dependency of the intermediate target.
- Target data(setup.settings(), Label(SourceDir("//foo/"), "data_target"));
- data.set_output_type(Target::EXECUTABLE);
- data.visibility().SetPublic();
- data.SetToolchain(setup.toolchain());
- ASSERT_TRUE(data.OnResolved(&err));
-
- // Intermediate source set target.
- Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter"));
- inter.set_output_type(Target::SOURCE_SET);
- inter.visibility().SetPublic();
- inter.data_deps().push_back(LabelTargetPair(&data));
- inter.SetToolchain(setup.toolchain());
- inter.sources().push_back(SourceFile("//foo/inter.cc"));
- ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
-
- // Write out the intermediate target.
- std::ostringstream inter_out;
- NinjaBinaryTargetWriter inter_writer(&inter, inter_out);
- inter_writer.Run();
-
- // The intermediate source set will be a stamp file that depends on the
- // object files, and will have an order-only dependency on its data dep and
- // data file.
- const char inter_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = inter\n"
- "\n"
- "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
- "\n"
- "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
- "./data_target\n";
- EXPECT_EQ(inter_expected, inter_out.str());
-
- // Final target.
- Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
- exe.set_output_type(Target::EXECUTABLE);
- exe.public_deps().push_back(LabelTargetPair(&inter));
- exe.SetToolchain(setup.toolchain());
- exe.sources().push_back(SourceFile("//foo/final.cc"));
- ASSERT_TRUE(exe.OnResolved(&err));
-
- std::ostringstream final_out;
- NinjaBinaryTargetWriter final_writer(&exe, final_out);
- final_writer.Run();
-
- // The final output depends on both object files (one from the final target,
- // one from the source set) and has an order-only dependency on the source
- // set's stamp file and the final target's data file. The source set stamp
- // dependency will create an implicit order-only dependency on the data
- // target.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = exe\n"
- "\n"
- "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
- "\n"
- "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
- "obj/foo/inter.stamp\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = \n";
- EXPECT_EQ(final_expected, final_out.str());
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
- Err err;
- TestWithScope setup;
-
- Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- shared_lib.set_output_type(Target::SHARED_LIBRARY);
- shared_lib.SetToolchain(setup.toolchain());
- shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
- shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
- ASSERT_TRUE(shared_lib.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&shared_lib, out);
- writer.Run();
-
- const char expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
- "\n"
- "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
- " ldflags = /DEF:../../foo/bar.def\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- EXPECT_EQ(expected, out.str());
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, LoadableModule) {
- Err err;
- TestWithScope setup;
-
- Target loadable_module(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- loadable_module.set_output_type(Target::LOADABLE_MODULE);
- loadable_module.visibility().SetPublic();
- loadable_module.SetToolchain(setup.toolchain());
- loadable_module.sources().push_back(SourceFile("//foo/sources.cc"));
- ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&loadable_module, out);
- writer.Run();
-
- const char loadable_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = libbar\n"
- "\n"
- "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
- "\n"
- "build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
- EXPECT_EQ(loadable_expected, out.str());
-
- // Final target.
- Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
- exe.set_output_type(Target::EXECUTABLE);
- exe.public_deps().push_back(LabelTargetPair(&loadable_module));
- exe.SetToolchain(setup.toolchain());
- exe.sources().push_back(SourceFile("//foo/final.cc"));
- ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
-
- std::ostringstream final_out;
- NinjaBinaryTargetWriter final_writer(&exe, final_out);
- final_writer.Run();
-
- // The final output depends on the loadable module so should have an
- // order-only dependency on the loadable modules's output file.
- const char final_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = exe\n"
- "\n"
- "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
- "\n"
- "build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = \n"
- " output_dir = \n";
- EXPECT_EQ(final_expected, final_out.str());
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, WinPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- TestWithScope setup;
-
- // A precompiled header toolchain.
- Settings pch_settings(setup.build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(setup.toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool.get());
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(Tool::PCH_MSVC);
- pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool.get());
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(Tool::PCH_MSVC);
- pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&no_pch_target, out);
- writer.Run();
-
- const char no_pch_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99\n"
- "cflags_cc =\n"
- "target_output_name = no_pch_target\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc\n"
- "build withpch/obj/foo/no_pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
- "withpch/obj/foo/no_pch_target.input2.o\n";
- EXPECT_EQ(no_pch_expected, out.str());
- }
-
- // This target specifies PCH.
- {
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_header("build/precompile.h");
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.cc"));
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&pch_target, out);
- writer.Run();
-
- const char pch_win_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- // It should output language-specific pch files.
- "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
- "/Yubuild/precompile.h\n"
- "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
- "/Yubuild/precompile.h\n"
- "target_output_name = pch_target\n"
- "\n"
- // Compile the precompiled source files with /Yc.
- "build withpch/obj/build/pch_target.precompile.c.o: "
- "withpch_cc ../../build/precompile.cc\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"
- " 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"
- "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"
- "\n"
- "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
- "withpch/obj/foo/pch_target.input1.o "
- "withpch/obj/foo/pch_target.input2.o "
- // The precompiled object files were added to the outputs.
- "withpch/obj/build/pch_target.precompile.c.o "
- "withpch/obj/build/pch_target.precompile.cc.o\n";
- EXPECT_EQ(pch_win_expected, out.str());
- }
-}
-
-TEST_F(NinjaBinaryTargetWriterTest, GCCPrecompiledHeaders) {
- Err err;
-
- // This setup's toolchain does not have precompiled headers defined.
- TestWithScope setup;
-
- // A precompiled header toolchain.
- Settings pch_settings(setup.build_settings(), "withpch/");
- Toolchain pch_toolchain(&pch_settings,
- Label(SourceDir("//toolchain/"), "withpch"));
- pch_settings.set_toolchain_label(pch_toolchain.label());
- pch_settings.set_default_toolchain_label(setup.toolchain()->label());
-
- // Declare a C++ compiler that supports PCH.
- std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
- TestWithScope::SetCommandForTool(
- "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cxx_tool.get());
- cxx_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cxx_tool->set_precompiled_header_type(Tool::PCH_GCC);
- pch_toolchain.SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
- pch_toolchain.ToolchainSetupComplete();
-
- // Add a C compiler as well.
- std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
- TestWithScope::SetCommandForTool(
- "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
- "-o {{output}}",
- cc_tool.get());
- cc_tool->set_outputs(SubstitutionList::MakeForTest(
- "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- cc_tool->set_precompiled_header_type(Tool::PCH_GCC);
- pch_toolchain.SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
- pch_toolchain.ToolchainSetupComplete();
-
- // This target doesn't specify precompiled headers.
- {
- Target no_pch_target(&pch_settings,
- Label(SourceDir("//foo/"), "no_pch_target"));
- no_pch_target.set_output_type(Target::SOURCE_SET);
- no_pch_target.visibility().SetPublic();
- no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- no_pch_target.config_values().cflags_c().push_back("-std=c99");
- no_pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(no_pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&no_pch_target, out);
- writer.Run();
-
- const char no_pch_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99\n"
- "cflags_cc =\n"
- "target_output_name = no_pch_target\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.input1.o: "
- "withpch_cxx ../../foo/input1.cc\n"
- "build withpch/obj/foo/no_pch_target.input2.o: "
- "withpch_cc ../../foo/input2.c\n"
- "\n"
- "build withpch/obj/foo/no_pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
- "withpch/obj/foo/no_pch_target.input2.o\n";
- EXPECT_EQ(no_pch_expected, out.str());
- }
-
- // This target specifies PCH.
- {
- Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
- pch_target.config_values().set_precompiled_source(
- SourceFile("//build/precompile.h"));
- pch_target.config_values().cflags_c().push_back("-std=c99");
- pch_target.set_output_type(Target::SOURCE_SET);
- pch_target.visibility().SetPublic();
- pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
- pch_target.sources().push_back(SourceFile("//foo/input2.c"));
- pch_target.SetToolchain(&pch_toolchain);
- ASSERT_TRUE(pch_target.OnResolved(&err));
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&pch_target, out);
- writer.Run();
-
- const char pch_gcc_expected[] =
- "defines =\n"
- "include_dirs =\n"
- "cflags =\n"
- "cflags_c = -std=c99 "
- "-include withpch/obj/build/pch_target.precompile.h-c\n"
- "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
- "target_output_name = pch_target\n"
- "\n"
- // Compile the precompiled sources with -x <lang>.
- "build withpch/obj/build/pch_target.precompile.h-c.gch: "
- "withpch_cc ../../build/precompile.h\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"
- " 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"
- "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"
- "\n"
- "build withpch/obj/foo/pch_target.stamp: "
- "withpch_stamp withpch/obj/foo/pch_target.input1.o "
- "withpch/obj/foo/pch_target.input2.o\n";
- EXPECT_EQ(pch_gcc_expected, out.str());
- }
-}
-
-// Should throw an error with the scheduler if a duplicate object file exists.
-// This is dependent on the toolchain's object file mapping.
-TEST_F(NinjaBinaryTargetWriterTest, DupeObjFileError) {
- TestWithScope setup;
- TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
- target.sources().push_back(SourceFile("//a.cc"));
- target.sources().push_back(SourceFile("//a.cc"));
-
- EXPECT_FALSE(scheduler().is_failed());
-
- scheduler().SuppressOutputForTesting(true);
-
- std::ostringstream out;
- NinjaBinaryTargetWriter writer(&target, out);
- writer.Run();
-
- scheduler().SuppressOutputForTesting(false);
-
- // Should have issued an error.
- EXPECT_TRUE(scheduler().is_failed());
-}
-
-// This tests that output extension and output dir overrides apply, and input
-// dependencies are applied.
-TEST_F(NinjaBinaryTargetWriterTest, InputFiles) {
- Err err;
- TestWithScope setup;
-
- // This target has one input.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
- 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"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
- " | ../../foo/input.data\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | ../../foo/input.data\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has one input but no source files.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SHARED_LIBRARY);
- target.visibility().SetPublic();
- target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
- 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 = libbar\n"
- "\n"
- "\n"
- "build ./libbar.so: solink | ../../foo/input.data\n"
- " ldflags =\n"
- " libs =\n"
- " output_extension = .so\n"
- " output_dir = \n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has multiple inputs.
- {
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
- target.config_values().inputs().push_back(SourceFile("//foo/input2.data"));
- 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"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.inputs.stamp: stamp"
- " ../../foo/input1.data ../../foo/input2.data\n"
- "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-
- // This target has one input itself, one from an immediate config, and one
- // from a config tacked on to said config.
- {
- Config far_config(setup.settings(), Label(SourceDir("//foo/"), "qux"));
- far_config.own_values().inputs().push_back(SourceFile("//foo/input3.data"));
- ASSERT_TRUE(far_config.OnResolved(&err));
-
- Config config(setup.settings(), Label(SourceDir("//foo/"), "baz"));
- config.own_values().inputs().push_back(SourceFile("//foo/input2.data"));
- config.configs().push_back(LabelConfigPair(&far_config));
- ASSERT_TRUE(config.OnResolved(&err));
-
- Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
- target.set_output_type(Target::SOURCE_SET);
- target.visibility().SetPublic();
- target.sources().push_back(SourceFile("//foo/input1.cc"));
- target.sources().push_back(SourceFile("//foo/input2.cc"));
- target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
- target.configs().push_back(LabelConfigPair(&config));
- 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"
- "cflags =\n"
- "cflags_cc =\n"
- "root_out_dir = .\n"
- "target_out_dir = obj/foo\n"
- "target_output_name = bar\n"
- "\n"
- "build obj/foo/bar.inputs.stamp: stamp"
- " ../../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"
- "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
- " | obj/foo/bar.inputs.stamp\n"
- "\n"
- "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
- "obj/foo/bar.input2.o\n";
-
- EXPECT_EQ(expected, out.str());
- }
-}
diff --git a/gn/tools/gn/ninja_build_writer.cc b/gn/tools/gn/ninja_build_writer.cc
index f13a8b98733..3127c9a31cd 100644
--- a/gn/tools/gn/ninja_build_writer.cc
+++ b/gn/tools/gn/ninja_build_writer.cc
@@ -306,10 +306,16 @@ void NinjaBuildWriter::WriteNinjaRules() {
const base::FilePath build_path =
build_settings_->build_dir().Resolve(build_settings_->root_path());
+
+ EscapeOptions depfile_escape;
+ depfile_escape.mode = ESCAPE_DEPFILE;
for (const auto& other_file : fileset) {
const base::FilePath file =
MakeAbsoluteFilePathRelativeIfPossible(build_path, other_file);
- dep_out_ << " " << FilePathToUTF8(file.NormalizePathSeparatorsTo('/'));
+ dep_out_ << " ";
+ EscapeStringToStream(dep_out_,
+ FilePathToUTF8(file.NormalizePathSeparatorsTo('/')),
+ depfile_escape);
}
out_ << std::endl;
@@ -319,11 +325,9 @@ void NinjaBuildWriter::WriteAllPools() {
// Compute the pools referenced by all tools of all used toolchains.
std::unordered_set<const Pool*> used_pools;
for (const auto& pair : used_toolchains_) {
- for (int j = Toolchain::TYPE_NONE + 1; j < Toolchain::TYPE_NUMTYPES; j++) {
- Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(j);
- const Tool* tool = pair.second->GetTool(tool_type);
- if (tool && tool->pool().ptr)
- used_pools.insert(tool->pool().ptr);
+ for (const auto& tool : pair.second->tools()) {
+ if (tool.second->pool().ptr)
+ used_pools.insert(tool.second->pool().ptr);
}
}
diff --git a/gn/tools/gn/ninja_build_writer_unittest.cc b/gn/tools/gn/ninja_build_writer_unittest.cc
index 50530b199d6..7b5e82a1c18 100644
--- a/gn/tools/gn/ninja_build_writer_unittest.cc
+++ b/gn/tools/gn/ninja_build_writer_unittest.cc
@@ -96,7 +96,7 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
Label(SourceDir("//other/"), "depth_pool", other_toolchain_label.dir(),
other_toolchain_label.name()));
other_regular_pool.set_depth(42);
- other_toolchain.GetTool(Toolchain::TYPE_LINK)
+ other_toolchain.GetTool(CTool::kCToolLink)
->set_pool(LabelPtrPair<Pool>(&other_regular_pool));
// Make another target that uses its own pool
@@ -122,7 +122,7 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
setup.toolchain()->label().dir(),
setup.toolchain()->label().name()));
console_pool.set_depth(1);
- other_toolchain.GetTool(Toolchain::TYPE_STAMP)
+ other_toolchain.GetTool(GeneralTool::kGeneralToolStamp)
->set_pool(LabelPtrPair<Pool>(&console_pool));
// Settings to go with the other toolchain.
@@ -184,6 +184,33 @@ TEST_F(NinjaBuildWriterTest, TwoTargets) {
EXPECT_EQ(std::string::npos, out_str.find("pool console"));
}
+TEST_F(NinjaBuildWriterTest, SpaceInDepfile) {
+ TestWithScope setup;
+ Err err;
+
+ // Setup sets the default root dir to ".".
+ base::FilePath root(FILE_PATH_LITERAL("."));
+ base::FilePath root_realpath = base::MakeAbsoluteFilePath(root);
+ setup.build_settings()->SetRootPath(root_realpath);
+
+ // Cannot use MakeAbsoluteFilePath for non-existed paths
+ base::FilePath dependency =
+ root_realpath.Append(FILE_PATH_LITERAL("path with space/BUILD.gn"));
+ g_scheduler->AddGenDependency(dependency);
+
+ std::unordered_map<const Settings*, const Toolchain*> used_toolchains;
+ used_toolchains[setup.settings()] = setup.toolchain();
+ std::vector<const Target*> targets;
+ std::ostringstream 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));
+
+ EXPECT_EQ(depfile_out.str(),
+ "build.ninja: ../../path\\ with\\ space/BUILD.gn");
+}
+
TEST_F(NinjaBuildWriterTest, DuplicateOutputs) {
TestWithScope setup;
Err err;
diff --git a/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc b/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
index 5b1a4bae48a..f2ec6c548d6 100644
--- a/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
+++ b/gn/tools/gn/ninja_bundle_data_target_writer_unittest.cc
@@ -22,6 +22,8 @@ TEST(NinjaBundleDataTargetWriter, Run) {
bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/Contents.json"));
bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
@@ -44,6 +46,7 @@ TEST(NinjaBundleDataTargetWriter, Run) {
"../../foo/input1.txt "
"../../foo/input2.txt "
"../../foo/Foo.xcassets/Contents.json "
+ "../../foo/Foo.xcassets/foo.colorset/Contents.json "
"../../foo/Foo.xcassets/foo.imageset/Contents.json "
"../../foo/Foo.xcassets/foo.imageset/FooIcon-29.png "
"../../foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png "
diff --git a/gn/tools/gn/ninja_c_binary_target_writer.cc b/gn/tools/gn/ninja_c_binary_target_writer.cc
new file mode 100644
index 00000000000..04698b58b93
--- /dev/null
+++ b/gn/tools/gn/ninja_c_binary_target_writer.cc
@@ -0,0 +1,714 @@
+// Copyright 2019 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 "tools/gn/ninja_c_binary_target_writer.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include <cstring>
+#include <set>
+#include <sstream>
+#include <unordered_set>
+
+#include "base/strings/string_util.h"
+#include "tools/gn/c_substitution_type.h"
+#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/deps_iterator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/escape.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
+#include "tools/gn/ninja_target_command_util.h"
+#include "tools/gn/ninja_utils.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/string_utils.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+// Returns the proper escape options for writing compiler and linker flags.
+EscapeOptions GetFlagOptions() {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ return opts;
+}
+
+// Returns the language-specific lang recognized by gcc’s -x flag for
+// precompiled header files.
+const char* GetPCHLangForToolType(const char* name) {
+ if (name == CTool::kCToolCc)
+ return "c-header";
+ if (name == CTool::kCToolCxx)
+ return "c++-header";
+ if (name == CTool::kCToolObjC)
+ return "objective-c-header";
+ if (name == CTool::kCToolObjCxx)
+ return "objective-c++-header";
+ NOTREACHED() << "Not a valid PCH tool type: " << name;
+ return "";
+}
+
+} // namespace
+
+NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaBinaryTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {}
+
+NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default;
+
+void NinjaCBinaryTargetWriter::Run() {
+ WriteCompilerVars();
+
+ OutputFile input_dep = WriteInputsStampAndGetDep();
+
+ // The input dependencies will be an order-only dependency. This will cause
+ // Ninja to make sure the inputs are up to date before compiling this source,
+ // but changes in the inputs deps won't cause the file to be recompiled.
+ //
+ // This is important to prevent changes in unrelated actions that are
+ // upstream of this target from causing everything to be recompiled.
+ //
+ // Why can we get away with this rather than using implicit deps ("|", which
+ // will force rebuilds when the inputs change)? For source code, the
+ // computed dependencies of all headers will be computed by the compiler,
+ // which will cause source rebuilds if any "real" upstream dependencies
+ // change.
+ //
+ // If a .cc file is generated by an input dependency, Ninja will see the
+ // input to the build rule doesn't exist, and that it is an output from a
+ // previous step, and build the previous step first. This is a "real"
+ // dependency and doesn't need | or || to express.
+ //
+ // The only case where this rule matters is for the first build where no .d
+ // files exist, and Ninja doesn't know what that source file depends on. In
+ // this case it's sufficient to ensure that the upstream dependencies are
+ // built first. This is exactly what Ninja's order-only dependencies
+ // expresses.
+ //
+ // The order only deps are referenced by each source file compile,
+ // but also by PCH compiles. The latter are annoying to count, so omit
+ // them here. This means that binary targets with a single source file
+ // that also use PCH files won't have a stamp file even though having
+ // one would make output ninja file size a bit lower. That's ok, binary
+ // targets with a single source are rare.
+ size_t num_stamp_uses = target_->sources().size();
+ std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+
+ // For GCC builds, the .gch files are not object files, but still need to be
+ // added as explicit dependencies below. The .gch output files are placed in
+ // |pch_other_files|. This is to prevent linking against them.
+ std::vector<OutputFile> pch_obj_files;
+ std::vector<OutputFile> pch_other_files;
+ WritePCHCommands(input_dep, order_only_deps, &pch_obj_files,
+ &pch_other_files);
+ std::vector<OutputFile>* pch_files =
+ !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files;
+
+ // Treat all pch output files as explicit dependencies of all
+ // compiles that support them. Some notes:
+ //
+ // - On Windows, the .pch file is the input to the compile, not the
+ // precompiled header's corresponding object file that we're using here.
+ // But Ninja's depslog doesn't support multiple outputs from the
+ // precompiled header compile step (it outputs both the .pch file and a
+ // corresponding .obj file). So we consistently list the .obj file and the
+ // .pch file we really need comes along with it.
+ //
+ // - GCC .gch files are not object files, therefore they are not added to the
+ // object file list.
+ std::vector<OutputFile> obj_files;
+ std::vector<SourceFile> other_files;
+ WriteSources(*pch_files, input_dep, order_only_deps, &obj_files,
+ &other_files);
+
+ // Link all MSVC pch object files. The vector will be empty on GCC toolchains.
+ obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end());
+ if (!CheckForDuplicateObjectFiles(obj_files))
+ return;
+
+ if (target_->output_type() == Target::SOURCE_SET) {
+ WriteSourceSetStamp(obj_files);
+#ifndef NDEBUG
+ // Verify that the function that separately computes a source set's object
+ // files match the object files just computed.
+ UniqueVector<OutputFile> computed_obj;
+ AddSourceSetFiles(target_, &computed_obj);
+ DCHECK_EQ(obj_files.size(), computed_obj.size());
+ for (const auto& obj : obj_files)
+ DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj));
+#endif
+ } else {
+ WriteLinkerStuff(obj_files, other_files, input_dep);
+ }
+}
+
+void NinjaCBinaryTargetWriter::WriteCompilerVars() {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
+ // Defines.
+ if (subst.used.count(&CSubstitutionDefines)) {
+ out_ << CSubstitutionDefines.ninja_name << " =";
+ RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines,
+ DefineWriter(), 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>(
+ target_, &ConfigValues::include_dirs,
+ IncludeWriter(include_path_output), out_);
+ out_ << std::endl;
+ }
+
+ 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(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)) {
+ WriteOneFlag(target_, &CSubstitutionCFlags, false, Tool::kToolNone,
+ &ConfigValues::cflags, opts, path_output_, out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_C)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsC, has_precompiled_headers,
+ CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_,
+ out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsCc, has_precompiled_headers,
+ CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_,
+ out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_M)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsObjC, has_precompiled_headers,
+ CTool::kCToolObjC, &ConfigValues::cflags_objc, opts,
+ path_output_, out_);
+ }
+ if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ WriteOneFlag(target_, &CSubstitutionCFlagsObjCc, has_precompiled_headers,
+ CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts,
+ path_output_, out_);
+ }
+
+ WriteSharedVars(subst);
+}
+
+void NinjaCBinaryTargetWriter::WritePCHCommands(
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files) {
+ if (!target_->config_values().has_precompiled_headers())
+ return;
+
+ const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc);
+ if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE &&
+ target_->source_types_used().Get(SourceFile::SOURCE_C)) {
+ WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc,
+ tool_c->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+ const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx);
+ if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE &&
+ target_->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+ WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx,
+ tool_cxx->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+
+ const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC);
+ if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC &&
+ target_->source_types_used().Get(SourceFile::SOURCE_M)) {
+ WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC,
+ tool_objc->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+
+ const CTool* tool_objcxx =
+ target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+ if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC &&
+ target_->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx,
+ tool_objcxx->precompiled_header_type(), input_dep,
+ order_only_deps, object_files, other_files);
+ }
+}
+
+void NinjaCBinaryTargetWriter::WritePCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ CTool::PrecompiledHeaderType header_type,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files) {
+ switch (header_type) {
+ case CTool::PCH_MSVC:
+ WriteWindowsPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
+ object_files);
+ break;
+ case CTool::PCH_GCC:
+ WriteGCCPCHCommand(flag_type, tool_name, input_dep, order_only_deps,
+ other_files);
+ break;
+ case CTool::PCH_NONE:
+ NOTREACHED() << "Cannot write a PCH command with no PCH header type";
+ break;
+ }
+}
+
+void NinjaCBinaryTargetWriter::WriteGCCPCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* gch_files) {
+ // Compute the pch output file (it will be language-specific).
+ std::vector<OutputFile> outputs;
+ GetPCHOutputFiles(target_, tool_name, &outputs);
+ if (outputs.empty())
+ return;
+
+ gch_files->insert(gch_files->end(), outputs.begin(), outputs.end());
+
+ std::vector<OutputFile> extra_deps;
+ if (!input_dep.value().empty())
+ extra_deps.push_back(input_dep);
+
+ // Build line to compile the file.
+ WriteCompilerBuildLine(target_->config_values().precompiled_source(),
+ extra_deps, order_only_deps, tool_name, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << flag_type->ninja_name << " =";
+
+ // Each substitution flag is overwritten in the target rule to replace the
+ // implicitly generated -include flag with the -x <header lang> flag required
+ // for .gch targets.
+ EscapeOptions opts = GetFlagOptions();
+ if (tool_name == CTool::kCToolCc) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts,
+ out_);
+ } else if (tool_name == CTool::kCToolCxx) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc,
+ opts, out_);
+ } else if (tool_name == CTool::kCToolObjC) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc,
+ opts, out_);
+ } else if (tool_name == CTool::kCToolObjCxx) {
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc,
+ opts, out_);
+ }
+
+ // Append the command to specify the language of the .gch file.
+ out_ << " -x " << GetPCHLangForToolType(tool_name);
+
+ // Write two blank lines to help separate the PCH build lines from the
+ // regular source build lines.
+ out_ << std::endl << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand(
+ const Substitution* flag_type,
+ const char* tool_name,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files) {
+ // Compute the pch output file (it will be language-specific).
+ std::vector<OutputFile> outputs;
+ GetPCHOutputFiles(target_, tool_name, &outputs);
+ if (outputs.empty())
+ return;
+
+ object_files->insert(object_files->end(), outputs.begin(), outputs.end());
+
+ std::vector<OutputFile> extra_deps;
+ if (!input_dep.value().empty())
+ extra_deps.push_back(input_dep);
+
+ // Build line to compile the file.
+ WriteCompilerBuildLine(target_->config_values().precompiled_source(),
+ extra_deps, order_only_deps, tool_name, outputs);
+
+ // This build line needs a custom language-specific flags value. Rule-specific
+ // variables are just indented underneath the rule line.
+ out_ << " " << flag_type->ninja_name << " =";
+
+ // Append the command to generate the .pch file.
+ // This adds the value to the existing flag instead of overwriting it.
+ out_ << " ${" << flag_type->ninja_name << "}";
+ out_ << " /Yc" << target_->config_values().precompiled_header();
+
+ // Write two blank lines to help separate the PCH build lines from the
+ // regular source build lines.
+ out_ << std::endl << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteSources(
+ const std::vector<OutputFile>& pch_deps,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<SourceFile>* other_files) {
+ object_files->reserve(object_files->size() + target_->sources().size());
+
+ std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
+ std::vector<OutputFile> deps;
+ for (const auto& source : target_->sources()) {
+ // Clear the vector but maintain the max capacity to prevent reallocations.
+ deps.resize(0);
+ const char* tool_name = Tool::kToolNone;
+ if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) {
+ if (source.type() == SourceFile::SOURCE_DEF)
+ other_files->push_back(source);
+ continue; // No output for this source.
+ }
+
+ if (!input_dep.value().empty())
+ deps.push_back(input_dep);
+
+ if (tool_name != Tool::kToolNone) {
+ // Only include PCH deps that correspond to the tool type, for instance,
+ // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep
+ // for the output of a C tool type.
+ //
+ // This makes the assumption that pch_deps only contains pch output files
+ // with the naming scheme specified in GetWindowsPCHObjectExtension or
+ // GetGCCPCHOutputExtension.
+ const CTool* tool = target_->toolchain()->GetToolAsC(tool_name);
+ if (tool->precompiled_header_type() != CTool::PCH_NONE) {
+ for (const auto& dep : pch_deps) {
+ const std::string& output_value = dep.value();
+ size_t extension_offset = FindExtensionOffset(output_value);
+ if (extension_offset == std::string::npos)
+ continue;
+ std::string output_extension;
+ if (tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ output_extension = GetWindowsPCHObjectExtension(
+ tool_name, output_value.substr(extension_offset - 1));
+ } else if (tool->precompiled_header_type() == CTool::PCH_GCC) {
+ output_extension = GetGCCPCHOutputExtension(tool_name);
+ }
+ if (output_value.compare(
+ output_value.size() - output_extension.size(),
+ output_extension.size(), output_extension) == 0) {
+ deps.push_back(dep);
+ }
+ }
+ }
+ WriteCompilerBuildLine(source, deps, order_only_deps, tool_name,
+ tool_outputs);
+ }
+
+ // It's theoretically possible for a compiler to produce more than one
+ // output, but we'll only link to the first output.
+ object_files->push_back(tool_outputs[0]);
+ }
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteLinkerStuff(
+ const std::vector<OutputFile>& object_files,
+ const std::vector<SourceFile>& other_files,
+ const OutputFile& input_dep) {
+ std::vector<OutputFile> output_files;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool_, tool_->outputs(), &output_files);
+
+ out_ << "build";
+ path_output_.WriteFiles(out_, output_files);
+
+ out_ << ": " << rule_prefix_
+ << Tool::GetToolTypeForTargetFinalOutput(target_);
+
+ UniqueVector<OutputFile> extra_object_files;
+ UniqueVector<const Target*> linkable_deps;
+ UniqueVector<const Target*> non_linkable_deps;
+ GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
+
+ // Object files.
+ path_output_.WriteFiles(out_, object_files);
+ path_output_.WriteFiles(out_, extra_object_files);
+
+ // Dependencies.
+ std::vector<OutputFile> implicit_deps;
+ std::vector<OutputFile> solibs;
+ for (const Target* cur : linkable_deps) {
+ // All linkable deps should have a link output file.
+ DCHECK(!cur->link_output_file().value().empty())
+ << "No link output file for "
+ << target_->label().GetUserVisibleName(false);
+
+ if (cur->dependency_output_file().value() !=
+ cur->link_output_file().value()) {
+ // This is a shared library with separate link and deps files. Save for
+ // later.
+ implicit_deps.push_back(cur->dependency_output_file());
+ solibs.push_back(cur->link_output_file());
+ } else {
+ // Normal case, just link to this target.
+ out_ << " ";
+ path_output_.WriteFile(out_, cur->link_output_file());
+ }
+ }
+
+ const SourceFile* optional_def_file = nullptr;
+ if (!other_files.empty()) {
+ for (const SourceFile& src_file : other_files) {
+ if (src_file.type() == SourceFile::SOURCE_DEF) {
+ optional_def_file = &src_file;
+ implicit_deps.push_back(
+ OutputFile(settings_->build_settings(), src_file));
+ break; // Only one def file is allowed.
+ }
+ }
+ }
+
+ // Libraries specified by paths.
+ const OrderedSet<LibFile>& libs = target_->all_libs();
+ for (size_t i = 0; i < libs.size(); i++) {
+ if (libs[i].is_source_file()) {
+ implicit_deps.push_back(
+ OutputFile(settings_->build_settings(), libs[i].source_file()));
+ }
+ }
+
+ // The input dependency is only needed if there are no object files, as the
+ // dependency is normally provided transitively by the source files.
+ if (!input_dep.value().empty() && object_files.empty())
+ implicit_deps.push_back(input_dep);
+
+ // Append implicit dependencies collected above.
+ if (!implicit_deps.empty()) {
+ out_ << " |";
+ path_output_.WriteFiles(out_, implicit_deps);
+ }
+
+ // Append data dependencies as order-only dependencies.
+ //
+ // This will include data dependencies and input dependencies (like when
+ // this target depends on an action). Having the data dependencies in this
+ // list ensures that the data is available at runtime when the user builds
+ // this target.
+ //
+ // The action dependencies are not strictly necessary in this case. They
+ // should also have been collected via the input deps stamp that each source
+ // file has for an order-only dependency, and since this target depends on
+ // the sources, there is already an implicit order-only dependency. However,
+ // it's extra work to separate these out and there's no disadvantage to
+ // listing them again.
+ WriteOrderOnlyDependencies(non_linkable_deps);
+
+ // End of the link "build" line.
+ out_ << std::endl;
+
+ // The remaining things go in the inner scope of the link line.
+ if (target_->output_type() == Target::EXECUTABLE ||
+ target_->output_type() == Target::SHARED_LIBRARY ||
+ target_->output_type() == Target::LOADABLE_MODULE) {
+ WriteLinkerFlags(optional_def_file);
+ WriteLibs();
+ } else if (target_->output_type() == Target::STATIC_LIBRARY) {
+ out_ << " arflags =";
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags,
+ GetFlagOptions(), out_);
+ out_ << std::endl;
+ }
+ WriteOutputSubstitutions();
+ WriteSolibs(solibs);
+}
+
+void NinjaCBinaryTargetWriter::WriteLinkerFlags(
+ const SourceFile* optional_def_file) {
+ out_ << " ldflags =";
+
+ // First the ldflags from the target and its config.
+ RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
+ GetFlagOptions(), out_);
+
+ // Followed by library search paths that have been recursively pushed
+ // through the dependency tree.
+ const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
+ if (!all_lib_dirs.empty()) {
+ // Since we're passing these on the command line to the linker and not
+ // to Ninja, we need to do shell escaping.
+ PathOutput lib_path_output(path_output_.current_dir(),
+ settings_->build_settings()->root_path_utf8(),
+ ESCAPE_NINJA_COMMAND);
+ for (size_t i = 0; i < all_lib_dirs.size(); i++) {
+ out_ << " " << tool_->lib_dir_switch();
+ lib_path_output.WriteDir(out_, all_lib_dirs[i],
+ PathOutput::DIR_NO_LAST_SLASH);
+ }
+ }
+
+ if (optional_def_file) {
+ out_ << " /DEF:";
+ path_output_.WriteFile(out_, *optional_def_file);
+ }
+
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteLibs() {
+ out_ << " libs =";
+
+ // Libraries that have been recursively pushed through the dependency tree.
+ EscapeOptions lib_escape_opts;
+ lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
+ const OrderedSet<LibFile> all_libs = target_->all_libs();
+ const std::string framework_ending(".framework");
+ for (size_t i = 0; i < all_libs.size(); i++) {
+ const LibFile& lib_file = all_libs[i];
+ const std::string& lib_value = lib_file.value();
+ if (lib_file.is_source_file()) {
+ out_ << " ";
+ path_output_.WriteFile(out_, lib_file.source_file());
+ } else if (base::EndsWith(lib_value, framework_ending,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ // Special-case libraries ending in ".framework" to support Mac: Add the
+ // -framework switch and don't add the extension to the output.
+ out_ << " -framework ";
+ EscapeStringToStream(
+ out_, lib_value.substr(0, lib_value.size() - framework_ending.size()),
+ lib_escape_opts);
+ } else {
+ out_ << " " << tool_->lib_switch();
+ EscapeStringToStream(out_, lib_value, lib_escape_opts);
+ }
+ }
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() {
+ out_ << " output_extension = "
+ << SubstitutionWriter::GetLinkerSubstitution(
+ target_, tool_, &CSubstitutionOutputExtension);
+ out_ << std::endl;
+ out_ << " output_dir = "
+ << SubstitutionWriter::GetLinkerSubstitution(target_, tool_,
+ &SubstitutionOutputDir);
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteSolibs(
+ const std::vector<OutputFile>& solibs) {
+ if (solibs.empty())
+ return;
+
+ out_ << " solibs =";
+ path_output_.WriteFiles(out_, solibs);
+ out_ << std::endl;
+}
+
+void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies(
+ const UniqueVector<const Target*>& non_linkable_deps) {
+ if (!non_linkable_deps.empty()) {
+ out_ << " ||";
+
+ // Non-linkable targets.
+ for (auto* non_linkable_dep : non_linkable_deps) {
+ out_ << " ";
+ path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file());
+ }
+ }
+}
+
+bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles(
+ const std::vector<OutputFile>& files) const {
+ std::unordered_set<std::string> set;
+ for (const auto& file : files) {
+ if (!set.insert(file.value()).second) {
+ Err err(
+ target_->defined_from(), "Duplicate object file",
+ "The target " + target_->label().GetUserVisibleName(false) +
+ "\ngenerates two object files with the same name:\n " +
+ file.value() +
+ "\n"
+ "\n"
+ "It could be you accidentally have a file listed twice in the\n"
+ "sources. Or, depending on how your toolchain maps sources to\n"
+ "object files, two source files with the same name in different\n"
+ "directories could map to the same object file.\n"
+ "\n"
+ "In the latter case, either rename one of the files or move one "
+ "of\n"
+ "the sources to a separate source_set to avoid them both being "
+ "in\n"
+ "the same target.");
+ g_scheduler->FailWithError(err);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Appends the object files generated by the given source set to the given
+// output vector.
+void NinjaCBinaryTargetWriter::AddSourceSetFiles(
+ const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const {
+ std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
+
+ // Compute object files for all sources. Only link the first output from
+ // the tool if there are more than one.
+ for (const auto& source : source_set->sources()) {
+ const char* tool_name = Tool::kToolNone;
+ if (source_set->GetOutputFilesForSource(source, &tool_name, &tool_outputs))
+ obj_files->push_back(tool_outputs[0]);
+ }
+
+ // Add MSVC precompiled header object files. GCC .gch files are not object
+ // files so they are omitted.
+ if (source_set->config_values().has_precompiled_headers()) {
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_C)) {
+ const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCc);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolCc, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_CPP)) {
+ const CTool* tool = source_set->toolchain()->GetToolAsC(CTool::kCToolCxx);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolCxx, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_M)) {
+ const CTool* tool =
+ source_set->toolchain()->GetToolAsC(CTool::kCToolObjC);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolObjC, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ if (source_set->source_types_used().Get(SourceFile::SOURCE_MM)) {
+ const CTool* tool =
+ source_set->toolchain()->GetToolAsC(CTool::kCToolObjCxx);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
+ GetPCHOutputFiles(source_set, CTool::kCToolObjCxx, &tool_outputs);
+ obj_files->Append(tool_outputs.begin(), tool_outputs.end());
+ }
+ }
+ }
+}
diff --git a/gn/tools/gn/ninja_c_binary_target_writer.h b/gn/tools/gn/ninja_c_binary_target_writer.h
new file mode 100644
index 00000000000..8dacf8eea27
--- /dev/null
+++ b/gn/tools/gn/ninja_c_binary_target_writer.h
@@ -0,0 +1,107 @@
+// Copyright 2019 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_NINJA_C_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "tools/gn/config_values.h"
+#include "tools/gn/ninja_binary_target_writer.h"
+#include "tools/gn/toolchain.h"
+#include "tools/gn/unique_vector.h"
+
+struct EscapeOptions;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaCBinaryTargetWriter : public NinjaBinaryTargetWriter {
+ public:
+ NinjaCBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaCBinaryTargetWriter() override;
+
+ void Run() override;
+
+ protected:
+ // Adds source_set files to the list of object files.
+ void AddSourceSetFiles(const Target* source_set,
+ UniqueVector<OutputFile>* obj_files) const override;
+
+ private:
+ typedef std::set<OutputFile> OutputFileSet;
+
+ // Writes all flags for the compiler: includes, defines, cflags, etc.
+ void WriteCompilerVars();
+
+ // Writes build lines required for precompiled headers. Any generated
+ // object files will be appended to the |object_files|. Any generated
+ // non-object files (for instance, .gch files from a GCC toolchain, are
+ // appended to |other_files|).
+ //
+ // input_dep is the stamp file collecting the dependencies required before
+ // compiling this target. It will be empty if there are no input deps.
+ void WritePCHCommands(const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files);
+
+ // Writes a .pch compile build line for a language type.
+ void WritePCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ CTool::PrecompiledHeaderType header_type,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<OutputFile>* other_files);
+
+ void WriteGCCPCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* gch_files);
+
+ void WriteWindowsPCHCommand(const Substitution* flag_type,
+ const char* tool_name,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files);
+
+ // pch_deps are additional dependencies to run before the rule. They are
+ // expected to abide by the naming conventions specified by GetPCHOutputFiles.
+ //
+ // order_only_dep are the dependencies that must be run before doing any
+ // compiles.
+ //
+ // The files produced by the compiler will be added to two output vectors.
+ void WriteSources(const std::vector<OutputFile>& pch_deps,
+ const OutputFile& input_dep,
+ const std::vector<OutputFile>& order_only_deps,
+ std::vector<OutputFile>* object_files,
+ std::vector<SourceFile>* other_files);
+
+ void WriteLinkerStuff(const std::vector<OutputFile>& object_files,
+ const std::vector<SourceFile>& other_files,
+ const OutputFile& input_dep);
+ void WriteLinkerFlags(const SourceFile* optional_def_file);
+ void WriteLibs();
+ void WriteOutputSubstitutions();
+ void WriteSolibs(const std::vector<OutputFile>& solibs);
+
+ // Writes the implicit dependencies for the link or stamp line. This is
+ // the "||" and everything following it on the ninja line.
+ //
+ // The order-only dependencies are the non-linkable deps passed in as an
+ // argument, plus the data file depdencies in the target.
+ void WriteOrderOnlyDependencies(
+ const UniqueVector<const Target*>& non_linkable_deps);
+
+ // Checks for duplicates in the given list of output files. If any duplicates
+ // are found, throws an error and return false.
+ bool CheckForDuplicateObjectFiles(const std::vector<OutputFile>& files) const;
+
+ const CTool* tool_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaCBinaryTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_C_BINARY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc
new file mode 100644
index 00000000000..3281176efe8
--- /dev/null
+++ b/gn/tools/gn/ninja_c_binary_target_writer_unittest.cc
@@ -0,0 +1,1187 @@
+// Copyright 2019 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 "tools/gn/ninja_c_binary_target_writer.h"
+
+#include <memory>
+#include <sstream>
+#include <utility>
+
+#include "tools/gn/config.h"
+#include "tools/gn/ninja_target_command_util.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scheduler.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+using NinjaCBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaCBinaryTargetWriterTest, SourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ // Also test object files, which should be just passed through to the
+ // dependents to link.
+ target.sources().push_back(SourceFile("//foo/input3.o"));
+ target.sources().push_back(SourceFile("//foo/input4.obj"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.source_types_used().Set(SourceFile::SOURCE_O);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Source set itself.
+ {
+ 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/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\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";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+
+ // A shared library that depends on the source set.
+ Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ shlib_target.set_output_type(Target::SHARED_LIBRARY);
+ shlib_target.public_deps().push_back(LabelTargetPair(&target));
+ shlib_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(shlib_target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&shlib_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 = libshlib\n"
+ "\n"
+ "\n"
+ // Ordering of the obj files here should come out in the order
+ // specified, with the target's first, followed by the source set's, in
+ // order.
+ "build ./libshlib.so: solink obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
+ "|| obj/foo/bar.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+
+ // A static library that depends on the source set (should not link it).
+ Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
+ stlib_target.set_output_type(Target::STATIC_LIBRARY);
+ stlib_target.public_deps().push_back(LabelTargetPair(&target));
+ stlib_target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(stlib_target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&stlib_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 = libstlib\n"
+ "\n"
+ "\n"
+ // There are no sources so there are no params to alink. (In practice
+ // this will probably fail in the archive tool.)
+ "build obj/foo/libstlib.a: alink || obj/foo/bar.stamp\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+
+ // Make the static library 'complete', which means it should be linked.
+ stlib_target.set_complete_static_lib(true);
+ {
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&stlib_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 = libstlib\n"
+ "\n"
+ "\n"
+ // Ordering of the obj files here should come out in the order
+ // specified, with the target's first, followed by the source set's, in
+ // order.
+ "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj "
+ "|| obj/foo/bar.stamp\n"
+ " arflags =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, EscapeDefines) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.config_values().defines().push_back("BOOL_DEF");
+ target.config_values().defines().push_back("INT_DEF=123");
+ target.config_values().defines().push_back("STR_DEF=\"ABCD-1\"");
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expectedSubstr[] =
+#if defined(OS_WIN)
+ "defines = -DBOOL_DEF -DINT_DEF=123 \"-DSTR_DEF=\\\"ABCD-1\\\"\"";
+#else
+ "defines = -DBOOL_DEF -DINT_DEF=123 -DSTR_DEF=\\\"ABCD-1\\\"";
+#endif
+ std::string out_str = out.str();
+ EXPECT_TRUE(out_str.find(out_str) != std::string::npos);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, StaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().arflags().push_back("--asdf");
+ 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/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, CompleteStaticLibrary) {
+ TestWithScope setup;
+ Err err;
+
+ TestTarget target(setup, "//foo:bar", Target::STATIC_LIBRARY);
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().arflags().push_back("--asdf");
+ target.set_complete_static_lib(true);
+
+ TestTarget baz(setup, "//foo:baz", Target::STATIC_LIBRARY);
+ baz.sources().push_back(SourceFile("//foo/input2.cc"));
+ baz.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ target.public_deps().push_back(LabelTargetPair(&baz));
+
+ ASSERT_TRUE(target.OnResolved(&err));
+ ASSERT_TRUE(baz.OnResolved(&err));
+
+ // A complete static library that depends on an incomplete static library
+ // should link in the dependent object files as if the dependent target
+ // were a source set.
+ {
+ 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/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
+ "obj/foo/libbaz.input2.o || obj/foo/libbaz.a\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+
+ // Make the dependent static library complete.
+ baz.set_complete_static_lib(true);
+
+ // Dependent complete static libraries should not be linked directly.
+ {
+ 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/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.input1.o: cxx ../../foo/input1.cc\n"
+ "\n"
+ "build obj/foo/libbar.a: alink obj/foo/libbar.input1.o "
+ "|| obj/foo/libbaz.a\n"
+ " arflags = --asdf\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+ }
+}
+
+// This tests that output extension and output dir overrides apply, and input
+// dependencies are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, OutputExtensionAndInputDeps) {
+ Err err;
+ TestWithScope setup;
+
+ // An action for our library to depend on.
+ Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
+ action.set_output_type(Target::ACTION_FOREACH);
+ action.visibility().SetPublic();
+ action.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ // A shared library w/ the output_extension set to a custom value.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.set_output_extension(std::string("so.6"));
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.public_deps().push_back(LabelTargetPair(&action));
+ 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/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
+ " || obj/foo/action.stamp\n"
+ "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
+ " || obj/foo/action.stamp\n"
+ "\n"
+ "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
+ // The order-only dependency here is stricly unnecessary since the
+ // sources list this as an order-only dep. See discussion in the code
+ // that writes this.
+ "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = .so.6\n"
+ " output_dir = foo\n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, NoHardDepsToNoPublicHeaderTarget) {
+ Err err;
+ TestWithScope setup;
+
+ SourceFile generated_file("//out/Debug/generated.cc");
+
+ // An action does code generation.
+ Target action(setup.settings(), Label(SourceDir("//foo/"), "generate"));
+ action.set_output_type(Target::ACTION);
+ action.visibility().SetPublic();
+ action.SetToolchain(setup.toolchain());
+ action.set_output_dir(SourceDir("//out/Debug/foo/"));
+ action.action_values().outputs() =
+ SubstitutionList::MakeForTest("//out/Debug/generated.cc");
+ ASSERT_TRUE(action.OnResolved(&err));
+
+ // A source set compiling geneated code, this target does not publicize any
+ // headers.
+ Target gen_obj(setup.settings(), Label(SourceDir("//foo/"), "gen_obj"));
+ gen_obj.set_output_type(Target::SOURCE_SET);
+ gen_obj.set_output_dir(SourceDir("//out/Debug/foo/"));
+ gen_obj.sources().push_back(generated_file);
+ gen_obj.source_types_used().Set(SourceFile::SOURCE_CPP);
+ gen_obj.visibility().SetPublic();
+ gen_obj.private_deps().push_back(LabelTargetPair(&action));
+ gen_obj.set_all_headers_public(false);
+ gen_obj.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(gen_obj.OnResolved(&err));
+
+ std::ostringstream obj_out;
+ NinjaCBinaryTargetWriter obj_writer(&gen_obj, obj_out);
+ obj_writer.Run();
+
+ const char obj_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = gen_obj\n"
+ "\n"
+ "build obj/out/Debug/gen_obj.generated.o: cxx generated.cc"
+ " || obj/foo/generate.stamp\n"
+ "\n"
+ "build obj/foo/gen_obj.stamp: stamp obj/out/Debug/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);
+
+ // A shared library depends on gen_obj, having corresponding header for
+ // generated obj.
+ Target gen_lib(setup.settings(), Label(SourceDir("//foo/"), "gen_lib"));
+ gen_lib.set_output_type(Target::SHARED_LIBRARY);
+ gen_lib.set_output_dir(SourceDir("//out/Debug/foo/"));
+ gen_lib.sources().push_back(SourceFile("//foor/generated.h"));
+ gen_lib.source_types_used().Set(SourceFile::SOURCE_H);
+ gen_lib.visibility().SetPublic();
+ gen_lib.private_deps().push_back(LabelTargetPair(&gen_obj));
+ gen_lib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(gen_lib.OnResolved(&err));
+
+ std::ostringstream lib_out;
+ NinjaCBinaryTargetWriter lib_writer(&gen_lib, lib_out);
+ lib_writer.Run();
+
+ const char lib_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libgen_lib\n"
+ "\n"
+ "\n"
+ "build ./libgen_lib.so: solink obj/out/Debug/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
+ " || obj/foo/gen_obj.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = .so\n"
+ " output_dir = foo\n";
+
+ std::string lib_str = lib_out.str();
+ EXPECT_EQ(lib_expected, lib_str);
+
+ // An executable depends on gen_lib.
+ Target executable(setup.settings(),
+ Label(SourceDir("//foo/"), "final_target"));
+ executable.set_output_type(Target::EXECUTABLE);
+ executable.set_output_dir(SourceDir("//out/Debug/foo/"));
+ executable.sources().push_back(SourceFile("//foo/main.cc"));
+ executable.source_types_used().Set(SourceFile::SOURCE_CPP);
+ executable.private_deps().push_back(LabelTargetPair(&gen_lib));
+ executable.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(executable.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&executable, final_out);
+ final_writer.Run();
+
+ // There is no order only dependency to action target.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = final_target\n"
+ "\n"
+ "build obj/foo/final_target.main.o: cxx ../../foo/main.cc\n"
+ "\n"
+ "build ./final_target: link obj/foo/final_target.main.o"
+ " ./libgen_lib.so\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = \n"
+ " output_dir = foo\n";
+
+ std::string final_str = final_out.str();
+ EXPECT_EQ(final_expected, final_str);
+}
+
+// Tests libs are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, LibsAndLibDirs) {
+ Err err;
+ TestWithScope setup;
+
+ // A shared library w/ libs and lib_dirs.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.config_values().libs().push_back(LibFile(SourceFile("//foo/lib1.a")));
+ target.config_values().libs().push_back(LibFile("foo"));
+ target.config_values().lib_dirs().push_back(SourceDir("//foo/bar/"));
+ 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"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libshlib\n"
+ "\n"
+ "\n"
+ "build ./libshlib.so: solink | ../../foo/lib1.a\n"
+ " ldflags = -L../../foo/bar\n"
+ " libs = ../../foo/lib1.a -lfoo\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, EmptyOutputExtension) {
+ Err err;
+ TestWithScope setup;
+
+ // This test is the same as OutputExtensionAndInputDeps, except that we call
+ // set_output_extension("") and ensure that we get an empty one and override
+ // the output prefix so that the name matches the target exactly.
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.set_output_prefix_override(true);
+ target.set_output_extension(std::string());
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ 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/foo\n"
+ "target_output_name = shlib\n"
+ "\n"
+ "build obj/foo/shlib.input1.o: cxx ../../foo/input1.cc\n"
+ "build obj/foo/shlib.input2.o: cxx ../../foo/input2.cc\n"
+ "\n"
+ "build ./shlib: solink obj/foo/shlib.input1.o "
+ "obj/foo/shlib.input2.o\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str);
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, SourceSetDataDeps) {
+ Err err;
+ TestWithScope setup;
+
+ // This target is a data (runtime) dependency of the intermediate target.
+ Target data(setup.settings(), Label(SourceDir("//foo/"), "data_target"));
+ data.set_output_type(Target::EXECUTABLE);
+ data.visibility().SetPublic();
+ data.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(data.OnResolved(&err));
+
+ // Intermediate source set target.
+ Target inter(setup.settings(), Label(SourceDir("//foo/"), "inter"));
+ inter.set_output_type(Target::SOURCE_SET);
+ inter.visibility().SetPublic();
+ inter.data_deps().push_back(LabelTargetPair(&data));
+ inter.SetToolchain(setup.toolchain());
+ inter.sources().push_back(SourceFile("//foo/inter.cc"));
+ inter.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(inter.OnResolved(&err)) << err.message();
+
+ // Write out the intermediate target.
+ std::ostringstream inter_out;
+ NinjaCBinaryTargetWriter inter_writer(&inter, inter_out);
+ inter_writer.Run();
+
+ // The intermediate source set will be a stamp file that depends on the
+ // object files, and will have an order-only dependency on its data dep and
+ // data file.
+ const char inter_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = inter\n"
+ "\n"
+ "build obj/foo/inter.inter.o: cxx ../../foo/inter.cc\n"
+ "\n"
+ "build obj/foo/inter.stamp: stamp obj/foo/inter.inter.o || "
+ "./data_target\n";
+ EXPECT_EQ(inter_expected, inter_out.str());
+
+ // Final target.
+ Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
+ exe.set_output_type(Target::EXECUTABLE);
+ exe.public_deps().push_back(LabelTargetPair(&inter));
+ exe.SetToolchain(setup.toolchain());
+ exe.sources().push_back(SourceFile("//foo/final.cc"));
+ exe.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(exe.OnResolved(&err));
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&exe, final_out);
+ final_writer.Run();
+
+ // The final output depends on both object files (one from the final target,
+ // one from the source set) and has an order-only dependency on the source
+ // set's stamp file and the final target's data file. The source set stamp
+ // dependency will create an implicit order-only dependency on the data
+ // target.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ "\n"
+ "build ./exe: link obj/foo/exe.final.o obj/foo/inter.inter.o || "
+ "obj/foo/inter.stamp\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ EXPECT_EQ(final_expected, final_out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, SharedLibraryModuleDefinitionFile) {
+ Err err;
+ TestWithScope setup;
+
+ Target shared_lib(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ shared_lib.set_output_type(Target::SHARED_LIBRARY);
+ shared_lib.SetToolchain(setup.toolchain());
+ shared_lib.sources().push_back(SourceFile("//foo/sources.cc"));
+ shared_lib.sources().push_back(SourceFile("//foo/bar.def"));
+ shared_lib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ shared_lib.source_types_used().Set(SourceFile::SOURCE_DEF);
+ ASSERT_TRUE(shared_lib.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&shared_lib, out);
+ writer.Run();
+
+ const char expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ "\n"
+ "build ./libbar.so: solink obj/foo/libbar.sources.o | ../../foo/bar.def\n"
+ " ldflags = /DEF:../../foo/bar.def\n"
+ " libs =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ EXPECT_EQ(expected, out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, LoadableModule) {
+ Err err;
+ TestWithScope setup;
+
+ Target loadable_module(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ loadable_module.set_output_type(Target::LOADABLE_MODULE);
+ loadable_module.visibility().SetPublic();
+ loadable_module.SetToolchain(setup.toolchain());
+ loadable_module.sources().push_back(SourceFile("//foo/sources.cc"));
+ loadable_module.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(loadable_module.OnResolved(&err)) << err.message();
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&loadable_module, out);
+ writer.Run();
+
+ const char loadable_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "build obj/foo/libbar.sources.o: cxx ../../foo/sources.cc\n"
+ "\n"
+ "build ./libbar.so: solink_module obj/foo/libbar.sources.o\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+ EXPECT_EQ(loadable_expected, out.str());
+
+ // Final target.
+ Target exe(setup.settings(), Label(SourceDir("//foo/"), "exe"));
+ exe.set_output_type(Target::EXECUTABLE);
+ exe.public_deps().push_back(LabelTargetPair(&loadable_module));
+ exe.SetToolchain(setup.toolchain());
+ exe.sources().push_back(SourceFile("//foo/final.cc"));
+ exe.source_types_used().Set(SourceFile::SOURCE_CPP);
+ ASSERT_TRUE(exe.OnResolved(&err)) << err.message();
+
+ std::ostringstream final_out;
+ NinjaCBinaryTargetWriter final_writer(&exe, final_out);
+ final_writer.Run();
+
+ // The final output depends on the loadable module so should have an
+ // order-only dependency on the loadable modules's output file.
+ const char final_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_cc =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = exe\n"
+ "\n"
+ "build obj/foo/exe.final.o: cxx ../../foo/final.cc\n"
+ "\n"
+ "build ./exe: link obj/foo/exe.final.o || ./libbar.so\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = \n"
+ " output_dir = \n";
+ EXPECT_EQ(final_expected, final_out.str());
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, WinPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ TestWithScope setup;
+
+ // A precompiled header toolchain.
+ Settings pch_settings(setup.build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(setup.toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cxx));
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_MSVC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&no_pch_target, out);
+ writer.Run();
+
+ const char no_pch_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99\n"
+ "cflags_cc =\n"
+ "target_output_name = no_pch_target\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc\n"
+ "build withpch/obj/foo/no_pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
+ "withpch/obj/foo/no_pch_target.input2.o\n";
+ EXPECT_EQ(no_pch_expected, out.str());
+ }
+
+ // This target specifies PCH.
+ {
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_header("build/precompile.h");
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.cc"));
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&pch_target, out);
+ writer.Run();
+
+ const char pch_win_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ // It should output language-specific pch files.
+ "cflags_c = /Fpwithpch/obj/foo/pch_target_c.pch "
+ "/Yubuild/precompile.h\n"
+ "cflags_cc = /Fpwithpch/obj/foo/pch_target_cc.pch "
+ "/Yubuild/precompile.h\n"
+ "target_output_name = pch_target\n"
+ "\n"
+ // Compile the precompiled source files with /Yc.
+ "build withpch/obj/build/pch_target.precompile.c.o: "
+ "withpch_cc ../../build/precompile.cc\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"
+ " 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"
+ "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"
+ "\n"
+ "build withpch/obj/foo/pch_target.stamp: withpch_stamp "
+ "withpch/obj/foo/pch_target.input1.o "
+ "withpch/obj/foo/pch_target.input2.o "
+ // The precompiled object files were added to the outputs.
+ "withpch/obj/build/pch_target.precompile.c.o "
+ "withpch/obj/build/pch_target.precompile.cc.o\n";
+ EXPECT_EQ(pch_win_expected, out.str());
+ }
+}
+
+TEST_F(NinjaCBinaryTargetWriterTest, GCCPrecompiledHeaders) {
+ Err err;
+
+ // This setup's toolchain does not have precompiled headers defined.
+ TestWithScope setup;
+
+ // A precompiled header toolchain.
+ Settings pch_settings(setup.build_settings(), "withpch/");
+ Toolchain pch_toolchain(&pch_settings,
+ Label(SourceDir("//toolchain/"), "withpch"));
+ pch_settings.set_toolchain_label(pch_toolchain.label());
+ pch_settings.set_default_toolchain_label(setup.toolchain()->label());
+
+ // Declare a C++ compiler that supports PCH.
+ std::unique_ptr<Tool> cxx = std::make_unique<CTool>(CTool::kCToolCxx);
+ CTool* cxx_tool = cxx->AsC();
+ TestWithScope::SetCommandForTool(
+ "c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cxx_tool);
+ cxx_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cxx_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cxx));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // Add a C compiler as well.
+ std::unique_ptr<Tool> cc = std::make_unique<CTool>(CTool::kCToolCc);
+ CTool* cc_tool = cc->AsC();
+ TestWithScope::SetCommandForTool(
+ "cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
+ "-o {{output}}",
+ cc_tool);
+ cc_tool->set_outputs(SubstitutionList::MakeForTest(
+ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
+ cc_tool->set_precompiled_header_type(CTool::PCH_GCC);
+ pch_toolchain.SetTool(std::move(cc));
+ pch_toolchain.ToolchainSetupComplete();
+
+ // This target doesn't specify precompiled headers.
+ {
+ Target no_pch_target(&pch_settings,
+ Label(SourceDir("//foo/"), "no_pch_target"));
+ no_pch_target.set_output_type(Target::SOURCE_SET);
+ no_pch_target.visibility().SetPublic();
+ no_pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ no_pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ no_pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ no_pch_target.config_values().cflags_c().push_back("-std=c99");
+ no_pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(no_pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&no_pch_target, out);
+ writer.Run();
+
+ const char no_pch_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99\n"
+ "cflags_cc =\n"
+ "target_output_name = no_pch_target\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.input1.o: "
+ "withpch_cxx ../../foo/input1.cc\n"
+ "build withpch/obj/foo/no_pch_target.input2.o: "
+ "withpch_cc ../../foo/input2.c\n"
+ "\n"
+ "build withpch/obj/foo/no_pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/no_pch_target.input1.o "
+ "withpch/obj/foo/no_pch_target.input2.o\n";
+ EXPECT_EQ(no_pch_expected, out.str());
+ }
+
+ // This target specifies PCH.
+ {
+ Target pch_target(&pch_settings, Label(SourceDir("//foo/"), "pch_target"));
+ pch_target.config_values().set_precompiled_source(
+ SourceFile("//build/precompile.h"));
+ pch_target.config_values().cflags_c().push_back("-std=c99");
+ pch_target.set_output_type(Target::SOURCE_SET);
+ pch_target.visibility().SetPublic();
+ pch_target.sources().push_back(SourceFile("//foo/input1.cc"));
+ pch_target.sources().push_back(SourceFile("//foo/input2.c"));
+ pch_target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ pch_target.source_types_used().Set(SourceFile::SOURCE_C);
+ pch_target.SetToolchain(&pch_toolchain);
+ ASSERT_TRUE(pch_target.OnResolved(&err));
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&pch_target, out);
+ writer.Run();
+
+ const char pch_gcc_expected[] =
+ "defines =\n"
+ "include_dirs =\n"
+ "cflags =\n"
+ "cflags_c = -std=c99 "
+ "-include withpch/obj/build/pch_target.precompile.h-c\n"
+ "cflags_cc = -include withpch/obj/build/pch_target.precompile.h-cc\n"
+ "target_output_name = pch_target\n"
+ "\n"
+ // Compile the precompiled sources with -x <lang>.
+ "build withpch/obj/build/pch_target.precompile.h-c.gch: "
+ "withpch_cc ../../build/precompile.h\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"
+ " 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"
+ "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"
+ "\n"
+ "build withpch/obj/foo/pch_target.stamp: "
+ "withpch_stamp withpch/obj/foo/pch_target.input1.o "
+ "withpch/obj/foo/pch_target.input2.o\n";
+ EXPECT_EQ(pch_gcc_expected, out.str());
+ }
+}
+
+// Should throw an error with the scheduler if a duplicate object file exists.
+// This is dependent on the toolchain's object file mapping.
+TEST_F(NinjaCBinaryTargetWriterTest, DupeObjFileError) {
+ TestWithScope setup;
+ TestTarget target(setup, "//foo:bar", Target::EXECUTABLE);
+ target.sources().push_back(SourceFile("//a.cc"));
+ target.sources().push_back(SourceFile("//a.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+
+ EXPECT_FALSE(scheduler().is_failed());
+
+ scheduler().SuppressOutputForTesting(true);
+
+ std::ostringstream out;
+ NinjaCBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ scheduler().SuppressOutputForTesting(false);
+
+ // Should have issued an error.
+ EXPECT_TRUE(scheduler().is_failed());
+}
+
+// This tests that output extension and output dir overrides apply, and input
+// dependencies are applied.
+TEST_F(NinjaCBinaryTargetWriterTest, InputFiles) {
+ Err err;
+ TestWithScope setup;
+
+ // This target has one input.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+ 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/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
+ " | ../../foo/input.data\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | ../../foo/input.data\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has one input but no source files.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SHARED_LIBRARY);
+ target.visibility().SetPublic();
+ target.config_values().inputs().push_back(SourceFile("//foo/input.data"));
+ 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"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = libbar\n"
+ "\n"
+ "\n"
+ "build ./libbar.so: solink | ../../foo/input.data\n"
+ " ldflags =\n"
+ " libs =\n"
+ " output_extension = .so\n"
+ " output_dir = \n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has multiple inputs.
+ {
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
+ target.config_values().inputs().push_back(SourceFile("//foo/input2.data"));
+ 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/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.inputs.stamp: stamp"
+ " ../../foo/input1.data ../../foo/input2.data\n"
+ "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+
+ // This target has one input itself, one from an immediate config, and one
+ // from a config tacked on to said config.
+ {
+ Config far_config(setup.settings(), Label(SourceDir("//foo/"), "qux"));
+ far_config.own_values().inputs().push_back(SourceFile("//foo/input3.data"));
+ ASSERT_TRUE(far_config.OnResolved(&err));
+
+ Config config(setup.settings(), Label(SourceDir("//foo/"), "baz"));
+ config.own_values().inputs().push_back(SourceFile("//foo/input2.data"));
+ config.configs().push_back(LabelConfigPair(&far_config));
+ ASSERT_TRUE(config.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.cc"));
+ target.sources().push_back(SourceFile("//foo/input2.cc"));
+ target.source_types_used().Set(SourceFile::SOURCE_CPP);
+ target.config_values().inputs().push_back(SourceFile("//foo/input1.data"));
+ target.configs().push_back(LabelConfigPair(&config));
+ 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/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.inputs.stamp: stamp"
+ " ../../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"
+ "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc"
+ " | obj/foo/bar.inputs.stamp\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
+ "obj/foo/bar.input2.o\n";
+
+ EXPECT_EQ(expected, out.str());
+ }
+}
diff --git a/gn/tools/gn/ninja_copy_target_writer.cc b/gn/tools/gn/ninja_copy_target_writer.cc
index 2a6f0018c66..ee5cf5273dc 100644
--- a/gn/tools/gn/ninja_copy_target_writer.cc
+++ b/gn/tools/gn/ninja_copy_target_writer.cc
@@ -5,6 +5,7 @@
#include "tools/gn/ninja_copy_target_writer.h"
#include "base/strings/string_util.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/ninja_utils.h"
#include "tools/gn/output_file.h"
#include "tools/gn/scheduler.h"
@@ -21,7 +22,7 @@ NinjaCopyTargetWriter::NinjaCopyTargetWriter(const Target* target,
NinjaCopyTargetWriter::~NinjaCopyTargetWriter() = default;
void NinjaCopyTargetWriter::Run() {
- const Tool* copy_tool = target_->toolchain()->GetTool(Toolchain::TYPE_COPY);
+ const Tool* copy_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolCopy);
if (!copy_tool) {
g_scheduler->FailWithError(Err(
nullptr, "Copy tool not defined",
@@ -32,7 +33,7 @@ void NinjaCopyTargetWriter::Run() {
return;
}
- const Tool* stamp_tool = target_->toolchain()->GetTool(Toolchain::TYPE_STAMP);
+ const Tool* stamp_tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolStamp);
if (!stamp_tool) {
g_scheduler->FailWithError(Err(
nullptr, "Copy tool not defined",
@@ -66,7 +67,7 @@ void NinjaCopyTargetWriter::WriteCopyRules(
const SubstitutionPattern& output_subst = output_subst_list.list()[0];
std::string tool_name = GetNinjaRulePrefixForToolchain(settings_) +
- Toolchain::ToolTypeToName(Toolchain::TYPE_COPY);
+ GeneralTool::kGeneralToolCopy;
size_t num_stamp_uses = target_->sources().size();
std::vector<OutputFile> input_deps = WriteInputDepsStampAndGetDep(
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer.cc b/gn/tools/gn/ninja_create_bundle_target_writer.cc
index cfc3d91dbdb..dab4485cbf5 100644
--- a/gn/tools/gn/ninja_create_bundle_target_writer.cc
+++ b/gn/tools/gn/ninja_create_bundle_target_writer.cc
@@ -7,6 +7,7 @@
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/ninja_utils.h"
#include "tools/gn/output_file.h"
#include "tools/gn/scheduler.h"
@@ -21,10 +22,9 @@ bool TargetRequireAssetCatalogCompilation(const Target* target) {
!target->bundle_data().partial_info_plist().is_null();
}
-void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
- const std::string& tool_name = Toolchain::ToolTypeToName(tool);
+void FailWithMissingToolError(const char* tool_name, const Target* target) {
g_scheduler->FailWithError(
- Err(nullptr, tool_name + " tool not defined",
+ Err(nullptr, std::string(tool_name) + " tool not defined",
"The toolchain " +
target->toolchain()->label().GetUserVisibleName(false) +
"\n"
@@ -36,9 +36,9 @@ void FailWithMissingToolError(Toolchain::ToolType tool, const Target* target) {
}
bool EnsureAllToolsAvailable(const Target* target) {
- const Toolchain::ToolType kRequiredTools[] = {
- Toolchain::TYPE_COPY_BUNDLE_DATA,
- Toolchain::TYPE_STAMP,
+ const char* kRequiredTools[] = {
+ GeneralTool::kGeneralToolCopyBundleData,
+ GeneralTool::kGeneralToolStamp,
};
for (size_t i = 0; i < arraysize(kRequiredTools); ++i) {
@@ -51,8 +51,10 @@ bool EnsureAllToolsAvailable(const Target* target) {
// The compile_xcassets tool is only required if the target has asset
// catalog resources to compile.
if (TargetRequireAssetCatalogCompilation(target)) {
- if (!target->toolchain()->GetTool(Toolchain::TYPE_COMPILE_XCASSETS)) {
- FailWithMissingToolError(Toolchain::TYPE_COMPILE_XCASSETS, target);
+ if (!target->toolchain()->GetTool(
+ GeneralTool::kGeneralToolCompileXCAssets)) {
+ FailWithMissingToolError(GeneralTool::kGeneralToolCompileXCAssets,
+ target);
return false;
}
}
@@ -161,7 +163,7 @@ void NinjaCreateBundleTargetWriter::WriteCopyBundleFileRuleSteps(
out_ << "build ";
path_output_.WriteFile(out_, expanded_output_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_COPY_BUNDLE_DATA) << " ";
+ << GeneralTool::kGeneralToolCopyBundleData << " ";
path_output_.WriteFile(out_, source_file);
if (!order_only_deps.empty()) {
@@ -205,7 +207,7 @@ void NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogStep(
out_ << "build ";
path_output_.WriteFile(out_, partial_info_plist);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
if (!order_only_deps.empty()) {
out_ << " ||";
path_output_.WriteFiles(out_, order_only_deps);
@@ -230,7 +232,7 @@ void NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogStep(
}
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_COMPILE_XCASSETS);
+ << GeneralTool::kGeneralToolCompileXCAssets;
std::set<SourceFile> asset_catalog_bundles;
for (const auto& source : target_->bundle_data().assets_catalog_sources()) {
@@ -274,7 +276,7 @@ NinjaCreateBundleTargetWriter::WriteCompileAssetsCatalogInputDepsStamp(
out_ << "build ";
path_output_.WriteFile(out_, xcassets_input_stamp_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
for (const Target* target : dependencies) {
out_ << " ";
@@ -305,7 +307,7 @@ void NinjaCreateBundleTargetWriter::WriteCodeSigningStep(
// Since the code signature step depends on all the files from the bundle,
// the create_bundle stamp can just depends on the output of the signature
// script (dependencies are transitive).
- output_files->swap(code_signing_output_files);
+ *output_files = std::move(code_signing_output_files);
out_ << ": " << code_signing_rule_name;
out_ << " | ";
@@ -340,7 +342,7 @@ OutputFile NinjaCreateBundleTargetWriter::WriteCodeSigningInputDepsStamp(
out_ << "build ";
path_output_.WriteFile(out_, code_signing_input_stamp_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
for (const SourceFile& source : code_signing_input_files) {
out_ << " ";
diff --git a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
index af7a7f5618b..51c982c828a 100644
--- a/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
+++ b/gn/tools/gn/ninja_create_bundle_target_writer_unittest.cc
@@ -186,6 +186,8 @@ TEST(NinjaCreateBundleTargetWriter, AssetCatalog) {
bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/Contents.json"));
bundle_data.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
bundle_data.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
@@ -290,6 +292,8 @@ TEST(NinjaCreateBundleTargetWriter, Complex) {
bundle_data2.sources().push_back(
SourceFile("//foo/Foo.xcassets/Contents.json"));
bundle_data2.sources().push_back(
+ SourceFile("//foo/Foo.xcassets/foo.colorset/Contents.json"));
+ bundle_data2.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json"));
bundle_data2.sources().push_back(
SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png"));
@@ -321,6 +325,18 @@ TEST(NinjaCreateBundleTargetWriter, Complex) {
bundle_data3.visibility().SetPublic();
ASSERT_TRUE(bundle_data3.OnResolved(&err));
+ Target bundle_data4(setup.settings(), Label(SourceDir("//biz/"), "assets"));
+ bundle_data4.set_output_type(Target::BUNDLE_DATA);
+ bundle_data4.sources().push_back(
+ SourceFile("//biz/Biz.xcassets/Contents.json"));
+ bundle_data4.sources().push_back(
+ SourceFile("//biz/Biz.xcassets/biz.colorset/Contents.json"));
+ bundle_data4.action_values().outputs() = SubstitutionList::MakeForTest(
+ "{{bundle_resources_dir}}/{{source_file_part}}");
+ bundle_data4.SetToolchain(setup.toolchain());
+ bundle_data4.visibility().SetPublic();
+ ASSERT_TRUE(bundle_data4.OnResolved(&err));
+
Target create_bundle(
setup.settings(),
Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(),
@@ -331,6 +347,7 @@ TEST(NinjaCreateBundleTargetWriter, Complex) {
create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1));
create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2));
create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3));
+ create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data4));
create_bundle.private_deps().push_back(LabelTargetPair(action.get()));
create_bundle.bundle_data().product_type().assign("com.apple.product-type");
create_bundle.bundle_data().set_partial_info_plist(
@@ -343,9 +360,9 @@ TEST(NinjaCreateBundleTargetWriter, Complex) {
writer.Run();
const char expected[] =
- "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/assets.stamp "
- "obj/foo/bar.stamp obj/foo/data.stamp obj/qux/info_plist.stamp "
- "obj/quz/assets.stamp\n"
+ "build obj/baz/bar.inputdeps.stamp: stamp obj/biz/assets.stamp "
+ "obj/foo/assets.stamp obj/foo/bar.stamp obj/foo/data.stamp "
+ "obj/qux/info_plist.stamp obj/quz/assets.stamp\n"
"build bar.bundle/Contents/Info.plist: copy_bundle_data "
"../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n"
"build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data "
@@ -354,11 +371,11 @@ TEST(NinjaCreateBundleTargetWriter, Complex) {
"../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n"
"build obj/baz/bar.xcassets.inputdeps.stamp: stamp "
"obj/foo/assets.stamp "
- "obj/quz/assets.stamp\n"
+ "obj/quz/assets.stamp obj/biz/assets.stamp\n"
"build bar.bundle/Contents/Resources/Assets.car | "
"baz/bar/bar_partial_info.plist: compile_xcassets "
- "../../foo/Foo.xcassets "
- "../../quz/Quz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
+ "../../foo/Foo.xcassets ../../quz/Quz.xcassets "
+ "../../biz/Biz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || "
"obj/baz/bar.inputdeps.stamp\n"
" product_type = com.apple.product-type\n"
" partial_info_plist = baz/bar/bar_partial_info.plist\n"
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer.cc b/gn/tools/gn/ninja_rust_binary_target_writer.cc
new file mode 100644
index 00000000000..5372fec834b
--- /dev/null
+++ b/gn/tools/gn/ninja_rust_binary_target_writer.cc
@@ -0,0 +1,247 @@
+// Copyright 2019 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 "tools/gn/ninja_rust_binary_target_writer.h"
+
+#include <sstream>
+
+#include "tools/gn/deps_iterator.h"
+#include "tools/gn/general_tool.h"
+#include "tools/gn/ninja_target_command_util.h"
+#include "tools/gn/ninja_utils.h"
+#include "tools/gn/rust_substitution_type.h"
+#include "tools/gn/substitution_writer.h"
+#include "tools/gn/target.h"
+
+namespace {
+
+// Returns the proper escape options for writing compiler and linker flags.
+EscapeOptions GetFlagOptions() {
+ EscapeOptions opts;
+ opts.mode = ESCAPE_NINJA_COMMAND;
+ return opts;
+}
+
+void WriteVar(const char* name,
+ const std::string& value,
+ EscapeOptions opts,
+ std::ostream& out) {
+ out << name << " = ";
+ EscapeStringToStream(out, value, opts);
+ out << std::endl;
+}
+
+void WriteCrateVars(const Target* target,
+ const Tool* tool,
+ EscapeOptions opts,
+ std::ostream& out) {
+ WriteVar(kRustSubstitutionCrateName.ninja_name,
+ target->rust_values().crate_name(), opts, out);
+
+ std::string crate_type;
+ switch (target->rust_values().crate_type()) {
+ // Auto-select the crate type for executables, static libraries, and rlibs.
+ case RustValues::CRATE_AUTO: {
+ switch (target->output_type()) {
+ case Target::EXECUTABLE:
+ crate_type = "bin";
+ break;
+ case Target::STATIC_LIBRARY:
+ crate_type = "staticlib";
+ break;
+ case Target::RUST_LIBRARY:
+ crate_type = "rlib";
+ break;
+ default:
+ NOTREACHED();
+ }
+ break;
+ }
+ case RustValues::CRATE_BIN:
+ crate_type = "bin";
+ break;
+ case RustValues::CRATE_CDYLIB:
+ crate_type = "cdylib";
+ break;
+ case RustValues::CRATE_DYLIB:
+ crate_type = "dylib";
+ break;
+ case RustValues::CRATE_PROC_MACRO:
+ crate_type = "proc-macro";
+ break;
+ case RustValues::CRATE_RLIB:
+ crate_type = "rlib";
+ break;
+ case RustValues::CRATE_STATICLIB:
+ crate_type = "staticlib";
+ break;
+ default:
+ NOTREACHED();
+ }
+ WriteVar(kRustSubstitutionCrateType.ninja_name, crate_type, opts, out);
+
+ WriteVar(SubstitutionOutputDir.ninja_name,
+ SubstitutionWriter::GetLinkerSubstitution(target, tool,
+ &SubstitutionOutputDir),
+ opts, out);
+ if (!target->output_extension_set()) {
+ DCHECK(tool->AsRust());
+ WriteVar(kRustSubstitutionOutputExtension.ninja_name,
+ tool->AsRust()->rustc_output_extension(
+ target->output_type(), target->rust_values().crate_type()),
+ opts, out);
+ } else if (target->output_extension().empty()) {
+ WriteVar(kRustSubstitutionOutputExtension.ninja_name, "", opts, out);
+ } else {
+ WriteVar(kRustSubstitutionOutputExtension.ninja_name,
+ std::string(".") + target->output_extension(), opts, out);
+ }
+
+ if (target->output_type() == Target::RUST_LIBRARY ||
+ target->output_type() == Target::SHARED_LIBRARY)
+ WriteVar(kRustSubstitutionOutputPrefix.ninja_name, "lib", opts, out);
+}
+
+} // namespace
+
+NinjaRustBinaryTargetWriter::NinjaRustBinaryTargetWriter(const Target* target,
+ std::ostream& out)
+ : NinjaBinaryTargetWriter(target, out),
+ tool_(target->toolchain()->GetToolForTargetFinalOutputAsRust(target)) {}
+
+NinjaRustBinaryTargetWriter::~NinjaRustBinaryTargetWriter() = default;
+
+// TODO(juliehockett): add inherited library support? and IsLinkable support?
+// for c-cross-compat
+void NinjaRustBinaryTargetWriter::Run() {
+ OutputFile input_dep = WriteInputsStampAndGetDep();
+
+ // The input dependencies will be an order-only dependency. This will cause
+ // Ninja to make sure the inputs are up to date before compiling this source,
+ // but changes in the inputs deps won't cause the file to be recompiled. See
+ // the comment on NinjaCBinaryTargetWriter::Run for more detailed explanation.
+ size_t num_stamp_uses = target_->sources().size();
+ std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep(
+ std::vector<const Target*>(), num_stamp_uses);
+
+ // Public rust_library deps go in a --extern rlibs, public non-rust deps go in
+ // -Ldependency rustdeps, and non-public source_sets get passed in as normal
+ // source files
+ UniqueVector<OutputFile> deps;
+ AddSourceSetFiles(target_, &deps);
+ if (target_->output_type() == Target::SOURCE_SET) {
+ WriteSharedVars(target_->toolchain()->substitution_bits());
+ WriteSourceSetStamp(deps.vector());
+ } else {
+ WriteCompilerVars();
+ UniqueVector<const Target*> linkable_deps;
+ UniqueVector<const Target*> non_linkable_deps;
+ GetDeps(&deps, &linkable_deps, &non_linkable_deps);
+
+ if (!input_dep.value().empty())
+ order_only_deps.push_back(input_dep);
+
+ std::vector<OutputFile> rustdeps;
+ std::vector<OutputFile> nonrustdeps;
+ for (const auto* non_linkable_dep : non_linkable_deps) {
+ order_only_deps.push_back(non_linkable_dep->dependency_output_file());
+ }
+
+ for (const auto* linkable_dep : linkable_deps) {
+ if (linkable_dep->source_types_used().RustSourceUsed()) {
+ rustdeps.push_back(linkable_dep->dependency_output_file());
+ } else {
+ nonrustdeps.push_back(linkable_dep->dependency_output_file());
+ }
+ deps.push_back(linkable_dep->dependency_output_file());
+ }
+
+ std::vector<OutputFile> tool_outputs;
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ target_, tool_, tool_->outputs(), &tool_outputs);
+ WriteCompilerBuildLine(target_->rust_values().crate_root(), deps.vector(),
+ order_only_deps, tool_->name(), tool_outputs);
+
+ std::vector<const Target*> extern_deps(linkable_deps.vector());
+ std::copy(non_linkable_deps.begin(), non_linkable_deps.end(),
+ std::back_inserter(extern_deps));
+ WriteExterns(extern_deps);
+
+ WriteRustdeps(rustdeps, nonrustdeps);
+ WriteEdition();
+ }
+}
+
+void NinjaRustBinaryTargetWriter::WriteCompilerVars() {
+ const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
+
+ EscapeOptions opts = GetFlagOptions();
+ WriteCrateVars(target_, tool_, opts, out_);
+
+ WriteOneFlag(target_, &kRustSubstitutionRustFlags, false,
+ RustTool::kRsToolRustc, &ConfigValues::rustflags, opts,
+ path_output_, out_);
+
+ WriteOneFlag(target_, &kRustSubstitutionRustEnv, false,
+ RustTool::kRsToolRustc, &ConfigValues::rustenv, opts,
+ path_output_, out_);
+
+ WriteSharedVars(subst);
+}
+
+void NinjaRustBinaryTargetWriter::WriteExterns(
+ const std::vector<const Target*>& deps) {
+ std::vector<const Target*> externs;
+ for (const Target* target : deps) {
+ if (target->output_type() == Target::RUST_LIBRARY ||
+ target->rust_values().crate_type() == RustValues::CRATE_PROC_MACRO) {
+ externs.push_back(target);
+ }
+ }
+ if (externs.empty())
+ return;
+ out_ << " externs =";
+ for (const Target* ex : externs) {
+ out_ << " --extern ";
+
+ const auto& renamed_dep =
+ target_->rust_values().aliased_deps().find(ex->label());
+ if (renamed_dep != target_->rust_values().aliased_deps().end()) {
+ out_ << renamed_dep->second << "=";
+ } else {
+ out_ << std::string(ex->rust_values().crate_name()) << "=";
+ }
+
+ path_output_.WriteFile(out_, ex->dependency_output_file());
+ }
+ out_ << std::endl;
+}
+
+void NinjaRustBinaryTargetWriter::WriteRustdeps(
+ const std::vector<OutputFile>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps) {
+ if (rustdeps.empty() && nonrustdeps.empty())
+ return;
+
+ out_ << " rustdeps =";
+ for (const auto& rustdep : rustdeps) {
+ out_ << " -Ldependency=";
+ path_output_.WriteDir(
+ out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
+ PathOutput::DIR_NO_LAST_SLASH);
+ }
+
+ for (const auto& rustdep : nonrustdeps) {
+ out_ << " -Lnative=";
+ path_output_.WriteDir(
+ out_, rustdep.AsSourceFile(settings_->build_settings()).GetDir(),
+ PathOutput::DIR_NO_LAST_SLASH);
+ }
+ out_ << std::endl;
+}
+
+void NinjaRustBinaryTargetWriter::WriteEdition() {
+ DCHECK(!target_->rust_values().edition().empty());
+ out_ << " edition = " << target_->rust_values().edition() << std::endl;
+}
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer.h b/gn/tools/gn/ninja_rust_binary_target_writer.h
new file mode 100644
index 00000000000..31d636b01aa
--- /dev/null
+++ b/gn/tools/gn/ninja_rust_binary_target_writer.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_NINJA_RUST_BINARY_TARGET_WRITER_H_
+#define TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
+
+#include "base/macros.h"
+#include "tools/gn/ninja_binary_target_writer.h"
+#include "tools/gn/rust_tool.h"
+
+struct EscapeOptions;
+
+// Writes a .ninja file for a binary target type (an executable, a shared
+// library, or a static library).
+class NinjaRustBinaryTargetWriter : public NinjaBinaryTargetWriter {
+ public:
+ NinjaRustBinaryTargetWriter(const Target* target, std::ostream& out);
+ ~NinjaRustBinaryTargetWriter() override;
+
+ void Run() override;
+
+ private:
+ 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>& rustdeps,
+ const std::vector<OutputFile>& nonrustdeps);
+ void WriteEdition();
+
+ const RustTool* tool_;
+
+ DISALLOW_COPY_AND_ASSIGN(NinjaRustBinaryTargetWriter);
+};
+
+#endif // TOOLS_GN_NINJA_RUST_BINARY_TARGET_WRITER_H_
diff --git a/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc b/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc
new file mode 100644
index 00000000000..90d592bf5db
--- /dev/null
+++ b/gn/tools/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -0,0 +1,578 @@
+// Copyright 2019 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 "tools/gn/ninja_rust_binary_target_writer.h"
+
+#include "tools/gn/config.h"
+#include "tools/gn/rust_values.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scheduler.h"
+#include "tools/gn/test_with_scope.h"
+#include "util/build_config.h"
+#include "util/test/test.h"
+
+using NinjaRustBinaryTargetWriterTest = TestWithScheduler;
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustSourceSet) {
+ Err err;
+ TestWithScope setup;
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::SOURCE_SET);
+ target.visibility().SetPublic();
+ target.sources().push_back(SourceFile("//foo/input1.rs"));
+ target.sources().push_back(SourceFile("//foo/main.rs"));
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ // Source set itself.
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/bar.stamp: stamp ../../foo/input1.rs "
+ "../../foo/main.rs\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustExecutable) {
+ Err err;
+ TestWithScope setup;
+
+ Target source_set(setup.settings(), Label(SourceDir("//foo/"), "sources"));
+ source_set.set_output_type(Target::SOURCE_SET);
+ source_set.visibility().SetPublic();
+ source_set.sources().push_back(SourceFile("//foo/input1.rs"));
+ source_set.sources().push_back(SourceFile("//foo/input2.rs"));
+ source_set.source_types_used().Set(SourceFile::SOURCE_RS);
+ source_set.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(source_set.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input3.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.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&source_set));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/input3.rs "
+ "../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
+ "obj/foo/sources.stamp\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+}
+
+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.rust_values().edition() = "2018";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mylib\n"
+ "crate_type = rlib\n"
+ "output_dir = \n"
+ "rustc_output_extension = .rlib\n"
+ "rustc_output_prefix = lib\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = mylib\n"
+ "\n"
+ "build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+
+ Target another_rlib(setup.settings(), Label(SourceDir("//foo/"), "direct"));
+ another_rlib.set_output_type(Target::RUST_LIBRARY);
+ another_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.rust_values().edition() = "2018";
+ another_rlib.SetToolchain(setup.toolchain());
+ another_rlib.public_deps().push_back(LabelTargetPair(&rlib));
+ ASSERT_TRUE(another_rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//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() = "foo_bar";
+ target.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/foo/libdirect.rlib obj/bar/libmylib.rlib\n"
+ " externs = --extern direct=obj/foo/libdirect.rlib --extern "
+ "mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/foo -Ldependency=obj/bar\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+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.rust_values().edition() = "2018";
+ another_rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(another_rlib.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//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() = "foo_bar";
+ target.rust_values().aliased_deps()[another_rlib.label()] = "direct_renamed";
+ target.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&another_rlib));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../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"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, NonRustDeps) {
+ 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.rust_values().edition() = "2018";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ Target staticlib(setup.settings(), Label(SourceDir("//foo/"), "static"));
+ staticlib.set_output_type(Target::STATIC_LIBRARY);
+ staticlib.visibility().SetPublic();
+ staticlib.sources().push_back(SourceFile("//foo/static.cpp"));
+ staticlib.source_types_used().Set(SourceFile::SOURCE_CPP);
+ staticlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(staticlib.OnResolved(&err));
+
+ Target nonrust(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ nonrust.set_output_type(Target::EXECUTABLE);
+ nonrust.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ nonrust.sources().push_back(SourceFile("//foo/source.rs"));
+ nonrust.sources().push_back(main);
+ nonrust.source_types_used().Set(SourceFile::SOURCE_RS);
+ nonrust.rust_values().set_crate_root(main);
+ nonrust.rust_values().crate_name() = "foo_bar";
+ nonrust.rust_values().edition() = "2018";
+ nonrust.private_deps().push_back(LabelTargetPair(&rlib));
+ nonrust.private_deps().push_back(LabelTargetPair(&staticlib));
+ nonrust.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(nonrust.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&nonrust, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/bar/libmylib.rlib obj/foo/libstatic.a\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/bar -Lnative=obj/foo\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+
+ Target nonrust_only(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ nonrust_only.set_output_type(Target::EXECUTABLE);
+ nonrust_only.visibility().SetPublic();
+ nonrust_only.sources().push_back(SourceFile("//foo/source.rs"));
+ nonrust_only.sources().push_back(main);
+ nonrust_only.source_types_used().Set(SourceFile::SOURCE_RS);
+ nonrust_only.rust_values().set_crate_root(main);
+ nonrust_only.rust_values().crate_name() = "foo_bar";
+ nonrust_only.rust_values().edition() = "2018";
+ nonrust_only.private_deps().push_back(LabelTargetPair(&staticlib));
+ nonrust_only.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(nonrust_only.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&nonrust_only, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/foo/libstatic.a\n"
+ " rustdeps = -Lnative=obj/foo\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, RustOutputExtensionAndDir) {
+ Err err;
+ TestWithScope setup;
+
+ Target source_set(setup.settings(), Label(SourceDir("//foo/"), "sources"));
+ source_set.set_output_type(Target::SOURCE_SET);
+ source_set.visibility().SetPublic();
+ source_set.sources().push_back(SourceFile("//foo/input1.rs"));
+ source_set.sources().push_back(SourceFile("//foo/input2.rs"));
+ source_set.source_types_used().Set(SourceFile::SOURCE_RS);
+ source_set.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(source_set.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//foo/input3.rs"));
+ target.sources().push_back(main);
+ target.source_types_used().Set(SourceFile::SOURCE_RS);
+ target.set_output_extension(std::string("exe"));
+ target.set_output_dir(SourceDir("//out/Debug/foo/"));
+ target.rust_values().set_crate_root(main);
+ target.rust_values().crate_name() = "foo_bar";
+ target.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&source_set));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = foo\n"
+ "rustc_output_extension = .exe\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar.exe: rustc ../../foo/main.rs | ../../foo/input3.rs "
+ "../../foo/main.rs ../../foo/input1.rs ../../foo/input2.rs || "
+ "obj/foo/sources.stamp\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, ProcMacro) {
+ Err err;
+ TestWithScope setup;
+
+ Target procmacro(setup.settings(), Label(SourceDir("//bar/"), "mymacro"));
+ procmacro.set_output_type(Target::LOADABLE_MODULE);
+ procmacro.visibility().SetPublic();
+ SourceFile barlib("//bar/lib.rs");
+ procmacro.sources().push_back(SourceFile("//bar/mylib.rs"));
+ procmacro.sources().push_back(barlib);
+ procmacro.source_types_used().Set(SourceFile::SOURCE_RS);
+ procmacro.rust_values().set_crate_root(barlib);
+ procmacro.rust_values().crate_name() = "mymacro";
+ procmacro.rust_values().edition() = "2018";
+ procmacro.rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ procmacro.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(procmacro.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&procmacro, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mymacro\n"
+ "crate_type = proc-macro\n"
+ "output_dir = \n"
+ "rustc_output_extension = .so\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = mymacro\n"
+ "\n"
+ "build obj/bar/libmymacro.so: rustc ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//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() = "foo_bar";
+ target.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&procmacro));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs || obj/bar/libmymacro.so\n"
+ " externs = --extern mymacro=obj/bar/libmymacro.so\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
+
+TEST_F(NinjaRustBinaryTargetWriterTest, GroupDeps) {
+ 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.rust_values().edition() = "2018";
+ rlib.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(rlib.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&rlib, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = mylib\n"
+ "crate_type = rlib\n"
+ "output_dir = \n"
+ "rustc_output_extension = .rlib\n"
+ "rustc_output_prefix = lib\n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/bar\n"
+ "target_output_name = mylib\n"
+ "\n"
+ "build obj/bar/libmylib.rlib: rustc ../../bar/lib.rs | "
+ "../../bar/mylib.rs ../../bar/lib.rs\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << out_str;
+ }
+
+ Target group(setup.settings(), Label(SourceDir("//baz/"), "group"));
+ group.set_output_type(Target::GROUP);
+ group.visibility().SetPublic();
+ group.public_deps().push_back(LabelTargetPair(&rlib));
+ group.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(group.OnResolved(&err));
+
+ Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
+ target.set_output_type(Target::EXECUTABLE);
+ target.visibility().SetPublic();
+ SourceFile main("//foo/main.rs");
+ target.sources().push_back(SourceFile("//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() = "foo_bar";
+ target.rust_values().edition() = "2018";
+ target.private_deps().push_back(LabelTargetPair(&group));
+ target.SetToolchain(setup.toolchain());
+ ASSERT_TRUE(target.OnResolved(&err));
+
+ {
+ std::ostringstream out;
+ NinjaRustBinaryTargetWriter writer(&target, out);
+ writer.Run();
+
+ const char expected[] =
+ "crate_name = foo_bar\n"
+ "crate_type = bin\n"
+ "output_dir = \n"
+ "rustc_output_extension = \n"
+ "rustflags =\n"
+ "rustenv =\n"
+ "root_out_dir = .\n"
+ "target_out_dir = obj/foo\n"
+ "target_output_name = bar\n"
+ "\n"
+ "build obj/foo/foo_bar: rustc ../../foo/main.rs | ../../foo/source.rs "
+ "../../foo/main.rs obj/bar/libmylib.rlib || obj/baz/group.stamp\n"
+ " externs = --extern mylib=obj/bar/libmylib.rlib\n"
+ " rustdeps = -Ldependency=obj/bar\n"
+ " edition = 2018\n";
+ std::string out_str = out.str();
+ EXPECT_EQ(expected, out_str) << expected << "\n" << out_str;
+ }
+}
diff --git a/gn/tools/gn/ninja_target_command_util.cc b/gn/tools/gn/ninja_target_command_util.cc
index 29662cddb83..ef9957e8c25 100644
--- a/gn/tools/gn/ninja_target_command_util.cc
+++ b/gn/tools/gn/ninja_target_command_util.cc
@@ -6,83 +6,81 @@
#include <string.h>
+#include "tools/gn/c_tool.h"
#include "tools/gn/substitution_writer.h"
namespace {
// Returns the language-specific suffix for precompiled header files.
-const char* GetPCHLangSuffixForToolType(Toolchain::ToolType type) {
- switch (type) {
- case Toolchain::TYPE_CC:
- return "c";
- case Toolchain::TYPE_CXX:
- return "cc";
- case Toolchain::TYPE_OBJC:
- return "m";
- case Toolchain::TYPE_OBJCXX:
- return "mm";
- default:
- NOTREACHED() << "Not a valid PCH tool type: " << type;
- return "";
- }
+const char* GetPCHLangSuffixForToolType(const char* name) {
+ if (name == CTool::kCToolCc)
+ return "c";
+ if (name == CTool::kCToolCxx)
+ return "cc";
+ if (name == CTool::kCToolObjC)
+ return "m";
+ if (name == CTool::kCToolObjCxx)
+ return "mm";
+ NOTREACHED() << "Not a valid PCH tool type: " << name;
+ return "";
}
} // namespace
// Returns the computed name of the Windows .pch file for the given
// tool type. The tool must support precompiled headers.
-OutputFile GetWindowsPCHFile(const Target* target,
- Toolchain::ToolType tool_type) {
+OutputFile GetWindowsPCHFile(const Target* target, const char* tool_name) {
// Use "obj/{dir}/{target_name}_{lang}.pch" which ends up
// looking like "obj/chrome/browser/browser_cc.pch"
OutputFile ret = GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ);
ret.value().append(target->label().name());
ret.value().push_back('_');
- ret.value().append(GetPCHLangSuffixForToolType(tool_type));
+ ret.value().append(GetPCHLangSuffixForToolType(tool_name));
ret.value().append(".pch");
return ret;
}
void WriteOneFlag(const Target* target,
- SubstitutionType subst_enum,
+ const Substitution* subst_enum,
bool has_precompiled_headers,
- Toolchain::ToolType tool_type,
+ const char* tool_name,
const std::vector<std::string>& (ConfigValues::*getter)()
const,
EscapeOptions flag_escape_options,
PathOutput& path_output,
std::ostream& out,
bool write_substitution) {
- if (!target->toolchain()->substitution_bits().used[subst_enum])
+ if (!target->toolchain()->substitution_bits().used.count(subst_enum))
return;
if (write_substitution)
- out << kSubstitutionNinjaNames[subst_enum] << " =";
+ out << subst_enum->ninja_name << " =";
if (has_precompiled_headers) {
- const Tool* tool = target->toolchain()->GetTool(tool_type);
- if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) {
+ const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
+ if (tool && tool->precompiled_header_type() == CTool::PCH_MSVC) {
// Name the .pch file.
out << " /Fp";
- path_output.WriteFile(out, GetWindowsPCHFile(target, tool_type));
+ path_output.WriteFile(out, GetWindowsPCHFile(target, tool_name));
// Enables precompiled headers and names the .h file. It's a string
// rather than a file name (so no need to rebase or use path_output).
out << " /Yu" << target->config_values().precompiled_header();
RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
out);
- } else if (tool && tool->precompiled_header_type() == Tool::PCH_GCC) {
+ } else if (tool && tool->precompiled_header_type() == CTool::PCH_GCC) {
// The targets to build the .gch files should omit the -include flag
- // below. To accomplish this, each substitution flag is overwritten in the
- // target rule and these values are repeated. The -include flag is omitted
- // in place of the required -x <header lang> flag for .gch targets.
+ // below. To accomplish this, each substitution flag is overwritten in
+ // the target rule and these values are repeated. The -include flag is
+ // omitted in place of the required -x <header lang> flag for .gch
+ // targets.
RecursiveTargetConfigStringsToStream(target, getter, flag_escape_options,
out);
// Compute the gch file (it will be language-specific).
std::vector<OutputFile> outputs;
- GetPCHOutputFiles(target, tool_type, &outputs);
+ GetPCHOutputFiles(target, tool_name, &outputs);
if (!outputs.empty()) {
// Trim the .gch suffix for the -include flag.
// e.g. for gch file foo/bar/target.precompiled.h.gch:
@@ -105,14 +103,14 @@ void WriteOneFlag(const Target* target,
}
void GetPCHOutputFiles(const Target* target,
- Toolchain::ToolType tool_type,
+ const char* tool_name,
std::vector<OutputFile>* outputs) {
outputs->clear();
// Compute the tool. This must use the tool type passed in rather than the
// detected file type of the precompiled source file since the same
// precompiled source file will be used for separate C/C++ compiles.
- const Tool* tool = target->toolchain()->GetTool(tool_type);
+ const CTool* tool = target->toolchain()->GetToolAsC(tool_name);
if (!tool)
return;
SubstitutionWriter::ApplyListToCompilerAsOutputFile(
@@ -134,16 +132,16 @@ void GetPCHOutputFiles(const Target* target,
DCHECK(output_value[extension_offset - 1] == '.');
std::string output_extension;
- Tool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
+ CTool::PrecompiledHeaderType header_type = tool->precompiled_header_type();
switch (header_type) {
- case Tool::PCH_MSVC:
+ case CTool::PCH_MSVC:
output_extension = GetWindowsPCHObjectExtension(
- tool_type, output_value.substr(extension_offset - 1));
+ tool_name, output_value.substr(extension_offset - 1));
break;
- case Tool::PCH_GCC:
- output_extension = GetGCCPCHOutputExtension(tool_type);
+ case CTool::PCH_GCC:
+ output_extension = GetGCCPCHOutputExtension(tool_name);
break;
- case Tool::PCH_NONE:
+ case CTool::PCH_NONE:
NOTREACHED() << "No outputs for no PCH type.";
break;
}
@@ -151,8 +149,8 @@ void GetPCHOutputFiles(const Target* target,
output_extension);
}
-std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type) {
- const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+std::string GetGCCPCHOutputExtension(const char* tool_name) {
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
std::string result = ".";
// For GCC, the output name must have a .gch suffix and be annotated with
// the language type. For example:
@@ -166,9 +164,9 @@ std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type) {
return result;
}
-std::string GetWindowsPCHObjectExtension(Toolchain::ToolType tool_type,
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
const std::string& obj_extension) {
- const char* lang_suffix = GetPCHLangSuffixForToolType(tool_type);
+ const char* lang_suffix = GetPCHLangSuffixForToolType(tool_name);
std::string result = ".";
// For MSVC, annotate the obj files with the language type. For example:
// obj/foo/target_name.precompile.obj ->
diff --git a/gn/tools/gn/ninja_target_command_util.h b/gn/tools/gn/ninja_target_command_util.h
index 619add3d20c..327e120279c 100644
--- a/gn/tools/gn/ninja_target_command_util.h
+++ b/gn/tools/gn/ninja_target_command_util.h
@@ -61,9 +61,9 @@ struct IncludeWriter {
// tool-specific (e.g. "cflags_c"). For non-tool-specific flags (e.g.
// "defines") tool_type should be TYPE_NONE.
void WriteOneFlag(const Target* target,
- SubstitutionType subst_enum,
+ const Substitution* subst_enum,
bool has_precompiled_headers,
- Toolchain::ToolType tool_type,
+ const char* tool_name,
const std::vector<std::string>& (ConfigValues::*getter)()
const,
EscapeOptions flag_escape_options,
@@ -74,11 +74,11 @@ void WriteOneFlag(const Target* target,
// Fills |outputs| with the object or gch file for the precompiled header of the
// given type (flag type and tool type must match).
void GetPCHOutputFiles(const Target* target,
- Toolchain::ToolType tool_type,
+ const char* tool_name,
std::vector<OutputFile>* outputs);
-std::string GetGCCPCHOutputExtension(Toolchain::ToolType tool_type);
-std::string GetWindowsPCHObjectExtension(Toolchain::ToolType tool_type,
+std::string GetGCCPCHOutputExtension(const char* tool_name);
+std::string GetWindowsPCHObjectExtension(const char* tool_name,
const std::string& obj_extension);
#endif // TOOLS_GN_NINJA_TARGET_COMMAND_WRITER_H_
diff --git a/gn/tools/gn/ninja_target_writer.cc b/gn/tools/gn/ninja_target_writer.cc
index 253436b4f68..239e50fb4cb 100644
--- a/gn/tools/gn/ninja_target_writer.cc
+++ b/gn/tools/gn/ninja_target_writer.cc
@@ -12,6 +12,7 @@
#include "tools/gn/err.h"
#include "tools/gn/escape.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/ninja_action_target_writer.h"
#include "tools/gn/ninja_binary_target_writer.h"
#include "tools/gn/ninja_bundle_data_target_writer.h"
@@ -119,11 +120,11 @@ std::string NinjaTargetWriter::RunAndWriteFile(const Target* target) {
return rules.str();
}
-void NinjaTargetWriter::WriteEscapedSubstitution(SubstitutionType type) {
+void NinjaTargetWriter::WriteEscapedSubstitution(const Substitution* type) {
EscapeOptions opts;
opts.mode = ESCAPE_NINJA;
- out_ << kSubstitutionNinjaNames[type] << " = ";
+ out_ << type->ninja_name << " = ";
EscapeStringToStream(
out_, SubstitutionWriter::GetTargetSubstitution(target_, type), opts);
out_ << std::endl;
@@ -133,44 +134,44 @@ void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
bool written_anything = false;
// Target label.
- if (bits.used[SUBSTITUTION_LABEL]) {
- WriteEscapedSubstitution(SUBSTITUTION_LABEL);
+ if (bits.used.count(&SubstitutionLabel)) {
+ WriteEscapedSubstitution(&SubstitutionLabel);
written_anything = true;
}
// Target label name
- if (bits.used[SUBSTITUTION_LABEL_NAME]) {
- WriteEscapedSubstitution(SUBSTITUTION_LABEL_NAME);
+ if (bits.used.count(&SubstitutionLabelName)) {
+ WriteEscapedSubstitution(&SubstitutionLabelName);
written_anything = true;
}
// Root gen dir.
- if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) {
- WriteEscapedSubstitution(SUBSTITUTION_ROOT_GEN_DIR);
+ if (bits.used.count(&SubstitutionRootGenDir)) {
+ WriteEscapedSubstitution(&SubstitutionRootGenDir);
written_anything = true;
}
// Root out dir.
- if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) {
- WriteEscapedSubstitution(SUBSTITUTION_ROOT_OUT_DIR);
+ if (bits.used.count(&SubstitutionRootOutDir)) {
+ WriteEscapedSubstitution(&SubstitutionRootOutDir);
written_anything = true;
}
// Target gen dir.
- if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) {
- WriteEscapedSubstitution(SUBSTITUTION_TARGET_GEN_DIR);
+ if (bits.used.count(&SubstitutionTargetGenDir)) {
+ WriteEscapedSubstitution(&SubstitutionTargetGenDir);
written_anything = true;
}
// Target out dir.
- if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) {
- WriteEscapedSubstitution(SUBSTITUTION_TARGET_OUT_DIR);
+ if (bits.used.count(&SubstitutionTargetOutDir)) {
+ WriteEscapedSubstitution(&SubstitutionTargetOutDir);
written_anything = true;
}
// Target output name.
- if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) {
- WriteEscapedSubstitution(SUBSTITUTION_TARGET_OUTPUT_NAME);
+ if (bits.used.count(&SubstitutionTargetOutputName)) {
+ WriteEscapedSubstitution(&SubstitutionTargetOutputName);
written_anything = true;
}
@@ -294,7 +295,7 @@ std::vector<OutputFile> NinjaTargetWriter::WriteInputDepsStampAndGetDep(
out_ << "build ";
path_output_.WriteFile(out_, input_stamp_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
path_output_.WriteFiles(out_, outs);
out_ << "\n";
@@ -317,7 +318,7 @@ void NinjaTargetWriter::WriteStampForTarget(
path_output_.WriteFile(out_, stamp_file);
out_ << ": " << GetNinjaRulePrefixForToolchain(settings_)
- << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
+ << GeneralTool::kGeneralToolStamp;
path_output_.WriteFiles(out_, files);
if (!order_only_deps.empty()) {
diff --git a/gn/tools/gn/ninja_target_writer.h b/gn/tools/gn/ninja_target_writer.h
index a6db06038f2..b49f33e3a11 100644
--- a/gn/tools/gn/ninja_target_writer.h
+++ b/gn/tools/gn/ninja_target_writer.h
@@ -64,7 +64,7 @@ class NinjaTargetWriter {
private:
void WriteCopyRules();
- void WriteEscapedSubstitution(SubstitutionType type);
+ void WriteEscapedSubstitution(const Substitution* type);
DISALLOW_COPY_AND_ASSIGN(NinjaTargetWriter);
};
diff --git a/gn/tools/gn/ninja_toolchain_writer.cc b/gn/tools/gn/ninja_toolchain_writer.cc
index ee7c5cdc021..89562773826 100644
--- a/gn/tools/gn/ninja_toolchain_writer.cc
+++ b/gn/tools/gn/ninja_toolchain_writer.cc
@@ -9,7 +9,9 @@
#include "base/files/file_util.h"
#include "base/strings/stringize_macros.h"
#include "tools/gn/build_settings.h"
+#include "tools/gn/c_tool.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/ninja_utils.h"
#include "tools/gn/pool.h"
#include "tools/gn/settings.h"
@@ -40,13 +42,10 @@ void NinjaToolchainWriter::Run(
const std::vector<NinjaWriter::TargetRulePair>& rules) {
std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
- for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
- Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
- const Tool* tool = toolchain_->GetTool(tool_type);
- if (tool_type == Toolchain::TYPE_ACTION)
+ for (const auto& tool : toolchain_->tools()) {
+ if (tool.second->name() == GeneralTool::kGeneralToolAction)
continue;
- if (tool)
- WriteToolRule(tool_type, tool, rule_prefix);
+ WriteToolRule(tool.second.get(), rule_prefix);
}
out_ << std::endl;
@@ -76,32 +75,34 @@ bool NinjaToolchainWriter::RunAndWriteFile(
return true;
}
-void NinjaToolchainWriter::WriteToolRule(const Toolchain::ToolType type,
- const Tool* tool,
+void NinjaToolchainWriter::WriteToolRule(Tool* tool,
const std::string& rule_prefix) {
- out_ << "rule " << rule_prefix << Toolchain::ToolTypeToName(type)
- << std::endl;
+ out_ << "rule " << rule_prefix << tool->name() << std::endl;
// Rules explicitly include shell commands, so don't try to escape.
EscapeOptions options;
options.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
- CHECK(!tool->command().empty()) << "Command should not be empty";
- WriteRulePattern("command", tool->command(), options);
+ WriteCommandRulePattern("command", tool->command_launcher(), tool->command(), options);
WriteRulePattern("description", tool->description(), options);
WriteRulePattern("rspfile", tool->rspfile(), options);
WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
- if (tool->depsformat() == Tool::DEPS_GCC) {
- // GCC-style deps require a depfile.
- if (!tool->depfile().empty()) {
- WriteRulePattern("depfile", tool->depfile(), options);
- out_ << kIndent << "deps = gcc" << std::endl;
+ if (CTool* c_tool = tool->AsC()) {
+ if (c_tool->depsformat() == CTool::DEPS_GCC) {
+ // GCC-style deps require a depfile.
+ if (!tool->depfile().empty()) {
+ WriteRulePattern("depfile", tool->depfile(), options);
+ out_ << kIndent << "deps = gcc" << std::endl;
+ }
+ } else if (c_tool->depsformat() == CTool::DEPS_MSVC) {
+ // MSVC deps don't have a depfile.
+ out_ << kIndent << "deps = msvc" << std::endl;
}
- } else if (tool->depsformat() == Tool::DEPS_MSVC) {
- // MSVC deps don't have a depfile.
- out_ << kIndent << "deps = msvc" << std::endl;
+ } else if (!tool->depfile().empty()) {
+ WriteRulePattern("depfile", tool->depfile(), options);
+ out_ << kIndent << "deps = gcc" << std::endl;
}
// Use pool is specified.
@@ -124,3 +125,18 @@ void NinjaToolchainWriter::WriteRulePattern(const char* name,
SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
out_ << std::endl;
}
+
+void NinjaToolchainWriter::WriteCommandRulePattern(
+ const char* name,
+ const std::string& launcher,
+ const SubstitutionPattern& command,
+ const EscapeOptions& options) {
+ CHECK(!command.empty()) << "Command should not be empty";
+ if (command.empty())
+ return;
+ out_ << kIndent << name << " = " ;
+ if (!launcher.empty())
+ out_ << launcher << " ";
+ SubstitutionWriter::WriteWithNinjaVariables(command, options, out_);
+ out_ << std::endl;
+}
diff --git a/gn/tools/gn/ninja_toolchain_writer.h b/gn/tools/gn/ninja_toolchain_writer.h
index 9c6ad24b96b..b9b9906c48d 100644
--- a/gn/tools/gn/ninja_toolchain_writer.h
+++ b/gn/tools/gn/ninja_toolchain_writer.h
@@ -31,6 +31,7 @@ class NinjaToolchainWriter {
private:
FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRule);
+ FRIEND_TEST_ALL_PREFIXES(NinjaToolchainWriter, WriteToolRuleWithLauncher);
NinjaToolchainWriter(const Settings* settings,
const Toolchain* toolchain,
@@ -40,12 +41,14 @@ class NinjaToolchainWriter {
void Run(const std::vector<NinjaWriter::TargetRulePair>& extra_rules);
void WriteRules();
- void WriteToolRule(Toolchain::ToolType type,
- const Tool* tool,
- const std::string& rule_prefix);
+ void WriteToolRule(Tool* tool, const std::string& rule_prefix);
void WriteRulePattern(const char* name,
const SubstitutionPattern& pattern,
const EscapeOptions& options);
+ void WriteCommandRulePattern(const char* name,
+ const std::string& launcher,
+ const SubstitutionPattern& command,
+ const EscapeOptions& options);
const Settings* settings_;
const Toolchain* toolchain_;
diff --git a/gn/tools/gn/ninja_toolchain_writer_unittest.cc b/gn/tools/gn/ninja_toolchain_writer_unittest.cc
index a1963fd4b0b..ce1c533016e 100644
--- a/gn/tools/gn/ninja_toolchain_writer_unittest.cc
+++ b/gn/tools/gn/ninja_toolchain_writer_unittest.cc
@@ -13,8 +13,7 @@ TEST(NinjaToolchainWriter, WriteToolRule) {
std::ostringstream stream;
NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
- writer.WriteToolRule(Toolchain::TYPE_CC,
- setup.toolchain()->GetTool(Toolchain::TYPE_CC),
+ writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCc),
std::string("prefix_"));
EXPECT_EQ(
@@ -23,3 +22,18 @@ TEST(NinjaToolchainWriter, WriteToolRule) {
"-o ${out}\n",
stream.str());
}
+
+TEST(NinjaToolchainWriter, WriteToolRuleWithLauncher) {
+ TestWithScope setup;
+
+ std::ostringstream stream;
+ NinjaToolchainWriter writer(setup.settings(), setup.toolchain(), stream);
+ writer.WriteToolRule(setup.toolchain()->GetTool(CTool::kCToolCxx),
+ std::string("prefix_"));
+
+ EXPECT_EQ(
+ "rule prefix_cxx\n"
+ " command = launcher c++ ${in} ${cflags} ${cflags_cc} ${defines} ${include_dirs} "
+ "-o ${out}\n",
+ stream.str());
+}
diff --git a/gn/tools/gn/operators.cc b/gn/tools/gn/operators.cc
index ad25159ea80..40ea900978a 100644
--- a/gn/tools/gn/operators.cc
+++ b/gn/tools/gn/operators.cc
@@ -350,7 +350,7 @@ Value ExecuteEquals(Scope* exec_scope,
// Optionally apply the assignment filter in-place.
const PatternList* filter = dest->GetAssignmentFilter(exec_scope);
- if (filter) {
+ if (filter && written_value->type() == Value::LIST) {
std::vector<Value>& list_value = written_value->list_value();
auto first_deleted = std::remove_if(
list_value.begin(), list_value.end(),
diff --git a/gn/tools/gn/ordered_set.h b/gn/tools/gn/ordered_set.h
index 5081112884b..fda4e121f5c 100644
--- a/gn/tools/gn/ordered_set.h
+++ b/gn/tools/gn/ordered_set.h
@@ -14,9 +14,9 @@
template <typename T>
class OrderedSet {
private:
- typedef std::set<T> set_type;
- typedef typename set_type::const_iterator set_iterator;
- typedef std::vector<set_iterator> vector_type;
+ using set_type = std::set<T>;
+ using set_iterator = typename set_type::const_iterator;
+ using vector_type = std::vector<set_iterator>;
public:
static const size_t npos = static_cast<size_t>(-1);
diff --git a/gn/tools/gn/output_file.cc b/gn/tools/gn/output_file.cc
index f92e0976b43..e0c82fc23ec 100644
--- a/gn/tools/gn/output_file.cc
+++ b/gn/tools/gn/output_file.cc
@@ -7,9 +7,7 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/source_file.h"
-OutputFile::OutputFile() : value_() {}
-
-OutputFile::OutputFile(std::string&& v) : value_(v) {}
+OutputFile::OutputFile(std::string&& v) : value_(std::move(v)) {}
OutputFile::OutputFile(const std::string& v) : value_(v) {}
@@ -19,16 +17,13 @@ OutputFile::OutputFile(const BuildSettings* build_settings,
build_settings->build_dir(),
build_settings->root_path_utf8())) {}
-OutputFile::~OutputFile() = default;
-
SourceFile OutputFile::AsSourceFile(const BuildSettings* build_settings) const {
DCHECK(!value_.empty());
DCHECK(value_[value_.size() - 1] != '/');
std::string path = build_settings->build_dir().value();
path.append(value_);
- NormalizePath(&path);
- return SourceFile(path);
+ return SourceFile(std::move(path));
}
SourceDir OutputFile::AsSourceDir(const BuildSettings* build_settings) const {
@@ -40,5 +35,5 @@ SourceDir OutputFile::AsSourceDir(const BuildSettings* build_settings) const {
std::string path = build_settings->build_dir().value();
path.append(value_);
NormalizePath(&path);
- return SourceDir(path);
+ return SourceDir(std::move(path));
}
diff --git a/gn/tools/gn/output_file.h b/gn/tools/gn/output_file.h
index a3a64e0d5d6..cd3ee33f798 100644
--- a/gn/tools/gn/output_file.h
+++ b/gn/tools/gn/output_file.h
@@ -17,12 +17,13 @@ class SourceFile;
// relative to the output directory.
class OutputFile {
public:
- OutputFile();
+ OutputFile() = default;
+
explicit OutputFile(std::string&& v);
explicit OutputFile(const std::string& v);
+
OutputFile(const BuildSettings* build_settings,
const SourceFile& source_file);
- ~OutputFile();
std::string& value() { return value_; }
const std::string& value() const { return value_; }
@@ -59,8 +60,4 @@ struct hash<OutputFile> {
} // namespace std
-inline void swap(OutputFile& lhs, OutputFile& rhs) {
- lhs.value().swap(rhs.value());
-}
-
#endif // TOOLS_GN_OUTPUT_FILE_H_
diff --git a/gn/tools/gn/rust_substitution_type.cc b/gn/tools/gn/rust_substitution_type.cc
new file mode 100644
index 00000000000..c6b8e9e74f7
--- /dev/null
+++ b/gn/tools/gn/rust_substitution_type.cc
@@ -0,0 +1,48 @@
+// Copyright 2019 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 "tools/gn/rust_substitution_type.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "tools/gn/err.h"
+#include "tools/gn/substitution_type.h"
+
+const SubstitutionTypes RustSubstitutions = {
+ &kRustSubstitutionCrateName, &kRustSubstitutionCrateType,
+ &kRustSubstitutionEdition, &kRustSubstitutionExterns,
+ &kRustSubstitutionOutputExtension, &kRustSubstitutionOutputPrefix,
+ &kRustSubstitutionRustDeps, &kRustSubstitutionRustFlags,
+ &kRustSubstitutionRustEnv,
+};
+
+// Valid for Rust tools.
+const Substitution kRustSubstitutionCrateName = {"{{crate_name}}",
+ "crate_name"};
+const Substitution kRustSubstitutionCrateType = {"{{crate_type}}",
+ "crate_type"};
+const Substitution kRustSubstitutionEdition = {"{{edition}}", "edition"};
+const Substitution kRustSubstitutionExterns = {"{{externs}}", "externs"};
+const Substitution kRustSubstitutionOutputExtension = {
+ "{{rustc_output_extension}}", "rustc_output_extension"};
+const Substitution kRustSubstitutionOutputPrefix = {"{{rustc_output_prefix}}",
+ "rustc_output_prefix"};
+const Substitution kRustSubstitutionRustDeps = {"{{rustdeps}}", "rustdeps"};
+const Substitution kRustSubstitutionRustEnv = {"{{rustenv}}", "rustenv"};
+const Substitution kRustSubstitutionRustFlags = {"{{rustflags}}", "rustflags"};
+
+bool IsValidRustSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
+ type == &SubstitutionOutputDir ||
+ type == &kRustSubstitutionCrateName ||
+ type == &kRustSubstitutionCrateType ||
+ type == &kRustSubstitutionEdition ||
+ type == &kRustSubstitutionExterns ||
+ type == &kRustSubstitutionOutputExtension ||
+ type == &kRustSubstitutionOutputPrefix ||
+ type == &kRustSubstitutionRustDeps ||
+ type == &kRustSubstitutionRustEnv ||
+ type == &kRustSubstitutionRustFlags;
+}
diff --git a/gn/tools/gn/rust_substitution_type.h b/gn/tools/gn/rust_substitution_type.h
new file mode 100644
index 00000000000..827f5d6d4a8
--- /dev/null
+++ b/gn/tools/gn/rust_substitution_type.h
@@ -0,0 +1,29 @@
+// Copyright 2019 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_RUST_SUBSTITUTION_TYPE_H_
+#define TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
+
+#include <set>
+#include <vector>
+
+#include "tools/gn/substitution_type.h"
+
+// The set of substitutions available to Rust tools.
+extern const SubstitutionTypes RustSubstitutions;
+
+// Valid for Rust tools.
+extern const Substitution kRustSubstitutionCrateName;
+extern const Substitution kRustSubstitutionCrateType;
+extern const Substitution kRustSubstitutionEdition;
+extern const Substitution kRustSubstitutionExterns;
+extern const Substitution kRustSubstitutionOutputExtension;
+extern const Substitution kRustSubstitutionOutputPrefix;
+extern const Substitution kRustSubstitutionRustDeps;
+extern const Substitution kRustSubstitutionRustEnv;
+extern const Substitution kRustSubstitutionRustFlags;
+
+bool IsValidRustSubstitution(const Substitution* type);
+
+#endif // TOOLS_GN_RUST_SUBSTITUTION_TYPE_H_
diff --git a/gn/tools/gn/rust_tool.cc b/gn/tools/gn/rust_tool.cc
new file mode 100644
index 00000000000..e4e127d2ddc
--- /dev/null
+++ b/gn/tools/gn/rust_tool.cc
@@ -0,0 +1,156 @@
+// Copyright 2019 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 "tools/gn/rust_tool.h"
+
+#include "tools/gn/rust_substitution_type.h"
+#include "tools/gn/target.h"
+
+const char* RustTool::kRsToolRustc = "rustc";
+
+RustTool::RustTool(const char* n) : Tool(n), rlib_output_extension_(".rlib") {
+ CHECK(ValidateName(n));
+}
+
+RustTool::~RustTool() = default;
+
+RustTool* RustTool::AsRust() {
+ return this;
+}
+const RustTool* RustTool::AsRust() const {
+ return this;
+}
+
+bool RustTool::ValidateName(const char* name) const {
+ return name_ == kRsToolRustc;
+}
+
+void RustTool::SetComplete() {
+ SetToolComplete();
+}
+
+bool RustTool::SetOutputExtension(const Value* value,
+ std::string* var,
+ Err* err) {
+ DCHECK(!complete_);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+ if (value->string_value().empty())
+ return true;
+
+ *var = std::move(value->string_value());
+ return true;
+}
+
+bool RustTool::ReadOutputExtensions(Scope* scope, Err* err) {
+ if (!SetOutputExtension(scope->GetValue("exe_output_extension", true),
+ &exe_output_extension_, err))
+ return false;
+ if (!SetOutputExtension(scope->GetValue("rlib_output_extension", true),
+ &rlib_output_extension_, err))
+ return false;
+ if (!SetOutputExtension(scope->GetValue("dylib_output_extension", true),
+ &dylib_output_extension_, err))
+ return false;
+ if (!SetOutputExtension(scope->GetValue("cdylib_output_extension", true),
+ &cdylib_output_extension_, err))
+ return false;
+ if (!SetOutputExtension(scope->GetValue("staticlib_output_extension", true),
+ &staticlib_output_extension_, err))
+ return false;
+ if (!SetOutputExtension(scope->GetValue("proc_macro_output_extension", true),
+ &proc_macro_output_extension_, err))
+ return false;
+ return true;
+}
+
+bool RustTool::ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (list.list().empty()) {
+ *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
+ return false;
+ }
+
+ for (const auto& cur_type : list.required_types()) {
+ if (!IsValidRustSubstitution(cur_type)) {
+ *err = Err(*value, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+
+ *field = std::move(list);
+ return true;
+}
+
+bool RustTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ // Initialize default vars.
+ if (!Tool::InitTool(scope, toolchain, err)) {
+ return false;
+ }
+
+ if (!ReadOutputExtensions(scope, err)) {
+ return false;
+ }
+
+ // All Rust tools should have outputs.
+ if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool RustTool::ValidateSubstitution(const Substitution* sub_type) const {
+ if (name_ == kRsToolRustc)
+ return IsValidRustSubstitution(sub_type);
+ NOTREACHED();
+ return false;
+}
+
+const std::string& RustTool::rustc_output_extension(
+ Target::OutputType type,
+ const RustValues::CrateType crate_type) const {
+ switch (crate_type) {
+ case RustValues::CRATE_AUTO: {
+ switch (type) {
+ case Target::EXECUTABLE:
+ return exe_output_extension_;
+ case Target::STATIC_LIBRARY:
+ return staticlib_output_extension_;
+ case Target::RUST_LIBRARY:
+ return rlib_output_extension_;
+ default:
+ NOTREACHED();
+ return exe_output_extension_;
+ }
+ }
+ case RustValues::CRATE_DYLIB:
+ return dylib_output_extension_;
+ case RustValues::CRATE_CDYLIB:
+ return cdylib_output_extension_;
+ case RustValues::CRATE_PROC_MACRO:
+ return proc_macro_output_extension_;
+ default:
+ NOTREACHED();
+ return exe_output_extension_;
+ }
+}
diff --git a/gn/tools/gn/rust_tool.h b/gn/tools/gn/rust_tool.h
new file mode 100644
index 00000000000..682f74ad275
--- /dev/null
+++ b/gn/tools/gn/rust_tool.h
@@ -0,0 +1,98 @@
+// Copyright 2019 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_RUST_TOOL_H_
+#define TOOLS_GN_RUST_TOOL_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+#include "tools/gn/rust_values.h"
+#include "tools/gn/source_file.h"
+#include "tools/gn/substitution_list.h"
+#include "tools/gn/substitution_pattern.h"
+#include "tools/gn/target.h"
+#include "tools/gn/tool.h"
+
+class RustTool : public Tool {
+ public:
+ // Rust tools
+ static const char* kRsToolRustc;
+
+ explicit RustTool(const char* n);
+ ~RustTool();
+
+ // 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;
+
+ RustTool* AsRust() override;
+ const RustTool* AsRust() const override;
+
+ void set_exe_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ exe_output_extension_ = std::move(ext);
+ }
+
+ void set_rlib_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ rlib_output_extension_ = std::move(ext);
+ }
+
+ void set_dylib_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ dylib_output_extension_ = std::move(ext);
+ }
+
+ void set_cdylib_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ cdylib_output_extension_ = std::move(ext);
+ }
+
+ void set_staticlib_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ staticlib_output_extension_ = std::move(ext);
+ }
+
+ void set_proc_macro_output_extension(std::string ext) {
+ DCHECK(!complete_);
+ DCHECK(ext.empty() || ext[0] == '.');
+ proc_macro_output_extension_ = std::move(ext);
+ }
+
+ // Will include a leading "." if nonempty.
+ const std::string& rustc_output_extension(
+ Target::OutputType type,
+ const RustValues::CrateType crate_type) const;
+
+ private:
+ bool SetOutputExtension(const Value* value, std::string* var, Err* err);
+ bool ReadOutputExtensions(Scope* scope, Err* err);
+ bool ReadOutputsPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err);
+
+ std::string exe_output_extension_;
+ std::string rlib_output_extension_;
+ std::string dylib_output_extension_;
+ std::string cdylib_output_extension_;
+ std::string staticlib_output_extension_;
+ std::string proc_macro_output_extension_;
+
+ DISALLOW_COPY_AND_ASSIGN(RustTool);
+};
+
+#endif // TOOLS_GN_RUST_TOOL_H_
diff --git a/gn/tools/gn/rust_values.cc b/gn/tools/gn/rust_values.cc
new file mode 100644
index 00000000000..08f3e5ee9a2
--- /dev/null
+++ b/gn/tools/gn/rust_values.cc
@@ -0,0 +1,9 @@
+// Copyright 2019 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 "tools/gn/rust_values.h"
+
+RustValues::RustValues() : crate_type_(RustValues::CRATE_AUTO) {}
+
+RustValues::~RustValues() = default; \ No newline at end of file
diff --git a/gn/tools/gn/rust_values.h b/gn/tools/gn/rust_values.h
new file mode 100644
index 00000000000..ad9c5c6c88d
--- /dev/null
+++ b/gn/tools/gn/rust_values.h
@@ -0,0 +1,67 @@
+// Copyright 2019 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_RUST_TARGET_VALUES_H_
+#define TOOLS_GN_RUST_TARGET_VALUES_H_
+
+#include <map>
+
+#include "base/containers/flat_map.h"
+#include "tools/gn/label.h"
+#include "tools/gn/source_file.h"
+
+// Holds the values (outputs, args, script name, etc.) for either an action or
+// an action_foreach target.
+class RustValues {
+ public:
+ RustValues();
+ ~RustValues();
+
+ // Library crate types are specified here. Shared library crate types must be
+ // specified, all other crate types can be automatically deduced from the
+ // target type (e.g. executables use crate_type = "bin", static_libraries use
+ // crate_type = "staticlib") unless explicitly set.
+ enum CrateType {
+ CRATE_AUTO = 0,
+ CRATE_BIN,
+ CRATE_CDYLIB,
+ CRATE_DYLIB,
+ CRATE_PROC_MACRO,
+ CRATE_RLIB,
+ CRATE_STATICLIB,
+ };
+
+ // Name of this crate.
+ std::string& crate_name() { return crate_name_; }
+ const std::string& crate_name() const { return crate_name_; }
+
+ // Main source file for this crate.
+ const SourceFile& crate_root() const { return crate_root_; }
+ void set_crate_root(SourceFile& s) { crate_root_ = s; }
+
+ // Crate type for compilation.
+ CrateType crate_type() { return crate_type_; }
+ const CrateType crate_type() const { return crate_type_; }
+ void set_crate_type(CrateType s) { crate_type_ = s; }
+
+ std::string& edition() { return edition_; }
+ const std::string& edition() const { return edition_; }
+
+ // Any renamed dependencies for the `extern` flags.
+ const std::map<Label, std::string>& aliased_deps() const {
+ return aliased_deps_;
+ }
+ std::map<Label, std::string>& aliased_deps() { return aliased_deps_; }
+
+ private:
+ std::string crate_name_;
+ SourceFile crate_root_;
+ CrateType crate_type_ = CRATE_AUTO;
+ std::string edition_;
+ std::map<Label, std::string> aliased_deps_;
+
+ DISALLOW_COPY_AND_ASSIGN(RustValues);
+};
+
+#endif // TOOLS_GN_RUST_TARGET_VALUES_H_
diff --git a/gn/tools/gn/rust_values_generator.cc b/gn/tools/gn/rust_values_generator.cc
new file mode 100644
index 00000000000..910d5af7f88
--- /dev/null
+++ b/gn/tools/gn/rust_values_generator.cc
@@ -0,0 +1,206 @@
+// Copyright 2019 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 "tools/gn/rust_values_generator.h"
+
+#include "tools/gn/config_values_generator.h"
+#include "tools/gn/err.h"
+#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/functions.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/rust_variables.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/target.h"
+#include "tools/gn/value_extractors.h"
+
+static const char* kRustSupportedCrateTypesError =
+ "\"crate_type\" must be one of \"bin\", \"cdylib\", \"dylib\", or "
+ "\"proc-macro\", \"rlib\", \"staticlib\".";
+
+RustTargetGenerator::RustTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : target_(target),
+ scope_(scope),
+ function_call_(function_call),
+ err_(err) {}
+
+RustTargetGenerator::~RustTargetGenerator() = default;
+
+void RustTargetGenerator::Run() {
+ // source_set targets don't need any special Rust handling.
+ if (target_->output_type() == Target::SOURCE_SET)
+ return;
+
+ // Check that this type of target is Rust-supported.
+ if (target_->output_type() != Target::EXECUTABLE &&
+ target_->output_type() != Target::SHARED_LIBRARY &&
+ target_->output_type() != Target::RUST_LIBRARY &&
+ target_->output_type() != Target::STATIC_LIBRARY &&
+ target_->output_type() != Target::LOADABLE_MODULE) {
+ // Only valid rust output types.
+ *err_ = Err(function_call_,
+ "Target type \"" +
+ std::string(Target::GetStringForOutputType(
+ target_->output_type())) +
+ "\" is not supported for Rust compilation.",
+ "Supported target types are \"executable\", \"loadable_module\""
+ "\"shared_library\", \"static_library\", or \"source_set\".");
+ return;
+ }
+
+ if (!FillCrateName())
+ return;
+
+ if (!FillCrateType())
+ return;
+
+ if (!FillCrateRoot())
+ return;
+
+ if (!FillEdition())
+ return;
+
+ if (!FillAliasedDeps())
+ return;
+}
+
+bool RustTargetGenerator::FillCrateName() {
+ const Value* value = scope_->GetValue(variables::kRustCrateName, true);
+ if (!value) {
+ // The target name will be used.
+ target_->rust_values().crate_name() = target_->label().name();
+ return true;
+ }
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->rust_values().crate_name() = std::move(value->string_value());
+ return true;
+}
+
+bool RustTargetGenerator::FillCrateType() {
+ const Value* value = scope_->GetValue(variables::kRustCrateType, true);
+ if (!value) {
+ // Require shared_library and loadable_module targets to tell us what
+ // they want.
+ if (target_->output_type() == Target::SHARED_LIBRARY ||
+ target_->output_type() == Target::LOADABLE_MODULE) {
+ *err_ = Err(function_call_,
+ "Must set \"crate_type\" on a Rust \"shared_library\".",
+ kRustSupportedCrateTypesError);
+ return false;
+ }
+
+ return true;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ if (value->string_value() == "bin") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_BIN);
+ return true;
+ }
+ if (value->string_value() == "cdylib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_CDYLIB);
+ return true;
+ }
+ if (value->string_value() == "dylib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_DYLIB);
+ return true;
+ }
+ if (value->string_value() == "proc-macro") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_PROC_MACRO);
+ return true;
+ }
+ if (value->string_value() == "rlib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_RLIB);
+ return true;
+ }
+ if (value->string_value() == "staticlib") {
+ target_->rust_values().set_crate_type(RustValues::CRATE_STATICLIB);
+ return true;
+ }
+
+ *err_ = Err(value->origin(),
+ "Inadmissible crate type \"" + value->string_value() + "\".",
+ kRustSupportedCrateTypesError);
+ return false;
+}
+
+bool RustTargetGenerator::FillCrateRoot() {
+ const Value* value = scope_->GetValue(variables::kRustCrateRoot, true);
+ if (!value) {
+ // If there's only one source, use that.
+ if (target_->sources().size() == 1) {
+ target_->rust_values().set_crate_root(target_->sources()[0]);
+ return true;
+ }
+ // Otherwise, see if "lib.rs" or "main.rs" (as relevant) are in sources.
+ std::string to_find =
+ target_->output_type() == Target::EXECUTABLE ? "main.rs" : "lib.rs";
+ for (auto& source : target_->sources()) {
+ if (source.GetName() == to_find) {
+ target_->rust_values().set_crate_root(source);
+ return true;
+ }
+ }
+ *err_ = Err(function_call_, "Missing \"crate_root\" and missing \"" +
+ to_find + "\" in sources.");
+ return false;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ SourceFile dest;
+ if (!ExtractRelativeFile(scope_->settings()->build_settings(), *value,
+ scope_->GetSourceDir(), &dest, err_))
+ return false;
+
+ target_->rust_values().set_crate_root(dest);
+ return true;
+}
+
+bool RustTargetGenerator::FillEdition() {
+ const Value* value = scope_->GetValue(variables::kRustEdition, true);
+ if (!value) {
+ *err_ = Err(function_call_, "Missing \"edition\" in Rust target.");
+ return false;
+ }
+
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+
+ target_->rust_values().edition() = std::move(value->string_value());
+ return true;
+}
+
+bool RustTargetGenerator::FillAliasedDeps() {
+ const Value* value = scope_->GetValue(variables::kRustAliasedDeps, true);
+ if (!value)
+ return true;
+
+ if (!value->VerifyTypeIs(Value::SCOPE, err_))
+ return false;
+
+ Scope::KeyValueMap aliased_deps;
+ value->scope_value()->GetCurrentScopeValues(&aliased_deps);
+ for (const auto& pair : aliased_deps) {
+ Label dep_label =
+ Label::Resolve(scope_->GetSourceDir(), ToolchainLabelForScope(scope_),
+ pair.second, err_);
+
+ if (err_->has_error())
+ return false;
+
+ // Insert into the aliased_deps map.
+ target_->rust_values().aliased_deps().emplace(std::move(dep_label),
+ pair.first.as_string());
+ }
+
+ return true;
+}
diff --git a/gn/tools/gn/rust_values_generator.h b/gn/tools/gn/rust_values_generator.h
new file mode 100644
index 00000000000..fb939dbf2c9
--- /dev/null
+++ b/gn/tools/gn/rust_values_generator.h
@@ -0,0 +1,39 @@
+// Copyright 2019 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_RUST_VALUES_GENERATOR_H_
+#define TOOLS_GN_RUST_VALUES_GENERATOR_H_
+
+#include "base/macros.h"
+#include "tools/gn/target.h"
+
+class FunctionCallNode;
+
+// Collects and writes specified data.
+class RustTargetGenerator {
+ public:
+ RustTargetGenerator(Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err);
+ ~RustTargetGenerator();
+
+ void Run();
+
+ private:
+ bool FillCrateName();
+ bool FillCrateRoot();
+ bool FillCrateType();
+ bool FillEdition();
+ bool FillAliasedDeps();
+
+ Target* target_;
+ Scope* scope_;
+ const FunctionCallNode* function_call_;
+ Err* err_;
+
+ DISALLOW_COPY_AND_ASSIGN(RustTargetGenerator);
+};
+
+#endif // TOOLS_GN_RUST_VALUES_GENERATOR_H_
diff --git a/gn/tools/gn/rust_variables.cc b/gn/tools/gn/rust_variables.cc
new file mode 100644
index 00000000000..67018c7e34f
--- /dev/null
+++ b/gn/tools/gn/rust_variables.cc
@@ -0,0 +1,129 @@
+// Copyright 2019 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 "tools/gn/rust_variables.h"
+
+namespace variables {
+
+// Rust target variables ------------------------------------------------------
+
+const char kRustAliasedDeps[] = "aliased_deps";
+const char kRustAliasedDeps_HelpShort[] =
+ "aliased_deps: [scope] Set of crate-dependency pairs.";
+const char kRustAliasedDeps_Help[] =
+ R"(aliased_deps: [scope] Set of crate-dependency pairs.
+
+ Valid for `rust_library` targets and `executable`, `static_library`, and
+ `shared_library` targets that contain Rust sources.
+
+ A scope, each key indicating the renamed crate and the corresponding value
+ specifying the label of the dependency producing the relevant binary.
+
+ All dependencies listed in this field *must* be listed as deps of the target.
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ "//bar" ]
+ }
+
+ This target would compile the `foo` crate with the following `extern` flag:
+ `rustc ...command... --extern bar=<build_out_dir>/obj/bar`
+
+ executable("foo") {
+ sources = [ "main.rs" ]
+ deps = [ ":bar" ]
+ aliased_deps = {
+ bar_renamed = ":bar"
+ }
+ }
+
+ With the addition of `aliased_deps`, above target would instead compile with:
+ `rustc ...command... --extern bar_renamed=<build_out_dir>/obj/bar`
+)";
+
+const char kRustCrateName[] = "crate_name";
+const char kRustCrateName_HelpShort[] =
+ "crate_name: [string] The name for the compiled crate.";
+const char kRustCrateName_Help[] =
+ R"(crate_name: [string] The name for the compiled crate.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ If crate_name is not set, then this rule will use the target name.
+)";
+
+const char kRustCrateType[] = "crate_type";
+const char kRustCrateType_HelpShort[] =
+ "crate_type: [string] The type of linkage to use on a shared_library.";
+const char kRustCrateType_Help[] =
+ R"(crate_type: [string] The type of linkage to use on a shared_library.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ Options for this field are "cdylib", "staticlib", "proc-macro", and "dylib".
+ This field sets the `crate-type` attribute for the `rustc` tool on static
+ libraries, as well as the appropiate output extension in the
+ `rust_output_extension` attribute. Since outputs must be explicit, the `lib`
+ crate type (where the Rust compiler produces what it thinks is the
+ appropriate library type) is not supported.
+
+ It should be noted that the "dylib" crate type in Rust is unstable in the set
+ of symbols it exposes, and most usages today are potentially wrong and will
+ be broken in the future.
+
+ Static libraries, rust libraries, and executables have this field set
+ automatically.
+)";
+
+const char kRustCrateRoot[] = "crate_root";
+const char kRustCrateRoot_HelpShort[] =
+ "crate_root: [string] The root source file for a binary or library.";
+const char kRustCrateRoot_Help[] =
+ R"(crate_root: [string] The root source file for a binary or library.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ This file is usually the `main.rs` or `lib.rs` for binaries and libraries,
+ respectively.
+
+ If crate_root is not set, then this rule will look for a lib.rs file (or
+ main.rs for executable) or a single file in sources, if sources contains
+ only one file.
+)";
+
+const char kRustEdition[] = "edition";
+const char kRustEdition_HelpShort[] =
+ "edition: [string] The rustc edition to use in compiliation.";
+const char kRustEdition_Help[] =
+ R"(edition: [string] The rustc edition to use in compiliation.
+
+ Valid for `rust_library` targets and `executable`, `static_library`,
+ `shared_library`, and `source_set` targets that contain Rust sources.
+
+ This indicates the compiler edition to use in compilition. Should be a value
+ like "2015" or "2018", indiicating the appropriate value to pass to the
+ `--edition=<>` flag in rustc.
+)";
+
+void InsertRustVariables(VariableInfoMap* info_map) {
+ info_map->insert(std::make_pair(
+ kRustAliasedDeps,
+ VariableInfo(kRustAliasedDeps_HelpShort, kRustAliasedDeps_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateName,
+ VariableInfo(kRustCrateName_HelpShort, kRustCrateName_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateType,
+ VariableInfo(kRustCrateType_HelpShort, kRustCrateType_Help)));
+ info_map->insert(std::make_pair(
+ kRustCrateRoot,
+ VariableInfo(kRustCrateRoot_HelpShort, kRustCrateRoot_Help)));
+ info_map->insert(std::make_pair(
+ kRustEdition, VariableInfo(kRustEdition_HelpShort, kRustEdition_Help)));
+}
+
+} // namespace variables
diff --git a/gn/tools/gn/rust_variables.h b/gn/tools/gn/rust_variables.h
new file mode 100644
index 00000000000..c9ae4a0870c
--- /dev/null
+++ b/gn/tools/gn/rust_variables.h
@@ -0,0 +1,38 @@
+// Copyright 2019 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_RUST_VARIABLES_H_
+#define TOOLS_GN_RUST_VARIABLES_H_
+
+#include "tools/gn/variables.h"
+
+namespace variables {
+
+// Rust target vars ------------------------------------------------------
+
+extern const char kRustAliasedDeps[];
+extern const char kRustAliasedDeps_HelpShort[];
+extern const char kRustAliasedDeps_Help[];
+
+extern const char kRustCrateName[];
+extern const char kRustCrateName_HelpShort[];
+extern const char kRustCrateName_Help[];
+
+extern const char kRustCrateType[];
+extern const char kRustCrateType_HelpShort[];
+extern const char kRustCrateType_Help[];
+
+extern const char kRustCrateRoot[];
+extern const char kRustCrateRoot_HelpShort[];
+extern const char kRustCrateRoot_Help[];
+
+extern const char kRustEdition[];
+extern const char kRustEdition_HelpShort[];
+extern const char kRustEdition_Help[];
+
+void InsertRustVariables(VariableInfoMap* info_map);
+
+} // namespace variables
+
+#endif // TOOLS_GN_RUST_VARIABLES_H_ \ No newline at end of file
diff --git a/gn/tools/gn/scheduler.cc b/gn/tools/gn/scheduler.cc
index 3e4eee98114..f4960b9bf63 100644
--- a/gn/tools/gn/scheduler.cc
+++ b/gn/tools/gn/scheduler.cc
@@ -16,14 +16,7 @@ Scheduler* g_scheduler = nullptr;
Scheduler::Scheduler()
: main_thread_run_loop_(MsgLoop::Current()),
- input_file_manager_(new InputFileManager),
- verbose_logging_(false),
- pool_work_count_lock_(),
- pool_work_count_cv_(),
- worker_pool_(),
- is_failed_(false),
- suppress_output_for_testing_(false),
- has_been_shutdown_(false) {
+ input_file_manager_(new InputFileManager) {
g_scheduler = this;
}
diff --git a/gn/tools/gn/scheduler.h b/gn/tools/gn/scheduler.h
index 160921e5d9c..d005df559c8 100644
--- a/gn/tools/gn/scheduler.h
+++ b/gn/tools/gn/scheduler.h
@@ -52,6 +52,8 @@ class Scheduler {
// Declares that the given file was read and affected the build output.
//
+ // Some consumers expect provided path to be absolute.kk
+ //
// TODO(brettw) this is global rather than per-BuildSettings. If we
// start using >1 build settings, then we probably want this to take a
// BuildSettings object so we know the depdency on a per-build basis.
@@ -113,7 +115,7 @@ class Scheduler {
scoped_refptr<InputFileManager> input_file_manager_;
- bool verbose_logging_;
+ bool verbose_logging_ = false;
base::AtomicRefCount work_count_;
@@ -130,14 +132,14 @@ class Scheduler {
WorkerPool worker_pool_;
mutable std::mutex lock_;
- bool is_failed_;
+ bool is_failed_ = false;
- bool suppress_output_for_testing_;
+ bool suppress_output_for_testing_ = false;
// Used to track whether the worker pool has been shutdown. This is necessary
// to clean up after tests that make a scheduler but don't run the message
// loop.
- bool has_been_shutdown_;
+ bool has_been_shutdown_ = false;
// Protected by the lock. See the corresponding Add/Get functions above.
std::vector<base::FilePath> gen_dependencies_;
diff --git a/gn/tools/gn/scope.h b/gn/tools/gn/scope.h
index 585f1513a87..6c26cdbece2 100644
--- a/gn/tools/gn/scope.h
+++ b/gn/tools/gn/scope.h
@@ -38,9 +38,9 @@ class Template;
// variables. So you should use a non-const containing scope whenever possible.
class Scope {
public:
- typedef std::map<base::StringPiece, Value> KeyValueMap;
+ using KeyValueMap = std::map<base::StringPiece, Value>;
// Holds an owning list of Items.
- typedef std::vector<std::unique_ptr<Item>> ItemVector;
+ using ItemVector = std::vector<std::unique_ptr<Item>>;
// A flag to indicate whether a function should recurse into nested scopes,
// or only operate on the current scope.
diff --git a/gn/tools/gn/settings.cc b/gn/tools/gn/settings.cc
index 737e72a57d5..c630fa72e0e 100644
--- a/gn/tools/gn/settings.cc
+++ b/gn/tools/gn/settings.cc
@@ -10,10 +10,7 @@
Settings::Settings(const BuildSettings* build_settings,
const std::string& output_subdir_name)
- : build_settings_(build_settings),
- import_manager_(),
- base_config_(this),
- greedy_target_generation_(false) {
+ : build_settings_(build_settings), base_config_(this) {
if (output_subdir_name.empty()) {
toolchain_output_dir_ = build_settings->build_dir();
} else {
@@ -30,5 +27,3 @@ Settings::Settings(const BuildSettings* build_settings,
if (!toolchain_output_dir_.is_null())
toolchain_gen_dir_ = SourceDir(toolchain_output_dir_.value() + "gen/");
}
-
-Settings::~Settings() = default;
diff --git a/gn/tools/gn/settings.h b/gn/tools/gn/settings.h
index f0a66915489..eb9952f68f6 100644
--- a/gn/tools/gn/settings.h
+++ b/gn/tools/gn/settings.h
@@ -35,7 +35,6 @@ class Settings {
// Otherwise, it must end in a slash.
Settings(const BuildSettings* build_settings,
const std::string& output_subdir_name);
- ~Settings();
const BuildSettings* build_settings() const { return build_settings_; }
@@ -108,7 +107,7 @@ class Settings {
Scope base_config_;
- bool greedy_target_generation_;
+ bool greedy_target_generation_ = false;
DISALLOW_COPY_AND_ASSIGN(Settings);
};
diff --git a/gn/tools/gn/setup.cc b/gn/tools/gn/setup.cc
index e461e26bafc..ef264317426 100644
--- a/gn/tools/gn/setup.cc
+++ b/gn/tools/gn/setup.cc
@@ -38,6 +38,7 @@
#if defined(OS_WIN)
#include <windows.h>
+
#include "base/win/scoped_process_information.h"
#endif
@@ -197,8 +198,7 @@ std::wstring SysMultiByteToWide(base::StringPiece mb) {
int mb_length = static_cast<int>(mb.length());
// Compute the length of the buffer.
- int charcount =
- MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
+ int charcount = MultiByteToWideChar(CP_ACP, 0, mb.data(), mb_length, NULL, 0);
if (charcount == 0)
return std::wstring();
@@ -297,12 +297,8 @@ Setup::Setup()
loader_(new LoaderImpl(&build_settings_)),
builder_(loader_.get()),
root_build_file_("//BUILD.gn"),
- check_public_headers_(false),
dotfile_settings_(&build_settings_, std::string()),
- dotfile_scope_(&dotfile_settings_),
- default_args_(nullptr),
- fill_arguments_(true),
- gen_empty_args_(false) {
+ dotfile_scope_(&dotfile_settings_) {
dotfile_settings_.set_toolchain_label(Label());
build_settings_.set_item_defined_callback(
@@ -314,8 +310,6 @@ Setup::Setup()
loader_->set_task_runner(scheduler_.task_runner());
}
-Setup::~Setup() = default;
-
bool Setup::DoSetup(const std::string& build_dir, bool force_create) {
return DoSetup(build_dir, force_create,
*base::CommandLine::ForCurrentProcess());
@@ -406,7 +400,7 @@ bool Setup::RunPostMessageLoop(const base::CommandLine& cmdline) {
OutputString(
"\nThe build continued as if that argument was "
"unspecified.\n\n");
- return true;
+ // Nonfatal error.
}
if (check_public_headers_) {
@@ -446,7 +440,7 @@ bool Setup::FillArguments(const base::CommandLine& cmdline) {
if (cmdline.HasSwitch(switches::kArgs) ||
(gen_empty_args_ && !PathExists(build_arg_file))) {
if (!FillArgsFromCommandLine(switch_value.empty() ? kDefaultArgsGn
- : switch_value)) {
+ : switch_value)) {
return false;
}
SaveArgsToFile();
@@ -538,7 +532,8 @@ bool Setup::SaveArgsToFile() {
base::CreateDirectory(build_arg_file.DirName());
std::string contents = args_input_file_->contents();
- commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive, &contents);
+ commands::FormatStringToString(contents, commands::TreeDumpMode::kInactive,
+ &contents);
#if defined(OS_WIN)
// Use Windows lineendings for this file since it will often open in
// Notepad which can't handle Unix ones.
diff --git a/gn/tools/gn/setup.h b/gn/tools/gn/setup.h
index cf0136a21c7..9aef0735ce1 100644
--- a/gn/tools/gn/setup.h
+++ b/gn/tools/gn/setup.h
@@ -34,7 +34,6 @@ extern const char kDotfile_Help[];
class Setup {
public:
Setup();
- ~Setup();
// Configures the build for the current command line. On success returns
// true. On failure, prints the error and returns false.
@@ -148,7 +147,7 @@ class Setup {
SourceFile root_build_file_;
- bool check_public_headers_;
+ bool check_public_headers_ = false;
// See getter for info.
std::unique_ptr<std::vector<LabelPattern>> check_patterns_;
@@ -168,14 +167,14 @@ class Setup {
// Default overrides, specified in the dotfile.
// Owned by the Value (if it exists) in the dotfile_scope_.
- const Scope* default_args_;
+ const Scope* default_args_ = nullptr;
// Set to true when we should populate the build arguments from the command
// line or build argument file. See setter above.
- bool fill_arguments_;
+ bool fill_arguments_ = true;
// Generate an empty args.gn file if it does not exists.
- bool gen_empty_args_;
+ bool gen_empty_args_ = false;
// State for invoking the command line args. We specifically want to keep
// this around for the entire run so that Values can blame to the command
diff --git a/gn/tools/gn/source_dir.cc b/gn/tools/gn/source_dir.cc
index 0fd5c75a88d..7def4b2ba0c 100644
--- a/gn/tools/gn/source_dir.cc
+++ b/gn/tools/gn/source_dir.cc
@@ -4,6 +4,8 @@
#include "tools/gn/source_dir.h"
+#include <string>
+
#include "base/logging.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/source_file.h"
@@ -57,23 +59,18 @@ bool ValidateResolveInput(bool as_file,
} // namespace
-SourceDir::SourceDir() = default;
-
-SourceDir::SourceDir(const base::StringPiece& p) : value_(p.data(), p.size()) {
+SourceDir::SourceDir(const std::string& s) : value_(s) {
if (!EndsWithSlash(value_))
value_.push_back('/');
AssertValueSourceDirString(value_);
}
-SourceDir::SourceDir(SwapIn, std::string* s) {
- value_.swap(*s);
+SourceDir::SourceDir(std::string&& s) : value_(std::move(s)) {
if (!EndsWithSlash(value_))
value_.push_back('/');
AssertValueSourceDirString(value_);
}
-SourceDir::~SourceDir() = default;
-
template <typename StringType>
std::string SourceDir::ResolveRelativeAs(
bool as_file,
@@ -98,10 +95,10 @@ SourceFile SourceDir::ResolveRelativeFile(
return ret;
const std::string& input_string = p.string_value();
- if (!ValidateResolveInput<std::string>(true, p, input_string, err)) {
+ if (!ValidateResolveInput<std::string>(true, p, input_string, err))
return ret;
- }
- ret.value_ = ResolveRelative(input_string, value_, true, source_root);
+
+ ret.SetValue(ResolveRelative(input_string, value_, true, source_root));
return ret;
}
@@ -137,11 +134,6 @@ base::FilePath SourceDir::Resolve(const base::FilePath& source_root) const {
return ResolvePath(value_, false, source_root);
}
-void SourceDir::SwapValue(std::string* v) {
- value_.swap(*v);
- AssertValueSourceDirString(value_);
-}
-
// Explicit template instantiation
template std::string SourceDir::ResolveRelativeAs(
bool as_file,
diff --git a/gn/tools/gn/source_dir.h b/gn/tools/gn/source_dir.h
index bfc260d514f..8b6dcc9e454 100644
--- a/gn/tools/gn/source_dir.h
+++ b/gn/tools/gn/source_dir.h
@@ -27,14 +27,10 @@ class Value;
// Two slashes at the beginning indicate a path relative to the source root.
class SourceDir {
public:
- enum SwapIn { SWAP_IN };
+ SourceDir() = default;
- SourceDir();
- explicit SourceDir(const base::StringPiece& p);
- // Swaps the given string in without copies. The given string will be empty
- // after this call.
- SourceDir(SwapIn, std::string* s);
- ~SourceDir();
+ SourceDir(const std::string& s);
+ explicit SourceDir(std::string&& s);
// Resolves a file or dir name (based on as_file parameter) relative
// to this source directory. Will return an empty string on error
@@ -137,13 +133,9 @@ class SourceDir {
bool operator!=(const SourceDir& other) const { return !operator==(other); }
bool operator<(const SourceDir& other) const { return value_ < other.value_; }
- void swap(SourceDir& other) { value_.swap(other.value_); }
-
private:
friend class SourceFile;
std::string value_;
-
- // Copy & assign supported.
};
namespace std {
@@ -158,8 +150,4 @@ struct hash<SourceDir> {
} // namespace std
-inline void swap(SourceDir& lhs, SourceDir& rhs) {
- lhs.swap(rhs);
-}
-
#endif // TOOLS_GN_SOURCE_DIR_H_
diff --git a/gn/tools/gn/source_file.cc b/gn/tools/gn/source_file.cc
index 79c6eb03913..ce24207e63e 100644
--- a/gn/tools/gn/source_file.cc
+++ b/gn/tools/gn/source_file.cc
@@ -21,26 +21,52 @@ void AssertValueSourceFileString(const std::string& s) {
DCHECK(!EndsWithSlash(s)) << s;
}
-} // namespace
+SourceFile::Type GetSourceFileType(const std::string& file) {
+ base::StringPiece extension = FindExtension(&file);
+ if (extension == "cc" || extension == "cpp" || extension == "cxx")
+ return SourceFile::SOURCE_CPP;
+ if (extension == "h" || extension == "hpp" || extension == "hxx" ||
+ extension == "hh" || extension == "inc" || extension == "ipp" ||
+ extension == "inl")
+ return SourceFile::SOURCE_H;
+ if (extension == "c")
+ return SourceFile::SOURCE_C;
+ if (extension == "m")
+ return SourceFile::SOURCE_M;
+ if (extension == "mm")
+ return SourceFile::SOURCE_MM;
+ if (extension == "rc")
+ return SourceFile::SOURCE_RC;
+ if (extension == "S" || extension == "s" || extension == "asm")
+ return SourceFile::SOURCE_S;
+ if (extension == "o" || extension == "obj")
+ return SourceFile::SOURCE_O;
+ if (extension == "def")
+ return SourceFile::SOURCE_DEF;
+ if (extension == "rs")
+ return SourceFile::SOURCE_RS;
+ if (extension == "go")
+ return SourceFile::SOURCE_GO;
-SourceFile::SourceFile() = default;
+ return SourceFile::SOURCE_UNKNOWN;
+}
-SourceFile::SourceFile(const base::StringPiece& p)
- : value_(p.data(), p.size()) {
+} // namespace
+
+SourceFile::SourceFile(const std::string& value) : value_(value) {
DCHECK(!value_.empty());
AssertValueSourceFileString(value_);
NormalizePath(&value_);
+ type_ = GetSourceFileType(value_);
}
-SourceFile::SourceFile(SwapIn, std::string* value) {
- value_.swap(*value);
+SourceFile::SourceFile(std::string&& value) : value_(std::move(value)) {
DCHECK(!value_.empty());
AssertValueSourceFileString(value_);
NormalizePath(&value_);
+ type_ = GetSourceFileType(value_);
}
-SourceFile::~SourceFile() = default;
-
std::string SourceFile::GetName() const {
if (is_null())
return std::string();
@@ -56,9 +82,41 @@ SourceDir SourceFile::GetDir() const {
DCHECK(value_.find('/') != std::string::npos);
size_t last_slash = value_.rfind('/');
- return SourceDir(base::StringPiece(&value_[0], last_slash + 1));
+ return SourceDir(value_.substr(0, last_slash + 1));
}
base::FilePath SourceFile::Resolve(const base::FilePath& source_root) const {
return ResolvePath(value_, true, source_root);
}
+
+void SourceFile::SetValue(const std::string& value) {
+ value_ = value;
+ type_ = GetSourceFileType(value_);
+}
+
+SourceFileTypeSet::SourceFileTypeSet() : empty_(true) {
+ memset(flags_, 0,
+ sizeof(bool) * static_cast<int>(SourceFile::SOURCE_NUMTYPES));
+}
+
+bool SourceFileTypeSet::CSourceUsed() const {
+ return empty_ || Get(SourceFile::SOURCE_CPP) || Get(SourceFile::SOURCE_H) ||
+ Get(SourceFile::SOURCE_C) || Get(SourceFile::SOURCE_M) ||
+ Get(SourceFile::SOURCE_MM) || Get(SourceFile::SOURCE_RC) ||
+ Get(SourceFile::SOURCE_S) || Get(SourceFile::SOURCE_O) ||
+ Get(SourceFile::SOURCE_DEF);
+}
+
+bool SourceFileTypeSet::RustSourceUsed() const {
+ return Get(SourceFile::SOURCE_RS);
+}
+
+bool SourceFileTypeSet::GoSourceUsed() const {
+ return Get(SourceFile::SOURCE_GO);
+}
+
+bool SourceFileTypeSet::MixedSourceUsed() const {
+ return (1 << static_cast<int>(CSourceUsed())
+ << static_cast<int>(RustSourceUsed())
+ << static_cast<int>(GoSourceUsed())) > 2;
+}
diff --git a/gn/tools/gn/source_file.h b/gn/tools/gn/source_file.h
index f033987560b..081cda61db6 100644
--- a/gn/tools/gn/source_file.h
+++ b/gn/tools/gn/source_file.h
@@ -20,22 +20,39 @@ class SourceDir;
// ends in one.
class SourceFile {
public:
- enum SwapIn { SWAP_IN };
-
- SourceFile();
+ // This should be sequential integers starting from 0 so they can be used as
+ // array indices.
+ enum Type {
+ SOURCE_UNKNOWN = 0,
+ SOURCE_ASM,
+ SOURCE_C,
+ SOURCE_CPP,
+ SOURCE_H,
+ SOURCE_M,
+ SOURCE_MM,
+ SOURCE_S,
+ SOURCE_RC,
+ SOURCE_O, // Object files can be inputs, too. Also counts .obj.
+ SOURCE_DEF,
+
+ SOURCE_RS,
+ SOURCE_GO,
+
+ // Must be last.
+ SOURCE_NUMTYPES,
+ };
+
+ SourceFile() = default;
// Takes a known absolute source file. Always begins in a slash.
- explicit SourceFile(const base::StringPiece& p);
- SourceFile(const SourceFile& other) = default;
-
- // Constructs from the given string by swapping in the contents of the given
- // value. The value will be the empty string after this call.
- SourceFile(SwapIn, std::string* value);
+ explicit SourceFile(const std::string& value);
+ explicit SourceFile(std::string&& value);
- ~SourceFile();
+ ~SourceFile() = default;
bool is_null() const { return value_.empty(); }
const std::string& value() const { return value_; }
+ Type type() const { return type_; }
// Returns everything after the last slash.
std::string GetName() const;
@@ -74,14 +91,13 @@ class SourceFile {
return value_ < other.value_;
}
- void swap(SourceFile& other) { value_.swap(other.value_); }
-
private:
friend class SourceDir;
- std::string value_;
+ void SetValue(const std::string& value);
- // Copy & assign supported.
+ std::string value_;
+ Type type_ = SOURCE_UNKNOWN;
};
namespace std {
@@ -96,8 +112,30 @@ struct hash<SourceFile> {
} // namespace std
-inline void swap(SourceFile& lhs, SourceFile& rhs) {
- lhs.swap(rhs);
-}
+// Represents a set of tool types.
+class SourceFileTypeSet {
+ public:
+ SourceFileTypeSet();
+
+ void Set(SourceFile::Type type) {
+ flags_[static_cast<int>(type)] = true;
+ empty_ = false;
+ }
+ bool Get(SourceFile::Type type) const {
+ return flags_[static_cast<int>(type)];
+ }
+
+ bool empty() const { return empty_; }
+
+ bool CSourceUsed() const;
+ bool RustSourceUsed() const;
+ bool GoSourceUsed() const;
+
+ bool MixedSourceUsed() const;
+
+ private:
+ bool empty_;
+ bool flags_[static_cast<int>(SourceFile::SOURCE_NUMTYPES)];
+};
#endif // TOOLS_GN_SOURCE_FILE_H_
diff --git a/gn/tools/gn/source_file_type.cc b/gn/tools/gn/source_file_type.cc
deleted file mode 100644
index 48df7f84e6c..00000000000
--- a/gn/tools/gn/source_file_type.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2014 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 "tools/gn/source_file_type.h"
-
-#include "tools/gn/filesystem_utils.h"
-#include "tools/gn/source_file.h"
-
-SourceFileType GetSourceFileType(const SourceFile& file) {
- base::StringPiece extension = FindExtension(&file.value());
- if (extension == "cc" || extension == "cpp" || extension == "cxx")
- return SOURCE_CPP;
- if (extension == "h" || extension == "hpp" || extension == "hxx" ||
- extension == "hh")
- return SOURCE_H;
- if (extension == "c")
- return SOURCE_C;
- if (extension == "m")
- return SOURCE_M;
- if (extension == "mm")
- return SOURCE_MM;
- if (extension == "rc")
- return SOURCE_RC;
- if (extension == "S" || extension == "s" || extension == "asm")
- return SOURCE_S;
- if (extension == "o" || extension == "obj")
- return SOURCE_O;
- if (extension == "def")
- return SOURCE_DEF;
-
- return SOURCE_UNKNOWN;
-}
diff --git a/gn/tools/gn/source_file_type.h b/gn/tools/gn/source_file_type.h
deleted file mode 100644
index c43b4324443..00000000000
--- a/gn/tools/gn/source_file_type.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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_SOURCE_FILE_TYPE_H_
-#define TOOLS_GN_SOURCE_FILE_TYPE_H_
-
-class SourceFile;
-
-// This should be sequential integers starting from 0 so they can be used as
-// array indices.
-enum SourceFileType {
- SOURCE_UNKNOWN = 0,
- SOURCE_ASM,
- SOURCE_C,
- SOURCE_CPP,
- SOURCE_H,
- SOURCE_M,
- SOURCE_MM,
- SOURCE_S,
- SOURCE_RC,
- SOURCE_O, // Object files can be inputs, too. Also counts .obj.
- SOURCE_DEF,
-
- // Must be last.
- SOURCE_NUMTYPES,
-};
-
-SourceFileType GetSourceFileType(const SourceFile& file);
-
-#endif // TOOLS_GN_SOURCE_FILE_TYPE_H_
diff --git a/gn/tools/gn/source_file_unittest.cc b/gn/tools/gn/source_file_unittest.cc
index 110707adea3..d26b35934fb 100644
--- a/gn/tools/gn/source_file_unittest.cc
+++ b/gn/tools/gn/source_file_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "tools/gn/source_file.h"
+
#include "util/test/test.h"
// The SourceFile object should normalize the input passed to the constructor.
@@ -13,7 +14,7 @@ TEST(SourceFile, Normalize) {
EXPECT_EQ("//bar.cc", a.value());
std::string b_str("//foo/././../bar.cc");
- SourceFile b(SourceFile::SwapIn(), &b_str);
+ SourceFile b(std::move(b_str));
EXPECT_TRUE(b_str.empty()); // Should have been swapped in.
EXPECT_EQ("//bar.cc", b.value());
}
diff --git a/gn/tools/gn/substitution_list.h b/gn/tools/gn/substitution_list.h
index 45123cbe502..e8614eb5200 100644
--- a/gn/tools/gn/substitution_list.h
+++ b/gn/tools/gn/substitution_list.h
@@ -31,7 +31,7 @@ class SubstitutionList {
// Returns a list of all substitution types used by the patterns in this
// list, with the exception of LITERAL.
- const std::vector<SubstitutionType>& required_types() const {
+ const std::vector<const Substitution*>& required_types() const {
return required_types_;
}
@@ -40,7 +40,7 @@ class SubstitutionList {
private:
std::vector<SubstitutionPattern> list_;
- std::vector<SubstitutionType> required_types_;
+ std::vector<const Substitution*> required_types_;
};
#endif // TOOLS_GN_SUBSTITUTION_LIST_H_
diff --git a/gn/tools/gn/substitution_pattern.cc b/gn/tools/gn/substitution_pattern.cc
index 9b5e815f940..b02532d1696 100644
--- a/gn/tools/gn/substitution_pattern.cc
+++ b/gn/tools/gn/substitution_pattern.cc
@@ -12,9 +12,9 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/value.h"
-SubstitutionPattern::Subrange::Subrange() : type(SUBSTITUTION_LITERAL) {}
+SubstitutionPattern::Subrange::Subrange() : type(&SubstitutionLiteral) {}
-SubstitutionPattern::Subrange::Subrange(SubstitutionType t,
+SubstitutionPattern::Subrange::Subrange(const Substitution* t,
const std::string& l)
: type(t), literal(l) {}
@@ -45,24 +45,25 @@ bool SubstitutionPattern::Parse(const std::string& str,
// Pick up everything from the previous spot to here as a literal.
if (next == std::string::npos) {
if (cur != str.size())
- ranges_.push_back(Subrange(SUBSTITUTION_LITERAL, str.substr(cur)));
+ ranges_.push_back(Subrange(&SubstitutionLiteral, str.substr(cur)));
break;
} else if (next > cur) {
ranges_.push_back(
- Subrange(SUBSTITUTION_LITERAL, str.substr(cur, next - cur)));
+ Subrange(&SubstitutionLiteral, str.substr(cur, next - cur)));
}
// Find which specific pattern this corresponds to.
bool found_match = false;
- for (size_t i = SUBSTITUTION_FIRST_PATTERN; i < SUBSTITUTION_NUM_TYPES;
- i++) {
- const char* cur_pattern = kSubstitutionNames[i];
- size_t cur_len = strlen(cur_pattern);
- if (str.compare(next, cur_len, cur_pattern) == 0) {
- ranges_.push_back(Subrange(static_cast<SubstitutionType>(i)));
- cur = next + cur_len;
- found_match = true;
- break;
+ for (const SubstitutionTypes* types : AllSubstitutions) {
+ for (const Substitution* sub : *types) {
+ const char* cur_pattern = sub->name;
+ size_t cur_len = strlen(cur_pattern);
+ if (str.compare(next, cur_len, cur_pattern) == 0) {
+ ranges_.push_back(Subrange(sub));
+ cur = next + cur_len;
+ found_match = true;
+ break;
+ }
}
}
@@ -99,18 +100,18 @@ SubstitutionPattern SubstitutionPattern::MakeForTest(const char* str) {
std::string SubstitutionPattern::AsString() const {
std::string result;
for (const auto& elem : ranges_) {
- if (elem.type == SUBSTITUTION_LITERAL)
+ if (elem.type == &SubstitutionLiteral)
result.append(elem.literal);
else
- result.append(kSubstitutionNames[elem.type]);
+ result.append(elem.type->name);
}
return result;
}
void SubstitutionPattern::FillRequiredTypes(SubstitutionBits* bits) const {
for (const auto& elem : ranges_) {
- if (elem.type != SUBSTITUTION_LITERAL)
- bits->used[static_cast<size_t>(elem.type)] = true;
+ if (elem.type != &SubstitutionLiteral)
+ bits->used.insert(elem.type);
}
}
@@ -121,7 +122,7 @@ bool SubstitutionPattern::IsInOutputDir(const BuildSettings* build_settings,
return false;
}
- if (ranges_[0].type == SUBSTITUTION_LITERAL) {
+ if (ranges_[0].type == &SubstitutionLiteral) {
// If the first thing is a literal, it must start with the output dir.
if (!EnsureStringIsInOutputDir(build_settings->build_dir(),
ranges_[0].literal, origin_, err))
diff --git a/gn/tools/gn/substitution_pattern.h b/gn/tools/gn/substitution_pattern.h
index 850d736280c..81bd89712a4 100644
--- a/gn/tools/gn/substitution_pattern.h
+++ b/gn/tools/gn/substitution_pattern.h
@@ -20,14 +20,14 @@ class SubstitutionPattern {
public:
struct Subrange {
Subrange();
- explicit Subrange(SubstitutionType t, const std::string& l = std::string());
+ explicit Subrange(const Substitution* t, const std::string& l = std::string());
~Subrange();
inline bool operator==(const Subrange& other) const {
return type == other.type && literal == other.literal;
}
- SubstitutionType type;
+ const Substitution* type;
// When type_ == LITERAL, this specifies the literal.
std::string literal;
@@ -50,7 +50,7 @@ class SubstitutionPattern {
std::string AsString() const;
// Sets the bits in the given vector corresponding to the substitutions used
- // by this pattern. SUBSTITUTION_LITERAL is ignored.
+ // by this pattern. SubstitutionLiteral is ignored.
void FillRequiredTypes(SubstitutionBits* bits) const;
// Checks whether this pattern resolves to something in the output directory
@@ -59,8 +59,8 @@ class SubstitutionPattern {
bool IsInOutputDir(const BuildSettings* build_settings, Err* err) const;
// Returns a vector listing the substitutions used by this pattern, not
- // counting SUBSTITUTION_LITERAL.
- const std::vector<SubstitutionType>& required_types() const {
+ // counting SubstitutionLiteral.
+ const std::vector<const Substitution*>& required_types() const {
return required_types_;
}
@@ -73,7 +73,7 @@ class SubstitutionPattern {
std::vector<Subrange> ranges_;
const ParseNode* origin_;
- std::vector<SubstitutionType> required_types_;
+ std::vector<const Substitution*> required_types_;
};
#endif // TOOLS_GN_SUBSTITUTION_PATTERN_H_
diff --git a/gn/tools/gn/substitution_pattern_unittest.cc b/gn/tools/gn/substitution_pattern_unittest.cc
index c8c739670de..1a6498f71a1 100644
--- a/gn/tools/gn/substitution_pattern_unittest.cc
+++ b/gn/tools/gn/substitution_pattern_unittest.cc
@@ -3,7 +3,9 @@
// found in the LICENSE file.
#include "tools/gn/substitution_pattern.h"
+
#include "tools/gn/err.h"
+#include "tools/gn/rust_substitution_type.h"
#include "util/test/test.h"
TEST(SubstitutionPattern, ParseLiteral) {
@@ -12,7 +14,7 @@ TEST(SubstitutionPattern, ParseLiteral) {
EXPECT_TRUE(pattern.Parse("This is a literal", nullptr, &err));
EXPECT_FALSE(err.has_error());
ASSERT_EQ(1u, pattern.ranges().size());
- EXPECT_EQ(SUBSTITUTION_LITERAL, pattern.ranges()[0].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
EXPECT_EQ("This is a literal", pattern.ranges()[0].literal);
}
@@ -24,13 +26,13 @@ TEST(SubstitutionPattern, ParseComplex) {
EXPECT_FALSE(err.has_error());
ASSERT_EQ(5u, pattern.ranges().size());
- EXPECT_EQ(SUBSTITUTION_LITERAL, pattern.ranges()[0].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
EXPECT_EQ("AA", pattern.ranges()[0].literal);
- EXPECT_EQ(SUBSTITUTION_SOURCE, pattern.ranges()[1].type);
- EXPECT_EQ(SUBSTITUTION_SOURCE_NAME_PART, pattern.ranges()[2].type);
- EXPECT_EQ(SUBSTITUTION_LITERAL, pattern.ranges()[3].type);
+ EXPECT_EQ(&SubstitutionSource, pattern.ranges()[1].type);
+ EXPECT_EQ(&SubstitutionSourceNamePart, pattern.ranges()[2].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
EXPECT_EQ("BB", pattern.ranges()[3].literal);
- EXPECT_EQ(SUBSTITUTION_SOURCE_FILE_PART, pattern.ranges()[4].type);
+ EXPECT_EQ(&SubstitutionSourceFilePart, pattern.ranges()[4].type);
}
TEST(SubstitutionPattern, ParseErrors) {
@@ -47,3 +49,25 @@ TEST(SubstitutionPattern, ParseErrors) {
EXPECT_FALSE(pattern.Parse("{{source{{source}}", nullptr, &err));
EXPECT_TRUE(err.has_error());
}
+
+TEST(SubstitutionPattern, ParseRust) {
+ SubstitutionPattern pattern;
+ Err err;
+ EXPECT_TRUE(pattern.Parse(
+ "AA{{rustflags}}{{rustenv}}BB{{crate_name}}{{rustdeps}}CC{{externs}}",
+ nullptr, &err));
+ EXPECT_FALSE(err.has_error());
+ ASSERT_EQ(8u, pattern.ranges().size());
+
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[0].type);
+ EXPECT_EQ("AA", pattern.ranges()[0].literal);
+ EXPECT_EQ(&kRustSubstitutionRustFlags, pattern.ranges()[1].type);
+ EXPECT_EQ(&kRustSubstitutionRustEnv, pattern.ranges()[2].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[3].type);
+ EXPECT_EQ("BB", pattern.ranges()[3].literal);
+ EXPECT_EQ(&kRustSubstitutionCrateName, pattern.ranges()[4].type);
+ EXPECT_EQ(&kRustSubstitutionRustDeps, pattern.ranges()[5].type);
+ EXPECT_EQ(&SubstitutionLiteral, pattern.ranges()[6].type);
+ EXPECT_EQ("CC", pattern.ranges()[6].literal);
+ EXPECT_EQ(&kRustSubstitutionExterns, pattern.ranges()[7].type);
+} \ No newline at end of file
diff --git a/gn/tools/gn/substitution_type.cc b/gn/tools/gn/substitution_type.cc
index d47a8d0fdc6..2f6e23e60a1 100644
--- a/gn/tools/gn/substitution_type.cc
+++ b/gn/tools/gn/substitution_type.cc
@@ -7,234 +7,183 @@
#include <stddef.h>
#include <stdlib.h>
+#include "tools/gn/c_substitution_type.h"
#include "tools/gn/err.h"
-
-const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES] = {
- "<<literal>>", // SUBSTITUTION_LITERAL
-
- "{{source}}", // SUBSTITUTION_SOURCE
- "{{output}}", // SUBSTITUTION_OUTPUT
-
- "{{source_name_part}}", // SUBSTITUTION_NAME_PART
- "{{source_file_part}}", // SUBSTITUTION_FILE_PART
- "{{source_dir}}", // SUBSTITUTION_SOURCE_DIR
- "{{source_root_relative_dir}}", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
- "{{source_gen_dir}}", // SUBSTITUTION_SOURCE_GEN_DIR
- "{{source_out_dir}}", // SUBSTITUTION_SOURCE_OUT_DIR
- "{{source_target_relative}}", // SUBSTITUTION_SOURCE_TARGET_RELATIVE
-
- "{{label}}", // SUBSTITUTION_LABEL
- "{{label_name}}", // SUBSTITUTION_LABEL_NAME
- "{{root_gen_dir}}", // SUBSTITUTION_ROOT_GEN_DIR
- "{{root_out_dir}}", // SUBSTITUTION_ROOT_OUT_DIR
- "{{target_gen_dir}}", // SUBSTITUTION_TARGET_GEN_DIR
- "{{target_out_dir}}", // SUBSTITUTION_TARGET_OUT_DIR
- "{{target_output_name}}", // SUBSTITUTION_TARGET_OUTPUT_NAME
-
- "{{asmflags}}", // SUBSTITUTION_ASMFLAGS
- "{{cflags}}", // SUBSTITUTION_CFLAGS
- "{{cflags_c}}", // SUBSTITUTION_CFLAGS_C
- "{{cflags_cc}}", // SUBSTITUTION_CFLAGS_CC
- "{{cflags_objc}}", // SUBSTITUTION_CFLAGS_OBJC
- "{{cflags_objcc}}", // SUBSTITUTION_CFLAGS_OBJCC
- "{{defines}}", // SUBSTITUTION_DEFINES
- "{{include_dirs}}", // SUBSTITUTION_INCLUDE_DIRS
-
- "{{inputs}}", // SUBSTITUTION_LINKER_INPUTS
- "{{inputs_newline}}", // SUBSTITUTION_LINKER_INPUTS_NEWLINE
- "{{ldflags}}", // SUBSTITUTION_LDFLAGS
- "{{libs}}", // SUBSTITUTION_LIBS
- "{{output_dir}}", // SUBSTITUTION_OUTPUT_DIR
- "{{output_extension}}", // SUBSTITUTION_OUTPUT_EXTENSION
- "{{solibs}}", // SUBSTITUTION_SOLIBS
-
- "{{arflags}}", // SUBSTITUTION_ARFLAGS
-
- "{{bundle_root_dir}}", // SUBSTITUTION_BUNDLE_ROOT_DIR
- "{{bundle_contents_dir}}", // SUBSTITUTION_BUNDLE_CONTENTS_DIR
- "{{bundle_resources_dir}}", // SUBSTITUTION_BUNDLE_RESOURCES_DIR
- "{{bundle_executable_dir}}", // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
- "{{bundle_product_type}}", // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
- "{{bundle_partial_info_plist}}", // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST,
-
- "{{response_file_name}}", // SUBSTITUTION_RSP_FILE_NAME
-};
-
-const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES] = {
- nullptr, // SUBSTITUTION_LITERAL
-
- "in", // SUBSTITUTION_SOURCE
- "out", // SUBSTITUTION_OUTPUT
-
- "source_name_part", // SUBSTITUTION_NAME_PART
- "source_file_part", // SUBSTITUTION_FILE_PART
- "source_dir", // SUBSTITUTION_SOURCE_DIR
- "source_root_relative_dir", // SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR
- "source_gen_dir", // SUBSTITUTION_SOURCE_GEN_DIR
- "source_out_dir", // SUBSTITUTION_SOURCE_OUT_DIR
- "source_target_relative", // SUBSTITUTION_SOURCE_TARGET_RELATIVE
-
- "label", // SUBSTITUTION_LABEL
- "label_name", // SUBSTITUTION_LABEL_NAME
- "root_gen_dir", // SUBSTITUTION_ROOT_GEN_DIR
- "root_out_dir", // SUBSTITUTION_ROOT_OUT_DIR
- "target_gen_dir", // SUBSTITUTION_TARGET_GEN_DIR
- "target_out_dir", // SUBSTITUTION_TARGET_OUT_DIR
- "target_output_name", // SUBSTITUTION_TARGET_OUTPUT_NAME
-
- "asmflags", // SUBSTITUTION_ASMFLAGS
- "cflags", // SUBSTITUTION_CFLAGS
- "cflags_c", // SUBSTITUTION_CFLAGS_C
- "cflags_cc", // SUBSTITUTION_CFLAGS_CC
- "cflags_objc", // SUBSTITUTION_CFLAGS_OBJC
- "cflags_objcc", // SUBSTITUTION_CFLAGS_OBJCC
- "defines", // SUBSTITUTION_DEFINES
- "include_dirs", // SUBSTITUTION_INCLUDE_DIRS
-
- // LINKER_INPUTS expands to the same Ninja var as SUBSTITUTION_SOURCE. These
- // are used in different contexts and are named differently to keep things
- // clear, but they both expand to the "set of input files" for a build rule.
- "in", // SUBSTITUTION_LINKER_INPUTS
- "in_newline", // SUBSTITUTION_LINKER_INPUTS_NEWLINE
- "ldflags", // SUBSTITUTION_LDFLAGS
- "libs", // SUBSTITUTION_LIBS
- "output_dir", // SUBSTITUTION_OUTPUT_DIR
- "output_extension", // SUBSTITUTION_OUTPUT_EXTENSION
- "solibs", // SUBSTITUTION_SOLIBS
-
- "arflags", // SUBSTITUTION_ARFLAGS
-
- "bundle_root_dir", // SUBSTITUTION_BUNDLE_ROOT_DIR
- "bundle_contents_dir", // SUBSTITUTION_BUNDLE_CONTENTS_DIR
- "bundle_resources_dir", // SUBSTITUTION_BUNDLE_RESOURCES_DIR
- "bundle_executable_dir", // SUBSTITUTION_BUNDLE_EXECUTABLE_DIR
- "product_type", // SUBSTITUTION_BUNDLE_PRODUCT_TYPE
- "partial_info_plist", // SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST
-
- "rspfile", // SUBSTITUTION_RSP_FILE_NAME
+#include "tools/gn/rust_substitution_type.h"
+
+const std::vector<SubstitutionTypes*> AllSubstitutions = {
+ &GeneralSubstitutions, &CSubstitutions, &RustSubstitutions};
+
+const SubstitutionTypes GeneralSubstitutions = {
+ &SubstitutionLiteral,
+
+ &SubstitutionOutput,
+ &SubstitutionLabel,
+ &SubstitutionLabelName,
+ &SubstitutionRootGenDir,
+ &SubstitutionRootOutDir,
+ &SubstitutionOutputDir,
+ &SubstitutionTargetGenDir,
+ &SubstitutionTargetOutDir,
+ &SubstitutionTargetOutputName,
+
+ &SubstitutionSource,
+ &SubstitutionSourceNamePart,
+ &SubstitutionSourceFilePart,
+ &SubstitutionSourceDir,
+ &SubstitutionSourceRootRelativeDir,
+ &SubstitutionSourceGenDir,
+ &SubstitutionSourceOutDir,
+ &SubstitutionSourceTargetRelative,
+
+ &SubstitutionBundleRootDir,
+ &SubstitutionBundleContentsDir,
+ &SubstitutionBundleResourcesDir,
+ &SubstitutionBundleExecutableDir,
+
+ &SubstitutionBundleProductType,
+ &SubstitutionBundlePartialInfoPlist,
+
+ &SubstitutionRspFileName,
};
-SubstitutionBits::SubstitutionBits() : used() {}
+const Substitution SubstitutionLiteral = {"<<literal>>", nullptr};
+
+const Substitution SubstitutionSource = {"{{source}}", "in"};
+const Substitution SubstitutionOutput = {"{{output}}", "out"};
+
+const Substitution SubstitutionSourceNamePart = {"{{source_name_part}}",
+ "source_name_part"};
+const Substitution SubstitutionSourceFilePart = {"{{source_file_part}}",
+ "source_file_part"};
+const Substitution SubstitutionSourceDir = {"{{source_dir}}", "source_dir"};
+const Substitution SubstitutionSourceRootRelativeDir = {
+ "{{source_root_relative_dir}}", "source_root_relative_dir"};
+const Substitution SubstitutionSourceGenDir = {"{{source_gen_dir}}",
+ "source_gen_dir"};
+const Substitution SubstitutionSourceOutDir = {"{{source_out_dir}}",
+ "source_out_dir"};
+const Substitution SubstitutionSourceTargetRelative = {
+ "{{source_target_relative}}", "source_target_relative"};
+
+// Valid for all compiler and linker tools. These depend on the target and
+// do not vary on a per-file basis.
+const Substitution SubstitutionLabel = {"{{label}}", "label"};
+const Substitution SubstitutionLabelName = {"{{label_name}}", "label_name"};
+const Substitution SubstitutionRootGenDir = {"{{root_gen_dir}}",
+ "root_gen_dir"};
+const Substitution SubstitutionRootOutDir = {"{{root_out_dir}}",
+ "root_out_dir"};
+const Substitution SubstitutionOutputDir = {"{{output_dir}}", "output_dir"};
+const Substitution SubstitutionTargetGenDir = {"{{target_gen_dir}}",
+ "target_gen_dir"};
+const Substitution SubstitutionTargetOutDir = {"{{target_out_dir}}",
+ "target_out_dir"};
+const Substitution SubstitutionTargetOutputName = {"{{target_output_name}}",
+ "target_output_name"};
+
+// Valid for bundle_data targets.
+const Substitution SubstitutionBundleRootDir = {"{{bundle_root_dir}}",
+ "bundle_root_dir"};
+const Substitution SubstitutionBundleContentsDir = {"{{bundle_contents_dir}}",
+ "bundle_contents_dir"};
+const Substitution SubstitutionBundleResourcesDir = {"{{bundle_resources_dir}}",
+ "bundle_resources_dir"};
+const Substitution SubstitutionBundleExecutableDir = {
+ "{{bundle_executable_dir}}", "bundle_executable_dir"};
+
+// Valid for compile_xcassets tool.
+const Substitution SubstitutionBundleProductType = {"{{bundle_product_type}}",
+ "product_type"};
+const Substitution SubstitutionBundlePartialInfoPlist = {
+ "{{bundle_partial_info_plist}}", "partial_info_plist"};
+
+// Used only for the args of actions.
+const Substitution SubstitutionRspFileName = {"{{response_file_name}}",
+ "rspfile"};
+
+SubstitutionBits::SubstitutionBits() = default;
void SubstitutionBits::MergeFrom(const SubstitutionBits& other) {
- for (size_t i = 0; i < SUBSTITUTION_NUM_TYPES; i++)
- used[i] |= other.used[i];
+ for (const Substitution* s : other.used)
+ used.insert(s);
}
-void SubstitutionBits::FillVector(std::vector<SubstitutionType>* vect) const {
- for (size_t i = SUBSTITUTION_FIRST_PATTERN; i < SUBSTITUTION_NUM_TYPES; i++) {
- if (used[i])
- vect->push_back(static_cast<SubstitutionType>(i));
+void SubstitutionBits::FillVector(
+ std::vector<const Substitution*>* vect) const {
+ for (const Substitution* s : used) {
+ vect->push_back(s);
}
}
-bool SubstitutionIsInOutputDir(SubstitutionType type) {
- return type == SUBSTITUTION_SOURCE_GEN_DIR ||
- type == SUBSTITUTION_SOURCE_OUT_DIR ||
- type == SUBSTITUTION_ROOT_GEN_DIR ||
- type == SUBSTITUTION_ROOT_OUT_DIR ||
- type == SUBSTITUTION_TARGET_GEN_DIR ||
- type == SUBSTITUTION_TARGET_OUT_DIR;
-}
-
-bool SubstitutionIsInBundleDir(SubstitutionType type) {
- return type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
- type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
- type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
- type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR;
-}
-
-bool IsValidBundleDataSubstitution(SubstitutionType type) {
- return type == SUBSTITUTION_LITERAL ||
- type == SUBSTITUTION_SOURCE_NAME_PART ||
- type == SUBSTITUTION_SOURCE_FILE_PART ||
- type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
- type == SUBSTITUTION_BUNDLE_ROOT_DIR ||
- type == SUBSTITUTION_BUNDLE_CONTENTS_DIR ||
- type == SUBSTITUTION_BUNDLE_RESOURCES_DIR ||
- type == SUBSTITUTION_BUNDLE_EXECUTABLE_DIR;
-}
-
-bool IsValidSourceSubstitution(SubstitutionType type) {
- return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_SOURCE ||
- type == SUBSTITUTION_SOURCE_NAME_PART ||
- type == SUBSTITUTION_SOURCE_FILE_PART ||
- type == SUBSTITUTION_SOURCE_DIR ||
- type == SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR ||
- type == SUBSTITUTION_SOURCE_GEN_DIR ||
- type == SUBSTITUTION_SOURCE_OUT_DIR ||
- type == SUBSTITUTION_SOURCE_TARGET_RELATIVE;
-}
-
-bool IsValidScriptArgsSubstitution(SubstitutionType type) {
- return IsValidSourceSubstitution(type) || type == SUBSTITUTION_RSP_FILE_NAME;
-}
-
-bool IsValidToolSubstitution(SubstitutionType type) {
- return type == SUBSTITUTION_LITERAL || type == SUBSTITUTION_OUTPUT ||
- type == SUBSTITUTION_LABEL || type == SUBSTITUTION_LABEL_NAME ||
- type == SUBSTITUTION_ROOT_GEN_DIR ||
- type == SUBSTITUTION_ROOT_OUT_DIR ||
- type == SUBSTITUTION_TARGET_GEN_DIR ||
- type == SUBSTITUTION_TARGET_OUT_DIR ||
- type == SUBSTITUTION_TARGET_OUTPUT_NAME;
+bool SubstitutionIsInOutputDir(const Substitution* type) {
+ return type == &SubstitutionSourceGenDir ||
+ type == &SubstitutionSourceOutDir || type == &SubstitutionRootGenDir ||
+ type == &SubstitutionRootOutDir || type == &SubstitutionTargetGenDir ||
+ type == &SubstitutionTargetOutDir;
}
-bool IsValidCompilerSubstitution(SubstitutionType type) {
- return IsValidToolSubstitution(type) || IsValidSourceSubstitution(type) ||
- type == SUBSTITUTION_SOURCE || type == SUBSTITUTION_ASMFLAGS ||
- type == SUBSTITUTION_CFLAGS || type == SUBSTITUTION_CFLAGS_C ||
- type == SUBSTITUTION_CFLAGS_CC || type == SUBSTITUTION_CFLAGS_OBJC ||
- type == SUBSTITUTION_CFLAGS_OBJCC || type == SUBSTITUTION_DEFINES ||
- type == SUBSTITUTION_INCLUDE_DIRS;
+bool SubstitutionIsInBundleDir(const Substitution* type) {
+ return type == &SubstitutionBundleRootDir ||
+ type == &SubstitutionBundleContentsDir ||
+ type == &SubstitutionBundleResourcesDir ||
+ type == &SubstitutionBundleExecutableDir;
}
-bool IsValidCompilerOutputsSubstitution(SubstitutionType type) {
- // All tool types except "output" (which would be infinitely recursive).
- return (IsValidToolSubstitution(type) && type != SUBSTITUTION_OUTPUT) ||
- IsValidSourceSubstitution(type);
+bool IsValidBundleDataSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral ||
+ type == &SubstitutionSourceTargetRelative ||
+ type == &SubstitutionSourceNamePart ||
+ type == &SubstitutionSourceFilePart ||
+ type == &SubstitutionSourceRootRelativeDir ||
+ type == &SubstitutionBundleRootDir ||
+ type == &SubstitutionBundleContentsDir ||
+ type == &SubstitutionBundleResourcesDir ||
+ type == &SubstitutionBundleExecutableDir;
}
-bool IsValidLinkerSubstitution(SubstitutionType type) {
- return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
- type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
- type == SUBSTITUTION_LDFLAGS || type == SUBSTITUTION_LIBS ||
- type == SUBSTITUTION_OUTPUT_DIR ||
- type == SUBSTITUTION_OUTPUT_EXTENSION || type == SUBSTITUTION_SOLIBS;
+bool IsValidSourceSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral || type == &SubstitutionSource ||
+ type == &SubstitutionSourceNamePart ||
+ type == &SubstitutionSourceFilePart ||
+ type == &SubstitutionSourceDir ||
+ type == &SubstitutionSourceRootRelativeDir ||
+ type == &SubstitutionSourceGenDir ||
+ type == &SubstitutionSourceOutDir ||
+ type == &SubstitutionSourceTargetRelative;
}
-bool IsValidLinkerOutputsSubstitution(SubstitutionType type) {
- // All valid compiler outputs plus the output extension.
- return IsValidCompilerOutputsSubstitution(type) ||
- type == SUBSTITUTION_OUTPUT_DIR ||
- type == SUBSTITUTION_OUTPUT_EXTENSION;
+bool IsValidScriptArgsSubstitution(const Substitution* type) {
+ return IsValidSourceSubstitution(type) || type == &SubstitutionRspFileName;
}
-bool IsValidALinkSubstitution(SubstitutionType type) {
- return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
- type == SUBSTITUTION_LINKER_INPUTS_NEWLINE ||
- type == SUBSTITUTION_ARFLAGS || type == SUBSTITUTION_OUTPUT_DIR ||
- type == SUBSTITUTION_OUTPUT_EXTENSION;
+bool IsValidToolSubstitution(const Substitution* type) {
+ return type == &SubstitutionLiteral || type == &SubstitutionOutput ||
+ type == &SubstitutionLabel || type == &SubstitutionLabelName ||
+ type == &SubstitutionRootGenDir || type == &SubstitutionRootOutDir ||
+ type == &SubstitutionTargetGenDir ||
+ type == &SubstitutionTargetOutDir ||
+ type == &SubstitutionTargetOutputName;
}
-bool IsValidCopySubstitution(SubstitutionType type) {
- return IsValidToolSubstitution(type) || type == SUBSTITUTION_SOURCE;
+bool IsValidCopySubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &SubstitutionSource;
}
-bool IsValidCompileXCassetsSubstitution(SubstitutionType type) {
- return IsValidToolSubstitution(type) || type == SUBSTITUTION_LINKER_INPUTS ||
- type == SUBSTITUTION_BUNDLE_PRODUCT_TYPE ||
- type == SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST;
+bool IsValidCompileXCassetsSubstitution(const Substitution* type) {
+ return IsValidToolSubstitution(type) || type == &CSubstitutionLinkerInputs ||
+ type == &SubstitutionBundleProductType ||
+ type == &SubstitutionBundlePartialInfoPlist;
}
-bool EnsureValidSubstitutions(const std::vector<SubstitutionType>& types,
- bool (*is_valid_subst)(SubstitutionType),
+bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
+ bool (*is_valid_subst)(const Substitution*),
const ParseNode* origin,
Err* err) {
- for (SubstitutionType type : types) {
+ for (const Substitution* type : types) {
if (!is_valid_subst(type)) {
*err = Err(origin, "Invalid substitution type.",
- "The substitution " + std::string(kSubstitutionNames[type]) +
+ "The substitution " + std::string(type->name) +
" isn't valid for something\n"
"operating on a source file such as this.");
return false;
diff --git a/gn/tools/gn/substitution_type.h b/gn/tools/gn/substitution_type.h
index 9e2bdb728b9..45e890c00eb 100644
--- a/gn/tools/gn/substitution_type.h
+++ b/gn/tools/gn/substitution_type.h
@@ -7,87 +7,65 @@
#include <vector>
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+
class Err;
class ParseNode;
-// Keep kSubstitutionNames, kSubstitutionNinjaNames and the
-// IsValid*Substitution functions in sync if you change anything here.
-enum SubstitutionType {
- SUBSTITUTION_LITERAL = 0,
-
- // The index of the first pattern. To loop overal all patterns, go from here
- // until NUM_TYPES.
- SUBSTITUTION_FIRST_PATTERN,
-
- // These map to Ninja's {in} and {out} variables.
- SUBSTITUTION_SOURCE = SUBSTITUTION_FIRST_PATTERN, // {{source}}
- SUBSTITUTION_OUTPUT, // {{output}}
-
- // Valid for all compiler tools.
- SUBSTITUTION_SOURCE_NAME_PART, // {{source_name_part}}
- SUBSTITUTION_SOURCE_FILE_PART, // {{source_file_part}}
- SUBSTITUTION_SOURCE_DIR, // {{source_dir}}
- SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR, // {{root_relative_dir}}
- SUBSTITUTION_SOURCE_GEN_DIR, // {{source_gen_dir}}
- SUBSTITUTION_SOURCE_OUT_DIR, // {{source_out_dir}}
- SUBSTITUTION_SOURCE_TARGET_RELATIVE, // {{source_target_relative}}
-
- // Valid for all compiler and linker tools. These depend on the target and
- // do not vary on a per-file basis.
- SUBSTITUTION_LABEL, // {{label}}
- SUBSTITUTION_LABEL_NAME, // {{label_name}}
- SUBSTITUTION_ROOT_GEN_DIR, // {{root_gen_dir}}
- SUBSTITUTION_ROOT_OUT_DIR, // {{root_out_dir}}
- SUBSTITUTION_TARGET_GEN_DIR, // {{target_gen_dir}}
- SUBSTITUTION_TARGET_OUT_DIR, // {{target_out_dir}}
- SUBSTITUTION_TARGET_OUTPUT_NAME, // {{target_output_name}}
-
- // Valid for compiler tools.
- SUBSTITUTION_ASMFLAGS, // {{asmflags}}
- SUBSTITUTION_CFLAGS, // {{cflags}}
- SUBSTITUTION_CFLAGS_C, // {{cflags_c}}
- SUBSTITUTION_CFLAGS_CC, // {{cflags_cc}}
- SUBSTITUTION_CFLAGS_OBJC, // {{cflags_objc}}
- SUBSTITUTION_CFLAGS_OBJCC, // {{cflags_objcc}}
- SUBSTITUTION_DEFINES, // {{defines}}
- SUBSTITUTION_INCLUDE_DIRS, // {{include_dirs}}
-
- // Valid for linker tools.
- SUBSTITUTION_LINKER_INPUTS, // {{inputs}}
- SUBSTITUTION_LINKER_INPUTS_NEWLINE, // {{inputs_newline}}
- SUBSTITUTION_LDFLAGS, // {{ldflags}}
- SUBSTITUTION_LIBS, // {{libs}}
- SUBSTITUTION_OUTPUT_DIR, // {{output_dir}}
- SUBSTITUTION_OUTPUT_EXTENSION, // {{output_extension}}
- SUBSTITUTION_SOLIBS, // {{solibs}}
-
- // Valid for alink only.
- SUBSTITUTION_ARFLAGS, // {{arflags}}
-
- // Valid for bundle_data targets.
- SUBSTITUTION_BUNDLE_ROOT_DIR, // {{bundle_root_dir}}
- SUBSTITUTION_BUNDLE_CONTENTS_DIR, // {{bundle_contents_dir}}
- SUBSTITUTION_BUNDLE_RESOURCES_DIR, // {{bundle_resources_dir}}
- SUBSTITUTION_BUNDLE_EXECUTABLE_DIR, // {{bundle_executable_dir}}
-
- // Valid for compile_xcassets tool.
- SUBSTITUTION_BUNDLE_PRODUCT_TYPE, // {{bundle_product_type}}
- SUBSTITUTION_BUNDLE_PARTIAL_INFO_PLIST, // {{bundle_partial_info_plist}}
-
- // Used only for the args of actions.
- SUBSTITUTION_RSP_FILE_NAME, // {{response_file_name}}
-
- SUBSTITUTION_NUM_TYPES // Must be last.
+// Each pair here represents the string representation of the substitution in GN
+// and in Ninja.
+struct Substitution {
+ const char* name;
+ const char* ninja_name;
+ DISALLOW_COPY_AND_ASSIGN(Substitution);
};
-// An array of size SUBSTITUTION_NUM_TYPES that lists the names of the
-// substitution patterns, including the curly braces. So, for example,
-// kSubstitutionNames[SUBSTITUTION_SOURCE] == "{{source}}".
-extern const char* kSubstitutionNames[SUBSTITUTION_NUM_TYPES];
-
-// Ninja variables corresponding to each substitution. These do not include
-// the dollar sign.
-extern const char* kSubstitutionNinjaNames[SUBSTITUTION_NUM_TYPES];
+using SubstitutionTypes = const std::vector<const Substitution*>;
+
+// All possible substitutions, organized into logical sets.
+extern const std::vector<SubstitutionTypes*> AllSubstitutions;
+
+// The set of substitutions available to all tools.
+extern const SubstitutionTypes GeneralSubstitutions;
+
+// Types of substitutions.
+extern const Substitution SubstitutionLiteral;
+
+// Valid for all tools. These depend on the target and
+// do not vary on a per-file basis.
+extern const Substitution SubstitutionOutput;
+extern const Substitution SubstitutionLabel;
+extern const Substitution SubstitutionLabelName;
+extern const Substitution SubstitutionRootGenDir;
+extern const Substitution SubstitutionRootOutDir;
+extern const Substitution SubstitutionOutputDir;
+extern const Substitution SubstitutionTargetGenDir;
+extern const Substitution SubstitutionTargetOutDir;
+extern const Substitution SubstitutionTargetOutputName;
+
+// Valid for all compiler tools.
+extern const Substitution SubstitutionSource;
+extern const Substitution SubstitutionSourceNamePart;
+extern const Substitution SubstitutionSourceFilePart;
+extern const Substitution SubstitutionSourceDir;
+extern const Substitution SubstitutionSourceRootRelativeDir;
+extern const Substitution SubstitutionSourceGenDir;
+extern const Substitution SubstitutionSourceOutDir;
+extern const Substitution SubstitutionSourceTargetRelative;
+
+// Valid for bundle_data targets.
+extern const Substitution SubstitutionBundleRootDir;
+extern const Substitution SubstitutionBundleContentsDir;
+extern const Substitution SubstitutionBundleResourcesDir;
+extern const Substitution SubstitutionBundleExecutableDir;
+
+// Valid for compile_xcassets tool.
+extern const Substitution SubstitutionBundleProductType;
+extern const Substitution SubstitutionBundlePartialInfoPlist;
+
+// Used only for the args of actions.
+extern const Substitution SubstitutionRspFileName;
// A wrapper around an array if flags indicating whether a given substitution
// type is required in some context. By convention, the LITERAL type bit is
@@ -99,43 +77,40 @@ struct SubstitutionBits {
// then be the union of all bits in the two lists.
void MergeFrom(const SubstitutionBits& other);
- // Converts the substitution type bitfield (with a true set for each required
- // item) to a vector of the types listed. Does not include LITERAL.
- void FillVector(std::vector<SubstitutionType>* vect) const;
+ // Converts the substitution type set to a vector of the types listed. Does
+ // not include SubstitutionLiteral.
+ void FillVector(std::vector<const Substitution*>* vect) const;
- bool used[SUBSTITUTION_NUM_TYPES];
+ // This set depends on global uniqueness of pointers, and so all points in
+ // this set should be the Substitution* constants.
+ base::flat_set<const Substitution*> used;
};
// Returns true if the given substitution pattern references the output
// directory. This is used to check strings that begin with a substitution to
// verify that they produce a file in the output directory.
-bool SubstitutionIsInOutputDir(SubstitutionType type);
+bool SubstitutionIsInOutputDir(const Substitution* type);
// Returns true if the given substitution pattern references the bundle
// directory. This is used to check strings that begin with a substitution to
// verify that they produce a file in the bundle directory.
-bool SubstitutionIsInBundleDir(SubstitutionType type);
+bool SubstitutionIsInBundleDir(const Substitution* type);
// Returns true if the given substitution is valid for the named purpose.
-bool IsValidBundleDataSubstitution(SubstitutionType type);
-bool IsValidSourceSubstitution(SubstitutionType type);
-bool IsValidScriptArgsSubstitution(SubstitutionType type);
+bool IsValidBundleDataSubstitution(const Substitution* type);
+bool IsValidSourceSubstitution(const Substitution* type);
+bool IsValidScriptArgsSubstitution(const Substitution* type);
// Both compiler and linker tools.
-bool IsValidToolSubstitution(SubstitutionType type);
-bool IsValidCompilerSubstitution(SubstitutionType type);
-bool IsValidCompilerOutputsSubstitution(SubstitutionType type);
-bool IsValidLinkerSubstitution(SubstitutionType type);
-bool IsValidLinkerOutputsSubstitution(SubstitutionType type);
-bool IsValidALinkSubstitution(SubstitutionType type);
-bool IsValidCopySubstitution(SubstitutionType type);
-bool IsValidCompileXCassetsSubstitution(SubstitutionType type);
+bool IsValidToolSubstitution(const Substitution* type);
+bool IsValidCopySubstitution(const Substitution* type);
+bool IsValidCompileXCassetsSubstitution(const Substitution* type);
// Validates that each substitution type in the vector passes the given
// is_valid_subst predicate. Returns true on success. On failure, fills in the
// error object with an appropriate message and returns false.
-bool EnsureValidSubstitutions(const std::vector<SubstitutionType>& types,
- bool (*is_valid_subst)(SubstitutionType),
+bool EnsureValidSubstitutions(const std::vector<const Substitution*>& types,
+ bool (*is_valid_subst)(const Substitution*),
const ParseNode* origin,
Err* err);
diff --git a/gn/tools/gn/substitution_writer.cc b/gn/tools/gn/substitution_writer.cc
index 96a12b1db6c..41be9b4b9ec 100644
--- a/gn/tools/gn/substitution_writer.cc
+++ b/gn/tools/gn/substitution_writer.cc
@@ -5,9 +5,12 @@
#include "tools/gn/substitution_writer.h"
#include "tools/gn/build_settings.h"
+#include "tools/gn/c_substitution_type.h"
#include "tools/gn/escape.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/output_file.h"
+#include "tools/gn/rust_substitution_type.h"
+#include "tools/gn/rust_tool.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
#include "tools/gn/string_utils.h"
@@ -101,8 +104,9 @@ Placeholders
{{source_target_relative}}
The path to the source file relative to the target's directory. This will
generally be used for replicating the source directory layout in the
- output directory. This can only be used in actions and it is an error to
- use in process_file_template where there is no "target".
+ output directory. This can only be used in actions and bundle_data
+ targets. It is an error to use in process_file_template where there is no
+ "target".
"//foo/bar/baz.txt" => "baz.txt"
(*) Note on directories
@@ -156,11 +160,11 @@ void SubstitutionWriter::WriteWithNinjaVariables(
bool needs_quotes = false;
std::string result;
for (const auto& range : pattern.ranges()) {
- if (range.type == SUBSTITUTION_LITERAL) {
+ if (range.type == &SubstitutionLiteral) {
result.append(EscapeString(range.literal, no_quoting, &needs_quotes));
} else {
result.append("${");
- result.append(kSubstitutionNinjaNames[range.type]);
+ result.append(range.type->ninja_name);
result.append("}");
}
}
@@ -176,7 +180,7 @@ void SubstitutionWriter::GetListAsSourceFiles(const SubstitutionList& list,
std::vector<SourceFile>* output) {
for (const auto& pattern : list.list()) {
CHECK(pattern.ranges().size() == 1 &&
- pattern.ranges()[0].type == SUBSTITUTION_LITERAL)
+ pattern.ranges()[0].type == &SubstitutionLiteral)
<< "The substitution pattern \"" << pattern.AsString()
<< "\" was expected to be a literal with no {{substitutions}}.";
const std::string& literal = pattern.ranges()[0].literal;
@@ -208,7 +212,7 @@ SourceFile SubstitutionWriter::ApplyPatternToSource(
CHECK(!result_value.empty() && result_value[0] == '/')
<< "The result of the pattern \"" << pattern.AsString()
<< "\" was not a path beginning in \"/\" or \"//\".";
- return SourceFile(SourceFile::SWAP_IN, &result_value);
+ return SourceFile(std::move(result_value));
}
// static
@@ -219,7 +223,7 @@ std::string SubstitutionWriter::ApplyPatternToSourceAsString(
const SourceFile& source) {
std::string result_value;
for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == SUBSTITUTION_LITERAL) {
+ if (subrange.type == &SubstitutionLiteral) {
result_value.append(subrange.literal);
} else {
result_value.append(GetSourceSubstitution(target, settings, source,
@@ -316,7 +320,7 @@ void SubstitutionWriter::WriteNinjaVariablesForSource(
const Target* target,
const Settings* settings,
const SourceFile& source,
- const std::vector<SubstitutionType>& types,
+ const std::vector<const Substitution*>& types,
const EscapeOptions& escape_options,
std::ostream& out) {
for (const auto& type : types) {
@@ -324,8 +328,8 @@ void SubstitutionWriter::WriteNinjaVariablesForSource(
// is implicit in the rule. RESPONSE_FILE_NAME is written separately
// only when writing target rules since it can never be used in any
// other context (like process_file_template).
- if (type != SUBSTITUTION_SOURCE && type != SUBSTITUTION_RSP_FILE_NAME) {
- out << " " << kSubstitutionNinjaNames[type] << " = ";
+ if (type != &SubstitutionSource && type != &SubstitutionRspFileName) {
+ out << " " << type->ninja_name << " = ";
EscapeStringToStream(
out,
GetSourceSubstitution(target, settings, source, type, OUTPUT_RELATIVE,
@@ -341,59 +345,46 @@ std::string SubstitutionWriter::GetSourceSubstitution(
const Target* target,
const Settings* settings,
const SourceFile& source,
- SubstitutionType type,
+ const Substitution* type,
OutputStyle output_style,
const SourceDir& relative_to) {
std::string to_rebase;
- switch (type) {
- case SUBSTITUTION_SOURCE:
- if (source.is_system_absolute())
- return source.value();
- to_rebase = source.value();
- break;
-
- case SUBSTITUTION_SOURCE_NAME_PART:
- return FindFilenameNoExtension(&source.value()).as_string();
-
- case SUBSTITUTION_SOURCE_FILE_PART:
- return source.GetName();
-
- case SUBSTITUTION_SOURCE_DIR:
- if (source.is_system_absolute())
- return DirectoryWithNoLastSlash(source.GetDir());
- to_rebase = DirectoryWithNoLastSlash(source.GetDir());
- break;
-
- case SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR:
- if (source.is_system_absolute())
- return DirectoryWithNoLastSlash(source.GetDir());
- return RebasePath(DirectoryWithNoLastSlash(source.GetDir()),
- SourceDir("//"),
+ if (type == &SubstitutionSource) {
+ if (source.is_system_absolute())
+ return source.value();
+ to_rebase = source.value();
+ } else if (type == &SubstitutionSourceNamePart) {
+ return FindFilenameNoExtension(&source.value()).as_string();
+ } else if (type == &SubstitutionSourceFilePart) {
+ return source.GetName();
+ } else if (type == &SubstitutionSourceDir) {
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ to_rebase = DirectoryWithNoLastSlash(source.GetDir());
+ } else if (type == &SubstitutionSourceRootRelativeDir) {
+ if (source.is_system_absolute())
+ return DirectoryWithNoLastSlash(source.GetDir());
+ return RebasePath(DirectoryWithNoLastSlash(source.GetDir()),
+ SourceDir("//"),
+ settings->build_settings()->root_path_utf8());
+ } else if (type == &SubstitutionSourceGenDir) {
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
+ } else if (type == &SubstitutionSourceOutDir) {
+ to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
+ BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
+ } else if (type == &SubstitutionSourceTargetRelative) {
+ if (target) {
+ return RebasePath(source.value(), target->label().dir(),
settings->build_settings()->root_path_utf8());
-
- case SUBSTITUTION_SOURCE_GEN_DIR:
- to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings), source.GetDir(), BuildDirType::GEN));
- break;
-
- case SUBSTITUTION_SOURCE_OUT_DIR:
- to_rebase = DirectoryWithNoLastSlash(GetSubBuildDirAsSourceDir(
- BuildDirContext(settings), source.GetDir(), BuildDirType::OBJ));
- break;
-
- case SUBSTITUTION_SOURCE_TARGET_RELATIVE:
- if (target) {
- return RebasePath(source.value(), target->label().dir(),
- settings->build_settings()->root_path_utf8());
- }
- NOTREACHED() << "Cannot use substitution " << kSubstitutionNames[type]
- << " without target";
- return std::string();
-
- default:
- NOTREACHED() << "Unsupported substitution for this function: "
- << kSubstitutionNames[type];
- return std::string();
+ }
+ NOTREACHED() << "Cannot use substitution " << type->name
+ << " without target";
+ return std::string();
+ } else {
+ NOTREACHED() << "Unsupported substitution for this function: "
+ << type->name;
+ return std::string();
}
// If we get here, the result is a path that should be made relative or
@@ -412,7 +403,7 @@ OutputFile SubstitutionWriter::ApplyPatternToTargetAsOutputFile(
const SubstitutionPattern& pattern) {
std::string result_value;
for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == SUBSTITUTION_LITERAL) {
+ if (subrange.type == &SubstitutionLiteral) {
result_value.append(subrange.literal);
} else {
std::string subst;
@@ -435,49 +426,42 @@ void SubstitutionWriter::ApplyListToTargetAsOutputFile(
// static
bool SubstitutionWriter::GetTargetSubstitution(const Target* target,
- SubstitutionType type,
+ const Substitution* type,
std::string* result) {
- switch (type) {
- case SUBSTITUTION_LABEL:
- // Only include the toolchain for non-default toolchains.
- *result =
- target->label().GetUserVisibleName(!target->settings()->is_default());
- break;
- case SUBSTITUTION_LABEL_NAME:
- *result = target->label().name();
- break;
- case SUBSTITUTION_ROOT_GEN_DIR:
- SetDirOrDotWithNoSlash(
- GetBuildDirAsOutputFile(BuildDirContext(target), BuildDirType::GEN)
- .value(),
- result);
- break;
- case SUBSTITUTION_ROOT_OUT_DIR:
- SetDirOrDotWithNoSlash(
- target->settings()->toolchain_output_subdir().value(), result);
- break;
- case SUBSTITUTION_TARGET_GEN_DIR:
- SetDirOrDotWithNoSlash(
- GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
- result);
- break;
- case SUBSTITUTION_TARGET_OUT_DIR:
- SetDirOrDotWithNoSlash(
- GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
- result);
- break;
- case SUBSTITUTION_TARGET_OUTPUT_NAME:
- *result = target->GetComputedOutputName();
- break;
- default:
- return false;
+ if (type == &SubstitutionLabel) {
+ // Only include the toolchain for non-default toolchains.
+ *result =
+ target->label().GetUserVisibleName(!target->settings()->is_default());
+ } else if (type == &SubstitutionLabelName) {
+ *result = target->label().name();
+ } else if (type == &SubstitutionRootGenDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirAsOutputFile(BuildDirContext(target), BuildDirType::GEN)
+ .value(),
+ result);
+ } else if (type == &SubstitutionRootOutDir) {
+ SetDirOrDotWithNoSlash(
+ target->settings()->toolchain_output_subdir().value(), result);
+ } else if (type == &SubstitutionTargetGenDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::GEN).value(),
+ result);
+ } else if (type == &SubstitutionTargetOutDir) {
+ SetDirOrDotWithNoSlash(
+ GetBuildDirForTargetAsOutputFile(target, BuildDirType::OBJ).value(),
+ result);
+ } else if (type == &SubstitutionTargetOutputName) {
+ *result = target->GetComputedOutputName();
+ } else {
+ return false;
}
return true;
}
// static
-std::string SubstitutionWriter::GetTargetSubstitution(const Target* target,
- SubstitutionType type) {
+std::string SubstitutionWriter::GetTargetSubstitution(
+ const Target* target,
+ const Substitution* type) {
std::string result;
GetTargetSubstitution(target, type, &result);
return result;
@@ -490,7 +474,7 @@ OutputFile SubstitutionWriter::ApplyPatternToCompilerAsOutputFile(
const SubstitutionPattern& pattern) {
OutputFile result;
for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == SUBSTITUTION_LITERAL) {
+ if (subrange.type == &SubstitutionLiteral) {
result.value().append(subrange.literal);
} else {
result.value().append(
@@ -514,7 +498,7 @@ void SubstitutionWriter::ApplyListToCompilerAsOutputFile(
std::string SubstitutionWriter::GetCompilerSubstitution(
const Target* target,
const SourceFile& source,
- SubstitutionType type) {
+ const Substitution* type) {
// First try the common tool ones.
std::string result;
if (GetTargetSubstitution(target, type, &result))
@@ -533,7 +517,7 @@ OutputFile SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
const SubstitutionPattern& pattern) {
OutputFile result;
for (const auto& subrange : pattern.ranges()) {
- if (subrange.type == SUBSTITUTION_LITERAL) {
+ if (subrange.type == &SubstitutionLiteral) {
result.value().append(subrange.literal);
} else {
result.value().append(GetLinkerSubstitution(target, tool, subrange.type));
@@ -553,44 +537,62 @@ void SubstitutionWriter::ApplyListToLinkerAsOutputFile(
}
// static
-std::string SubstitutionWriter::GetLinkerSubstitution(const Target* target,
- const Tool* tool,
- SubstitutionType type) {
+std::string SubstitutionWriter::GetLinkerSubstitution(
+ const Target* target,
+ const Tool* tool,
+ const Substitution* type) {
// First try the common tool ones.
std::string result;
if (GetTargetSubstitution(target, type, &result))
return result;
// Fall-through to the linker-specific ones.
- switch (type) {
- case SUBSTITUTION_OUTPUT_DIR:
- // Use the target's value if there is one (it will have no expansion
- // patterns since it can directly use GN variables to compute whatever
- // path it wants), or the tool's default (which will contain further
- // expansions).
- if (target->output_dir().is_null()) {
- return ApplyPatternToLinkerAsOutputFile(target, tool,
- tool->default_output_dir())
- .value();
- }
- SetDirOrDotWithNoSlash(
- RebasePath(target->output_dir().value(),
- target->settings()->build_settings()->build_dir()),
- &result);
- return result;
-
- case SUBSTITUTION_OUTPUT_EXTENSION:
- // Use the extension provided on the target if specified, otherwise
- // fall back on the default. Note that the target's output extension
- // does not include the dot but the tool's does.
- if (!target->output_extension_set())
- return tool->default_output_extension();
- if (target->output_extension().empty())
- return std::string(); // Explicitly set to no extension.
- return std::string(".") + target->output_extension();
-
- default:
- NOTREACHED();
- return std::string();
+ if (type == &SubstitutionOutputDir) {
+ // Use the target's value if there is one (it will have no expansion
+ // patterns since it can directly use GN variables to compute whatever
+ // path it wants), or the tool's default (which will contain further
+ // expansions).
+ if (target->output_dir().is_null()) {
+ return ApplyPatternToLinkerAsOutputFile(target, tool,
+ tool->default_output_dir())
+ .value();
+ }
+ SetDirOrDotWithNoSlash(
+ RebasePath(target->output_dir().value(),
+ target->settings()->build_settings()->build_dir()),
+ &result);
+ return result;
+ } else if (type == &CSubstitutionOutputExtension) {
+ // Use the extension provided on the target if specified, otherwise
+ // fall back on the default. Note that the target's output extension
+ // does not include the dot but the tool's does.
+ if (!target->output_extension_set())
+ return tool->default_output_extension();
+ if (target->output_extension().empty())
+ return std::string(); // Explicitly set to no extension.
+ return std::string(".") + target->output_extension();
+ } else if (type == &kRustSubstitutionCrateName) {
+ // Only include the toolchain for non-default toolchains.
+ return target->rust_values().crate_name();
+ } else if (type == &kRustSubstitutionOutputPrefix) {
+ // Rustc expects specific output prefixes, so make sure we provide it if
+ // necessary.
+ if (target->output_type() == Target::RUST_LIBRARY ||
+ target->output_type() == Target::SHARED_LIBRARY ||
+ target->output_type() == Target::LOADABLE_MODULE)
+ return "lib";
+ return "";
+ } else if (type == &kRustSubstitutionOutputExtension) {
+ if (!target->output_extension_set()) {
+ DCHECK(tool->AsRust());
+ return tool->AsRust()->rustc_output_extension(
+ target->output_type(), target->rust_values().crate_type());
+ }
+ if (target->output_extension().empty())
+ return std::string(); // Explicitly set to no extension.
+ return std::string(".") + target->output_extension();
+ } else {
+ NOTREACHED();
+ return std::string();
}
}
diff --git a/gn/tools/gn/substitution_writer.h b/gn/tools/gn/substitution_writer.h
index 530f0647695..24cc283b24d 100644
--- a/gn/tools/gn/substitution_writer.h
+++ b/gn/tools/gn/substitution_writer.h
@@ -155,7 +155,7 @@ class SubstitutionWriter {
const Target* target,
const Settings* settings,
const SourceFile& source,
- const std::vector<SubstitutionType>& types,
+ const std::vector<const Substitution*>& types,
const EscapeOptions& escape_options,
std::ostream& out);
@@ -168,7 +168,7 @@ class SubstitutionWriter {
static std::string GetSourceSubstitution(const Target* target,
const Settings* settings,
const SourceFile& source,
- SubstitutionType type,
+ const Substitution* type,
OutputStyle output_style,
const SourceDir& relative_to);
@@ -190,10 +190,10 @@ class SubstitutionWriter {
// compiler and linker ones which will fall through if it's not a common tool
// one).
static bool GetTargetSubstitution(const Target* target,
- SubstitutionType type,
+ const Substitution* type,
std::string* result);
static std::string GetTargetSubstitution(const Target* target,
- SubstitutionType type);
+ const Substitution* type);
// Compiler substitutions ----------------------------------------------------
//
@@ -214,7 +214,7 @@ class SubstitutionWriter {
// directory.
static std::string GetCompilerSubstitution(const Target* target,
const SourceFile& source,
- SubstitutionType type);
+ const Substitution* type);
// Linker substitutions ------------------------------------------------------
@@ -232,7 +232,7 @@ class SubstitutionWriter {
// directory.
static std::string GetLinkerSubstitution(const Target* target,
const Tool* tool,
- SubstitutionType type);
+ const Substitution* type);
};
#endif // TOOLS_GN_SUBSTITUTION_WRITER_H_
diff --git a/gn/tools/gn/substitution_writer_unittest.cc b/gn/tools/gn/substitution_writer_unittest.cc
index 8093e4177e8..bea8fec9abb 100644
--- a/gn/tools/gn/substitution_writer_unittest.cc
+++ b/gn/tools/gn/substitution_writer_unittest.cc
@@ -4,6 +4,7 @@
#include <sstream>
+#include "tools/gn/c_substitution_type.h"
#include "tools/gn/err.h"
#include "tools/gn/escape.h"
#include "tools/gn/substitution_list.h"
@@ -62,10 +63,10 @@ TEST(SubstitutionWriter, ApplyPatternToSourceAsOutputFile) {
TEST(SubstitutionWriter, WriteNinjaVariablesForSource) {
TestWithScope setup;
- std::vector<SubstitutionType> types;
- types.push_back(SUBSTITUTION_SOURCE);
- types.push_back(SUBSTITUTION_SOURCE_NAME_PART);
- types.push_back(SUBSTITUTION_SOURCE_DIR);
+ std::vector<const Substitution*> types;
+ types.push_back(&SubstitutionSource);
+ types.push_back(&SubstitutionSourceNamePart);
+ types.push_back(&SubstitutionSourceDir);
EscapeOptions options;
options.mode = ESCAPE_NONE;
@@ -123,61 +124,58 @@ TEST(SubstitutionWriter, SourceSubstitutions) {
// Try all possible templates with a normal looking string.
EXPECT_EQ("../../foo/bar/baz.txt",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSource));
EXPECT_EQ("//foo/bar/baz.txt",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSource));
EXPECT_EQ("baz",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
EXPECT_EQ("baz",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_NAME_PART));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceNamePart));
EXPECT_EQ("baz.txt",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
EXPECT_EQ("baz.txt",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_FILE_PART));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceFilePart));
EXPECT_EQ("../../foo/bar",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
EXPECT_EQ("//foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_DIR));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceDir));
EXPECT_EQ("foo/bar", GetRelSubst("//foo/bar/baz.txt",
- SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+ &SubstitutionSourceRootRelativeDir));
EXPECT_EQ("foo/bar", GetAbsSubst("//foo/bar/baz.txt",
- SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+ &SubstitutionSourceRootRelativeDir));
EXPECT_EQ("gen/foo/bar",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
EXPECT_EQ("//out/Debug/gen/foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceGenDir));
EXPECT_EQ("obj/foo/bar",
- GetRelSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+ GetRelSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
EXPECT_EQ("//out/Debug/obj/foo/bar",
- GetAbsSubst("//foo/bar/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+ GetAbsSubst("//foo/bar/baz.txt", &SubstitutionSourceOutDir));
// Operations on an absolute path.
- EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE));
- EXPECT_EQ("/.", GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_DIR));
- EXPECT_EQ("gen/ABS_PATH",
- GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
- EXPECT_EQ("obj/ABS_PATH",
- GetRelSubst("/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+ EXPECT_EQ("/baz.txt", GetRelSubst("/baz.txt", &SubstitutionSource));
+ EXPECT_EQ("/.", GetRelSubst("/baz.txt", &SubstitutionSourceDir));
+ EXPECT_EQ("gen/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceGenDir));
+ EXPECT_EQ("obj/ABS_PATH", GetRelSubst("/baz.txt", &SubstitutionSourceOutDir));
#if defined(OS_WIN)
EXPECT_EQ("gen/ABS_PATH/C",
- GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_GEN_DIR));
+ GetRelSubst("/C:/baz.txt", &SubstitutionSourceGenDir));
EXPECT_EQ("obj/ABS_PATH/C",
- GetRelSubst("/C:/baz.txt", SUBSTITUTION_SOURCE_OUT_DIR));
+ GetRelSubst("/C:/baz.txt", &SubstitutionSourceOutDir));
#endif
- EXPECT_EQ(".",
- GetRelSubst("//baz.txt", SUBSTITUTION_SOURCE_ROOT_RELATIVE_DIR));
+ EXPECT_EQ(".", GetRelSubst("//baz.txt", &SubstitutionSourceRootRelativeDir));
EXPECT_EQ("baz.txt", GetRelSubst("//foo/bar/baz.txt",
- SUBSTITUTION_SOURCE_TARGET_RELATIVE));
+ &SubstitutionSourceTargetRelative));
EXPECT_EQ("baz.txt", GetAbsSubst("//foo/bar/baz.txt",
- SUBSTITUTION_SOURCE_TARGET_RELATIVE));
+ &SubstitutionSourceTargetRelative));
#undef GetAbsSubst
#undef GetRelSubst
@@ -194,31 +192,31 @@ TEST(SubstitutionWriter, TargetSubstitutions) {
std::string result;
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_LABEL, &result));
+ &target, &SubstitutionLabel, &result));
EXPECT_EQ("//foo/bar:baz", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_LABEL_NAME, &result));
+ &target, &SubstitutionLabelName, &result));
EXPECT_EQ("baz", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_ROOT_GEN_DIR, &result));
+ &target, &SubstitutionRootGenDir, &result));
EXPECT_EQ("gen", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_ROOT_OUT_DIR, &result));
+ &target, &SubstitutionRootOutDir, &result));
EXPECT_EQ(".", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_TARGET_GEN_DIR, &result));
+ &target, &SubstitutionTargetGenDir, &result));
EXPECT_EQ("gen/foo/bar", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_TARGET_OUT_DIR, &result));
+ &target, &SubstitutionTargetOutDir, &result));
EXPECT_EQ("obj/foo/bar", result);
EXPECT_TRUE(SubstitutionWriter::GetTargetSubstitution(
- &target, SUBSTITUTION_TARGET_OUTPUT_NAME, &result));
+ &target, &SubstitutionTargetOutputName, &result));
EXPECT_EQ("libbaz", result);
}
@@ -235,10 +233,10 @@ TEST(SubstitutionWriter, CompilerSubstitutions) {
// of each of those classes of things to make sure this is hooked up.
EXPECT_EQ("file", SubstitutionWriter::GetCompilerSubstitution(
&target, SourceFile("//foo/bar/file.txt"),
- SUBSTITUTION_SOURCE_NAME_PART));
+ &SubstitutionSourceNamePart));
EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetCompilerSubstitution(
&target, SourceFile("//foo/bar/file.txt"),
- SUBSTITUTION_TARGET_GEN_DIR));
+ &SubstitutionTargetGenDir));
}
TEST(SubstitutionWriter, LinkerSubstitutions) {
@@ -255,9 +253,9 @@ TEST(SubstitutionWriter, LinkerSubstitutions) {
// The compiler substitution is just target + OUTPUT_EXTENSION combined. So
// test one target one plus the output extension.
EXPECT_EQ(".so", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, SUBSTITUTION_OUTPUT_EXTENSION));
+ &target, tool, &CSubstitutionOutputExtension));
EXPECT_EQ("gen/foo/bar", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, SUBSTITUTION_TARGET_GEN_DIR));
+ &target, tool, &SubstitutionTargetGenDir));
// Test that we handle paths that end up in the root build dir properly
// (no leading "./" or "/").
@@ -272,10 +270,10 @@ TEST(SubstitutionWriter, LinkerSubstitutions) {
// Output extensions can be overridden.
target.set_output_extension("extension");
EXPECT_EQ(".extension", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, SUBSTITUTION_OUTPUT_EXTENSION));
+ &target, tool, &CSubstitutionOutputExtension));
target.set_output_extension("");
EXPECT_EQ("", SubstitutionWriter::GetLinkerSubstitution(
- &target, tool, SUBSTITUTION_OUTPUT_EXTENSION));
+ &target, tool, &CSubstitutionOutputExtension));
// Output directory is tested in a separate test below.
}
@@ -286,12 +284,12 @@ TEST(SubstitutionWriter, OutputDir) {
// This tool has an output directory pattern and uses that for the
// output name.
- Tool tool;
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolLink);
SubstitutionPattern out_dir_pattern;
ASSERT_TRUE(out_dir_pattern.Parse("{{root_out_dir}}/{{target_output_name}}",
nullptr, &err));
- tool.set_default_output_dir(out_dir_pattern);
- tool.SetComplete();
+ tool->set_default_output_dir(out_dir_pattern);
+ tool->SetComplete();
// Default target with no output dir overrides.
Target target(setup.settings(), Label(SourceDir("//foo/"), "baz"));
@@ -304,20 +302,20 @@ TEST(SubstitutionWriter, OutputDir) {
ASSERT_TRUE(output_name.Parse("{{output_dir}}/{{target_output_name}}.exe",
nullptr, &err));
EXPECT_EQ("./baz/baz.exe",
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, &tool,
- output_name)
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool.get(), output_name)
.value());
// Override the output name to the root build dir.
target.set_output_dir(SourceDir("//out/Debug/"));
EXPECT_EQ("./baz.exe", SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- &target, &tool, output_name)
+ &target, tool.get(), output_name)
.value());
// Override the output name to a new subdirectory.
target.set_output_dir(SourceDir("//out/Debug/foo/bar"));
EXPECT_EQ("foo/bar/baz.exe",
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(&target, &tool,
- output_name)
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ &target, tool.get(), output_name)
.value());
}
diff --git a/gn/tools/gn/target.cc b/gn/tools/gn/target.cc
index 376e09f0417..27dce258fec 100644
--- a/gn/tools/gn/target.cc
+++ b/gn/tools/gn/target.cc
@@ -10,12 +10,12 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "tools/gn/c_tool.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/deps_iterator.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/functions.h"
#include "tools/gn/scheduler.h"
-#include "tools/gn/source_file_type.h"
#include "tools/gn/substitution_writer.h"
#include "tools/gn/tool.h"
#include "tools/gn/toolchain.h"
@@ -94,8 +94,8 @@ bool EnsureFileIsGeneratedByDependency(const Target* target,
if (consider_object_files && target->IsBinary()) {
std::vector<OutputFile> source_outputs;
for (const SourceFile& source : target->sources()) {
- Toolchain::ToolType tool_type;
- if (!target->GetOutputFilesForSource(source, &tool_type, &source_outputs))
+ const char* tool_name;
+ if (!target->GetOutputFilesForSource(source, &tool_name, &source_outputs))
continue;
if (base::ContainsValue(source_outputs, file))
return true;
@@ -279,15 +279,7 @@ Dependencies
Target::Target(const Settings* settings,
const Label& label,
const std::set<SourceFile>& build_dependency_files)
- : Item(settings, label, build_dependency_files),
- output_type_(UNKNOWN),
- output_prefix_override_(false),
- output_extension_set_(false),
- all_headers_public_(true),
- check_includes_(true),
- complete_static_lib_(false),
- testonly_(false),
- toolchain_(nullptr) {}
+ : Item(settings, label, build_dependency_files) {}
Target::~Target() = default;
@@ -407,11 +399,12 @@ bool Target::OnResolved(Err* err) {
bool Target::IsBinary() const {
return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
output_type_ == LOADABLE_MODULE || output_type_ == STATIC_LIBRARY ||
- output_type_ == SOURCE_SET;
+ output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY;
}
bool Target::IsLinkable() const {
- return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
+ return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY ||
+ output_type_ == RUST_LIBRARY;
}
bool Target::IsFinal() const {
@@ -475,30 +468,28 @@ bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
label().GetUserVisibleName(false).c_str(),
GetStringForOutputType(output_type_),
label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
- Toolchain::ToolTypeToName(
- toolchain->GetToolTypeForTargetFinalOutput(this))
- .c_str()));
+ Tool::GetToolTypeForTargetFinalOutput(this)));
}
return false;
}
bool Target::GetOutputFilesForSource(const SourceFile& source,
- Toolchain::ToolType* computed_tool_type,
+ const char** computed_tool_type,
std::vector<OutputFile>* outputs) const {
outputs->clear();
- *computed_tool_type = Toolchain::TYPE_NONE;
+ *computed_tool_type = Tool::kToolNone;
- SourceFileType file_type = GetSourceFileType(source);
- if (file_type == SOURCE_UNKNOWN)
+ SourceFile::Type file_type = source.type();
+ if (file_type == SourceFile::SOURCE_UNKNOWN)
return false;
- if (file_type == SOURCE_O) {
+ if (file_type == SourceFile::SOURCE_O) {
// Object files just get passed to the output and not compiled.
outputs->push_back(OutputFile(settings()->build_settings(), source));
return true;
}
- *computed_tool_type = toolchain_->GetToolTypeForSourceType(file_type);
- if (*computed_tool_type == Toolchain::TYPE_NONE)
+ *computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
+ if (*computed_tool_type == Tool::kToolNone)
return false; // No tool for this file (it's a header file or something).
const Tool* tool = toolchain_->GetTool(*computed_tool_type);
if (!tool)
@@ -527,7 +518,8 @@ void Target::PullDependentTargetConfigs() {
void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
// Direct dependent libraries.
if (dep->output_type() == STATIC_LIBRARY ||
- dep->output_type() == SHARED_LIBRARY || dep->output_type() == SOURCE_SET)
+ dep->output_type() == SHARED_LIBRARY ||
+ dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY)
inherited_libraries_.Append(dep, is_public);
if (dep->output_type() == SHARED_LIBRARY) {
@@ -669,6 +661,7 @@ bool Target::FillOutputFiles(Err* err) {
this, tool, tool->runtime_outputs(), &runtime_outputs_);
}
break;
+ case RUST_LIBRARY:
case STATIC_LIBRARY:
// Static libraries both have dependencies and linking going off of the
// first output.
@@ -681,30 +674,37 @@ bool Target::FillOutputFiles(Err* err) {
case SHARED_LIBRARY:
CHECK(tool->outputs().list().size() >= 1);
check_tool_outputs = true;
- if (tool->link_output().empty() && tool->depend_output().empty()) {
+ if (const CTool* ctool = tool->AsC()) {
+ if (ctool->link_output().empty() && ctool->depend_output().empty()) {
+ // Default behavior, use the first output file for both.
+ link_output_file_ = dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, tool->outputs().list()[0]);
+ } else {
+ // Use the tool-specified ones.
+ if (!ctool->link_output().empty()) {
+ link_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, ctool->link_output());
+ }
+ if (!ctool->depend_output().empty()) {
+ dependency_output_file_ =
+ SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
+ this, tool, ctool->depend_output());
+ }
+ }
+ if (tool->runtime_outputs().list().empty()) {
+ // Default to the link output for the runtime output.
+ runtime_outputs_.push_back(link_output_file_);
+ } else {
+ SubstitutionWriter::ApplyListToLinkerAsOutputFile(
+ this, tool, tool->runtime_outputs(), &runtime_outputs_);
+ }
+ } else if (const RustTool* rstool = tool->AsRust()) {
// Default behavior, use the first output file for both.
link_output_file_ = dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
- } else {
- // Use the tool-specified ones.
- if (!tool->link_output().empty()) {
- link_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->link_output());
- }
- if (!tool->depend_output().empty()) {
- dependency_output_file_ =
- SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
- this, tool, tool->depend_output());
- }
- }
- if (tool->runtime_outputs().list().empty()) {
- // Default to the link output for the runtime output.
- runtime_outputs_.push_back(link_output_file_);
- } else {
- SubstitutionWriter::ApplyListToLinkerAsOutputFile(
- this, tool, tool->runtime_outputs(), &runtime_outputs_);
}
break;
case UNKNOWN:
diff --git a/gn/tools/gn/target.h b/gn/tools/gn/target.h
index b73ae575d8e..549b3545eb1 100644
--- a/gn/tools/gn/target.h
+++ b/gn/tools/gn/target.h
@@ -23,6 +23,7 @@
#include "tools/gn/metadata.h"
#include "tools/gn/ordered_set.h"
#include "tools/gn/output_file.h"
+#include "tools/gn/rust_values.h"
#include "tools/gn/source_file.h"
#include "tools/gn/toolchain.h"
#include "tools/gn/unique_vector.h"
@@ -47,6 +48,7 @@ class Target : public Item {
BUNDLE_DATA,
CREATE_BUNDLE,
GENERATED_FILE,
+ RUST_LIBRARY,
};
enum DepsIterationType {
@@ -125,6 +127,11 @@ class Target : public Item {
const FileList& sources() const { return sources_; }
FileList& sources() { return sources_; }
+ const SourceFileTypeSet& source_types_used() const {
+ return source_types_used_;
+ }
+ SourceFileTypeSet& source_types_used() { return source_types_used_; }
+
// Set to true when all sources are public. This is the default. In this case
// the public headers list should be empty.
bool all_headers_public() const { return all_headers_public_; }
@@ -263,6 +270,9 @@ class Target : public Item {
ActionValues& action_values() { return action_values_; }
const ActionValues& action_values() const { return action_values_; }
+ RustValues& rust_values() { return rust_values_; }
+ const RustValues& rust_values() const { return rust_values_; }
+
const OrderedSet<SourceDir>& all_lib_dirs() const { return all_lib_dirs_; }
const OrderedSet<LibFile>& all_libs() const { return all_libs_; }
@@ -331,7 +341,7 @@ class Target : public Item {
// are just passed to the output. The output will always be overwritten, not
// appended to.
bool GetOutputFilesForSource(const SourceFile& source,
- Toolchain::ToolType* computed_tool_type,
+ const char** computed_tool_type,
std::vector<OutputFile>* outputs) const;
private:
@@ -359,19 +369,20 @@ class Target : public Item {
void CheckSourcesGenerated() const;
void CheckSourceGenerated(const SourceFile& source) const;
- OutputType output_type_;
+ OutputType output_type_ = UNKNOWN;
std::string output_name_;
- bool output_prefix_override_;
+ bool output_prefix_override_ = false;
SourceDir output_dir_;
std::string output_extension_;
- bool output_extension_set_;
+ bool output_extension_set_ = false;
FileList sources_;
- bool all_headers_public_;
+ SourceFileTypeSet source_types_used_;
+ bool all_headers_public_ = true;
FileList public_headers_;
- bool check_includes_;
- bool complete_static_lib_;
- bool testonly_;
+ bool check_includes_ = true;
+ bool complete_static_lib_ = false;
+ bool testonly_ = false;
std::vector<std::string> data_;
BundleData bundle_data_;
OutputFile write_runtime_deps_output_;
@@ -411,8 +422,11 @@ class Target : public Item {
// Used for action[_foreach] targets.
ActionValues action_values_;
+ // Used for Rust targets.
+ RustValues rust_values_;
+
// Toolchain used by this target. Null until target is resolved.
- const Toolchain* toolchain_;
+ const Toolchain* toolchain_ = nullptr;
// Output files. Empty until the target is resolved.
std::vector<OutputFile> computed_outputs_;
diff --git a/gn/tools/gn/target_generator.cc b/gn/tools/gn/target_generator.cc
index 9ad8969d9a3..09de19dec78 100644
--- a/gn/tools/gn/target_generator.cc
+++ b/gn/tools/gn/target_generator.cc
@@ -144,6 +144,10 @@ void TargetGenerator::GenerateTarget(Scope* scope,
GeneratedFileTargetGenerator generator(target.get(), scope, function_call,
Target::GENERATED_FILE, err);
generator.Run();
+ } else if (output_type == functions::kRustLibrary) {
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
+ Target::RUST_LIBRARY, err);
+ generator.Run();
} else {
*err = Err(function_call, "Not a known target type",
"I am very confused by the target type \"" + output_type + "\"");
@@ -174,7 +178,7 @@ bool TargetGenerator::FillSources() {
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &dest_sources, err_))
return false;
- target_->sources().swap(dest_sources);
+ target_->sources() = std::move(dest_sources);
return true;
}
@@ -190,7 +194,7 @@ bool TargetGenerator::FillPublic() {
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &dest_public, err_))
return false;
- target_->public_headers().swap(dest_public);
+ target_->public_headers() = std::move(dest_public);
return true;
}
@@ -359,6 +363,16 @@ bool TargetGenerator::FillCheckIncludes() {
return true;
}
+bool TargetGenerator::FillOutputExtension() {
+ const Value* value = scope_->GetValue(variables::kOutputExtension, true);
+ if (!value)
+ return true;
+ if (!value->VerifyTypeIs(Value::STRING, err_))
+ return false;
+ target_->set_output_extension(value->string_value());
+ return true;
+}
+
bool TargetGenerator::EnsureSubstitutionIsInOutputDir(
const SubstitutionPattern& pattern,
const Value& original_value) {
@@ -368,7 +382,7 @@ bool TargetGenerator::EnsureSubstitutionIsInOutputDir(
return false;
}
- if (pattern.ranges()[0].type == SUBSTITUTION_LITERAL) {
+ if (pattern.ranges()[0].type == &SubstitutionLiteral) {
// If the first thing is a literal, it must start with the output dir.
if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(),
pattern.ranges()[0].literal,
diff --git a/gn/tools/gn/target_generator.h b/gn/tools/gn/target_generator.h
index 627505da2da..ae54882c79b 100644
--- a/gn/tools/gn/target_generator.h
+++ b/gn/tools/gn/target_generator.h
@@ -47,11 +47,12 @@ class TargetGenerator {
const BuildSettings* GetBuildSettings() const;
- bool FillSources();
+ virtual bool FillSources();
bool FillPublic();
bool FillConfigs();
bool FillOutputs(bool allow_substitutions);
bool FillCheckIncludes();
+ bool FillOutputExtension();
// Rrturns true if the given pattern will expand to a file in the output
// directory. If not, returns false and sets the error, blaming the given
diff --git a/gn/tools/gn/target_unittest.cc b/gn/tools/gn/target_unittest.cc
index b6ad0ff566b..f1178462fe1 100644
--- a/gn/tools/gn/target_unittest.cc
+++ b/gn/tools/gn/target_unittest.cc
@@ -637,7 +637,8 @@ TEST_F(TargetTest, LinkAndDepOutputs) {
Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
- std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
solink_tool->set_output_prefix("lib");
solink_tool->set_default_output_extension(".so");
@@ -656,7 +657,7 @@ TEST_F(TargetTest, LinkAndDepOutputs) {
solink_tool->set_outputs(
SubstitutionList::MakeForTest(kLinkPattern, kDependPattern));
- toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+ toolchain.SetTool(std::move(solink));
Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
target.set_output_type(Target::SHARED_LIBRARY);
@@ -678,7 +679,8 @@ TEST_F(TargetTest, RuntimeOuputs) {
Toolchain toolchain(setup.settings(), Label(SourceDir("//tc/"), "tc"));
- std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
solink_tool->set_output_prefix("");
solink_tool->set_default_output_extension(".dll");
@@ -699,7 +701,7 @@ TEST_F(TargetTest, RuntimeOuputs) {
solink_tool->set_runtime_outputs(
SubstitutionList::MakeForTest(kDllPattern, kPdbPattern));
- toolchain.SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+ toolchain.SetTool(std::move(solink));
Target target(setup.settings(), Label(SourceDir("//a/"), "a"));
target.set_output_type(Target::SHARED_LIBRARY);
diff --git a/gn/tools/gn/test_with_scope.cc b/gn/tools/gn/test_with_scope.cc
index 7063acdebb5..80d969295be 100644
--- a/gn/tools/gn/test_with_scope.cc
+++ b/gn/tools/gn/test_with_scope.cc
@@ -74,119 +74,144 @@ void TestWithScope::SetupToolchain(Toolchain* toolchain) {
Err err;
// CC
- std::unique_ptr<Tool> cc_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cc_tool = Tool::CreateTool(CTool::kCToolCc);
SetCommandForTool(
"cc {{source}} {{cflags}} {{cflags_c}} {{defines}} {{include_dirs}} "
"-o {{output}}",
cc_tool.get());
cc_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(Toolchain::TYPE_CC, std::move(cc_tool));
+ toolchain->SetTool(std::move(cc_tool));
// CXX
- std::unique_ptr<Tool> cxx_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> cxx_tool = Tool::CreateTool(CTool::kCToolCxx);
SetCommandForTool(
"c++ {{source}} {{cflags}} {{cflags_cc}} {{defines}} {{include_dirs}} "
"-o {{output}}",
cxx_tool.get());
cxx_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(Toolchain::TYPE_CXX, std::move(cxx_tool));
+ cxx_tool->set_command_launcher("launcher");
+ toolchain->SetTool(std::move(cxx_tool));
// OBJC
- std::unique_ptr<Tool> objc_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> objc_tool = Tool::CreateTool(CTool::kCToolObjC);
SetCommandForTool(
"objcc {{source}} {{cflags}} {{cflags_objc}} {{defines}} "
"{{include_dirs}} -o {{output}}",
objc_tool.get());
objc_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(Toolchain::TYPE_OBJC, std::move(objc_tool));
+ toolchain->SetTool(std::move(objc_tool));
// OBJC
- std::unique_ptr<Tool> objcxx_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> objcxx_tool = Tool::CreateTool(CTool::kCToolObjCxx);
SetCommandForTool(
"objcxx {{source}} {{cflags}} {{cflags_objcc}} {{defines}} "
"{{include_dirs}} -o {{output}}",
objcxx_tool.get());
objcxx_tool->set_outputs(SubstitutionList::MakeForTest(
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o"));
- toolchain->SetTool(Toolchain::TYPE_OBJCXX, std::move(objcxx_tool));
+ toolchain->SetTool(std::move(objcxx_tool));
// Don't use RC and ASM tools in unit tests yet. Add here if needed.
// ALINK
- std::unique_ptr<Tool> alink_tool = std::make_unique<Tool>();
- SetCommandForTool("ar {{output}} {{source}}", alink_tool.get());
+ std::unique_ptr<Tool> alink = Tool::CreateTool(CTool::kCToolAlink);
+ CTool* alink_tool = alink->AsC();
+ SetCommandForTool("ar {{output}} {{source}}", alink_tool);
alink_tool->set_lib_switch("-l");
alink_tool->set_lib_dir_switch("-L");
alink_tool->set_output_prefix("lib");
alink_tool->set_outputs(SubstitutionList::MakeForTest(
"{{target_out_dir}}/{{target_output_name}}.a"));
- toolchain->SetTool(Toolchain::TYPE_ALINK, std::move(alink_tool));
+ toolchain->SetTool(std::move(alink));
// SOLINK
- std::unique_ptr<Tool> solink_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> solink = Tool::CreateTool(CTool::kCToolSolink);
+ CTool* solink_tool = solink->AsC();
SetCommandForTool(
"ld -shared -o {{target_output_name}}.so {{inputs}} "
"{{ldflags}} {{libs}}",
- solink_tool.get());
+ solink_tool);
solink_tool->set_lib_switch("-l");
solink_tool->set_lib_dir_switch("-L");
solink_tool->set_output_prefix("lib");
solink_tool->set_default_output_extension(".so");
solink_tool->set_outputs(SubstitutionList::MakeForTest(
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
- toolchain->SetTool(Toolchain::TYPE_SOLINK, std::move(solink_tool));
+ toolchain->SetTool(std::move(solink));
// SOLINK_MODULE
- std::unique_ptr<Tool> solink_module_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> solink_module =
+ Tool::CreateTool(CTool::kCToolSolinkModule);
+ CTool* solink_module_tool = solink_module->AsC();
SetCommandForTool(
"ld -bundle -o {{target_output_name}}.so {{inputs}} "
"{{ldflags}} {{libs}}",
- solink_module_tool.get());
+ solink_module_tool);
solink_module_tool->set_lib_switch("-l");
solink_module_tool->set_lib_dir_switch("-L");
solink_module_tool->set_output_prefix("lib");
solink_module_tool->set_default_output_extension(".so");
solink_module_tool->set_outputs(SubstitutionList::MakeForTest(
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}"));
- toolchain->SetTool(Toolchain::TYPE_SOLINK_MODULE,
- std::move(solink_module_tool));
+ toolchain->SetTool(std::move(solink_module));
// LINK
- std::unique_ptr<Tool> link_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> link = Tool::CreateTool(CTool::kCToolLink);
+ CTool* link_tool = link->AsC();
SetCommandForTool(
"ld -o {{target_output_name}} {{source}} "
"{{ldflags}} {{libs}}",
- link_tool.get());
+ link_tool);
link_tool->set_lib_switch("-l");
link_tool->set_lib_dir_switch("-L");
link_tool->set_outputs(
SubstitutionList::MakeForTest("{{root_out_dir}}/{{target_output_name}}"));
- toolchain->SetTool(Toolchain::TYPE_LINK, std::move(link_tool));
+ toolchain->SetTool(std::move(link));
// STAMP
- std::unique_ptr<Tool> stamp_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> stamp_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolStamp);
SetCommandForTool("touch {{output}}", stamp_tool.get());
- toolchain->SetTool(Toolchain::TYPE_STAMP, std::move(stamp_tool));
+ toolchain->SetTool(std::move(stamp_tool));
// COPY
- std::unique_ptr<Tool> copy_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> copy_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCopy);
SetCommandForTool("cp {{source}} {{output}}", copy_tool.get());
- toolchain->SetTool(Toolchain::TYPE_COPY, std::move(copy_tool));
+ toolchain->SetTool(std::move(copy_tool));
// COPY_BUNDLE_DATA
- std::unique_ptr<Tool> copy_bundle_data_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> copy_bundle_data_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCopyBundleData);
SetCommandForTool("cp {{source}} {{output}}", copy_bundle_data_tool.get());
- toolchain->SetTool(Toolchain::TYPE_COPY_BUNDLE_DATA,
- std::move(copy_bundle_data_tool));
+ toolchain->SetTool(std::move(copy_bundle_data_tool));
// COMPILE_XCASSETS
- std::unique_ptr<Tool> compile_xcassets_tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> compile_xcassets_tool =
+ Tool::CreateTool(GeneralTool::kGeneralToolCompileXCAssets);
SetCommandForTool("touch {{output}}", compile_xcassets_tool.get());
- toolchain->SetTool(Toolchain::TYPE_COMPILE_XCASSETS,
- std::move(compile_xcassets_tool));
+ toolchain->SetTool(std::move(compile_xcassets_tool));
+
+ // RUST
+ std::unique_ptr<Tool> rustc_tool = Tool::CreateTool(RustTool::kRsToolRustc);
+ SetCommandForTool(
+ "{{rustenv}} rustc --edition=2018 --crate-name {{crate_name}} {{source}} "
+ "--crate-type {{crate_type}} {{rustflags}} -o "
+ "{{target_out_dir}}/"
+ "{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}} "
+ "{{rustdeps}} {{externs}}",
+ rustc_tool.get());
+ rustc_tool->AsRust()->set_dylib_output_extension(".so");
+ rustc_tool->AsRust()->set_cdylib_output_extension(".so");
+ rustc_tool->AsRust()->set_staticlib_output_extension(".a");
+ rustc_tool->AsRust()->set_proc_macro_output_extension(".so");
+ rustc_tool->AsRust()->set_outputs(SubstitutionList::MakeForTest(
+ "{{target_out_dir}}/"
+ "{{rustc_output_prefix}}{{crate_name}}{{rustc_output_extension}}"));
+ toolchain->SetTool(std::move(rustc_tool));
toolchain->ToolchainSetupComplete();
}
diff --git a/gn/tools/gn/test_with_scope.h b/gn/tools/gn/test_with_scope.h
index 88532438213..897ef1b9c88 100644
--- a/gn/tools/gn/test_with_scope.h
+++ b/gn/tools/gn/test_with_scope.h
@@ -10,9 +10,12 @@
#include "base/macros.h"
#include "tools/gn/build_settings.h"
+#include "tools/gn/c_tool.h"
#include "tools/gn/err.h"
+#include "tools/gn/general_tool.h"
#include "tools/gn/input_file.h"
#include "tools/gn/parse_tree.h"
+#include "tools/gn/rust_tool.h"
#include "tools/gn/scope.h"
#include "tools/gn/scope_per_file_provider.h"
#include "tools/gn/settings.h"
diff --git a/gn/tools/gn/tokenizer.cc b/gn/tools/gn/tokenizer.cc
index 9625203dcd4..09f0b4b7d14 100644
--- a/gn/tools/gn/tokenizer.cc
+++ b/gn/tools/gn/tokenizer.cc
@@ -69,12 +69,7 @@ Token::Type GetSpecificOperatorType(base::StringPiece value) {
} // namespace
Tokenizer::Tokenizer(const InputFile* input_file, Err* err)
- : input_file_(input_file),
- input_(input_file->contents()),
- err_(err),
- cur_(0),
- line_number_(1),
- column_number_(1) {}
+ : input_file_(input_file), input_(input_file->contents()), err_(err) {}
Tokenizer::~Tokenizer() = default;
diff --git a/gn/tools/gn/tokenizer.h b/gn/tools/gn/tokenizer.h
index 67590df55ef..7b66ac0dab4 100644
--- a/gn/tools/gn/tokenizer.h
+++ b/gn/tools/gn/tokenizer.h
@@ -79,10 +79,10 @@ class Tokenizer {
const InputFile* input_file_;
const base::StringPiece input_;
Err* err_;
- size_t cur_; // Byte offset into input buffer.
+ size_t cur_ = 0; // Byte offset into input buffer.
- int line_number_;
- int column_number_;
+ int line_number_ = 1;
+ int column_number_ = 1;
DISALLOW_COPY_AND_ASSIGN(Tokenizer);
};
diff --git a/gn/tools/gn/tool.cc b/gn/tools/gn/tool.cc
index b066fba400c..d67933ee39e 100644
--- a/gn/tools/gn/tool.cc
+++ b/gn/tools/gn/tool.cc
@@ -4,16 +4,18 @@
#include "tools/gn/tool.h"
-Tool::Tool()
- : defined_from_(nullptr),
- depsformat_(DEPS_GCC),
- precompiled_header_type_(PCH_NONE),
- restat_(false),
- complete_(false) {}
+#include "tools/gn/c_tool.h"
+#include "tools/gn/general_tool.h"
+#include "tools/gn/rust_tool.h"
+#include "tools/gn/target.h"
+
+const char* Tool::kToolNone = "";
+
+Tool::Tool(const char* n) : name_(n) {}
Tool::~Tool() = default;
-void Tool::SetComplete() {
+void Tool::SetToolComplete() {
DCHECK(!complete_);
complete_ = true;
@@ -21,8 +23,322 @@ void Tool::SetComplete() {
depfile_.FillRequiredTypes(&substitution_bits_);
description_.FillRequiredTypes(&substitution_bits_);
outputs_.FillRequiredTypes(&substitution_bits_);
- link_output_.FillRequiredTypes(&substitution_bits_);
- depend_output_.FillRequiredTypes(&substitution_bits_);
rspfile_.FillRequiredTypes(&substitution_bits_);
rspfile_content_.FillRequiredTypes(&substitution_bits_);
}
+
+GeneralTool* Tool::AsGeneral() {
+ return nullptr;
+}
+
+const GeneralTool* Tool::AsGeneral() const {
+ return nullptr;
+}
+
+CTool* Tool::AsC() {
+ return nullptr;
+}
+
+const CTool* Tool::AsC() const {
+ return nullptr;
+}
+
+RustTool* Tool::AsRust() {
+ return nullptr;
+}
+const RustTool* Tool::AsRust() const {
+ return nullptr;
+}
+
+bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
+ const SubstitutionPattern& pattern) const {
+ for (const auto& cur : output_list.list()) {
+ if (pattern.ranges().size() == cur.ranges().size() &&
+ std::equal(pattern.ranges().begin(), pattern.ranges().end(),
+ cur.ranges().begin()))
+ return true;
+ }
+ return false;
+}
+
+bool Tool::ValidateSubstitutionList(
+ const std::vector<const Substitution*>& list,
+ const Value* origin,
+ Err* err) const {
+ for (const auto& cur_type : list) {
+ if (!ValidateSubstitution(cur_type)) {
+ *err = Err(*origin, "Pattern not valid here.",
+ "You used the pattern " + std::string(cur_type->name) +
+ " which is not valid\nfor this variable.");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Tool::ReadBool(Scope* scope, const char* var, bool* field, Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::BOOLEAN, err))
+ return false;
+ *field = v->boolean_value();
+ return true;
+}
+
+bool Tool::ReadString(Scope* scope,
+ const char* var,
+ std::string* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+ if (!v->VerifyTypeIs(Value::STRING, err))
+ return false;
+ *field = v->string_value();
+ return true;
+}
+
+bool Tool::ReadPattern(Scope* scope,
+ const char* var,
+ SubstitutionPattern* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ SubstitutionPattern pattern;
+ if (!pattern.Parse(*value, err))
+ return false;
+ if (!ValidateSubstitutionList(pattern.required_types(), value, err))
+ return false;
+
+ *field = std::move(pattern);
+ return true;
+}
+
+bool Tool::ReadPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue(var, true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::LIST, err))
+ return false;
+
+ SubstitutionList list;
+ if (!list.Parse(*value, err))
+ return false;
+
+ // Validate the right kinds of patterns are used.
+ if (!ValidateSubstitutionList(list.required_types(), value, err))
+ return false;
+
+ *field = std::move(list);
+ return true;
+}
+
+bool Tool::ReadLabel(Scope* scope,
+ const char* var,
+ const Label& current_toolchain,
+ LabelPtrPair<Pool>* field,
+ Err* err) {
+ DCHECK(!complete_);
+ const Value* v = scope->GetValue(var, true);
+ if (!v)
+ return true; // Not present is fine.
+
+ Label label =
+ Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
+ if (err->has_error())
+ return false;
+
+ LabelPtrPair<Pool> pair(label);
+ pair.origin = defined_from();
+
+ *field = std::move(pair);
+ return true;
+}
+
+bool Tool::ReadOutputExtension(Scope* scope, Err* err) {
+ DCHECK(!complete_);
+ const Value* value = scope->GetValue("default_output_extension", true);
+ if (!value)
+ return true; // Not present is fine.
+ if (!value->VerifyTypeIs(Value::STRING, err))
+ return false;
+
+ if (value->string_value().empty())
+ return true; // Accept empty string.
+
+ if (value->string_value()[0] != '.') {
+ *err = Err(*value, "default_output_extension must begin with a '.'");
+ return false;
+ }
+
+ set_default_output_extension(value->string_value());
+ return true;
+}
+
+bool Tool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
+ if (!ReadPattern(scope, "command", &command_, err) ||
+ !ReadString(scope, "command_launcher", &command_launcher_, err) ||
+ !ReadOutputExtension(scope, err) ||
+ !ReadPattern(scope, "depfile", &depfile_, err) ||
+ !ReadPattern(scope, "description", &description_, err) ||
+ !ReadPatternList(scope, "runtime_outputs", &runtime_outputs_, err) ||
+ !ReadString(scope, "output_prefix", &output_prefix_, err) ||
+ !ReadPattern(scope, "default_output_dir", &default_output_dir_, err) ||
+ !ReadBool(scope, "restat", &restat_, err) ||
+ !ReadPattern(scope, "rspfile", &rspfile_, err) ||
+ !ReadPattern(scope, "rspfile_content", &rspfile_content_, err) ||
+ !ReadLabel(scope, "pool", toolchain->label(), &pool_, err)) {
+ return false;
+ }
+ return true;
+}
+
+std::unique_ptr<Tool> Tool::CreateTool(const ParseNode* function,
+ const std::string& name,
+ Scope* scope,
+ Toolchain* toolchain,
+ Err* err) {
+ std::unique_ptr<Tool> tool = CreateTool(name);
+ if (!tool) {
+ *err = Err(function, "Unknown tool type.");
+ return nullptr;
+ }
+ if (CTool* c_tool = tool->AsC()) {
+ if (c_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ if (GeneralTool* general_tool = tool->AsGeneral()) {
+ if (general_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ if (RustTool* rust_tool = tool->AsRust()) {
+ if (rust_tool->InitTool(scope, toolchain, err))
+ return tool;
+ return nullptr;
+ }
+ NOTREACHED();
+ *err = Err(function, "Unknown tool type.");
+ return nullptr;
+}
+
+// static
+std::unique_ptr<Tool> Tool::CreateTool(const std::string& name) {
+ // C tools
+ if (name == CTool::kCToolCc)
+ return std::make_unique<CTool>(CTool::kCToolCc);
+ else if (name == CTool::kCToolCxx)
+ return std::make_unique<CTool>(CTool::kCToolCxx);
+ else if (name == CTool::kCToolObjC)
+ return std::make_unique<CTool>(CTool::kCToolObjC);
+ else if (name == CTool::kCToolObjCxx)
+ return std::make_unique<CTool>(CTool::kCToolObjCxx);
+ else if (name == CTool::kCToolRc)
+ return std::make_unique<CTool>(CTool::kCToolRc);
+ else if (name == CTool::kCToolAsm)
+ return std::make_unique<CTool>(CTool::kCToolAsm);
+ else if (name == CTool::kCToolAlink)
+ return std::make_unique<CTool>(CTool::kCToolAlink);
+ else if (name == CTool::kCToolSolink)
+ return std::make_unique<CTool>(CTool::kCToolSolink);
+ else if (name == CTool::kCToolSolinkModule)
+ return std::make_unique<CTool>(CTool::kCToolSolinkModule);
+ else if (name == CTool::kCToolLink)
+ return std::make_unique<CTool>(CTool::kCToolLink);
+
+ // General tools
+ else if (name == GeneralTool::kGeneralToolAction)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolAction);
+ else if (name == GeneralTool::kGeneralToolStamp)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolStamp);
+ else if (name == GeneralTool::kGeneralToolCopy)
+ return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolCopy);
+ else if (name == GeneralTool::kGeneralToolCopyBundleData)
+ return std::make_unique<GeneralTool>(
+ GeneralTool::kGeneralToolCopyBundleData);
+ else if (name == GeneralTool::kGeneralToolCompileXCAssets)
+ return std::make_unique<GeneralTool>(
+ GeneralTool::kGeneralToolCompileXCAssets);
+
+ // Rust tool
+ else if (name == RustTool::kRsToolRustc)
+ return std::make_unique<RustTool>(RustTool::kRsToolRustc);
+
+ return nullptr;
+}
+
+// static
+const char* Tool::GetToolTypeForSourceType(SourceFile::Type type) {
+ switch (type) {
+ case SourceFile::SOURCE_C:
+ return CTool::kCToolCc;
+ case SourceFile::SOURCE_CPP:
+ return CTool::kCToolCxx;
+ case SourceFile::SOURCE_M:
+ return CTool::kCToolObjC;
+ case SourceFile::SOURCE_MM:
+ return CTool::kCToolObjCxx;
+ case SourceFile::SOURCE_ASM:
+ case SourceFile::SOURCE_S:
+ return CTool::kCToolAsm;
+ case SourceFile::SOURCE_RC:
+ return CTool::kCToolRc;
+ case SourceFile::SOURCE_RS:
+ return RustTool::kRsToolRustc;
+ case SourceFile::SOURCE_UNKNOWN:
+ case SourceFile::SOURCE_H:
+ case SourceFile::SOURCE_O:
+ case SourceFile::SOURCE_DEF:
+ case SourceFile::SOURCE_GO:
+ return kToolNone;
+ default:
+ NOTREACHED();
+ return kToolNone;
+ }
+}
+
+// static
+const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
+ // The contents of this list might be suprising (i.e. stamp 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())
+ return RustTool::kRsToolRustc;
+ switch (target->output_type()) {
+ case Target::GROUP:
+ return GeneralTool::kGeneralToolStamp;
+ case Target::EXECUTABLE:
+ return CTool::kCToolLink;
+ case Target::SHARED_LIBRARY:
+ return CTool::kCToolSolink;
+ case Target::LOADABLE_MODULE:
+ 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::GENERATED_FILE:
+ return GeneralTool::kGeneralToolStamp;
+ default:
+ NOTREACHED();
+ return kToolNone;
+ }
+}
diff --git a/gn/tools/gn/tool.h b/gn/tools/gn/tool.h
index d2d0b7ceacb..b963ebf69cd 100644
--- a/gn/tools/gn/tool.h
+++ b/gn/tools/gn/tool.h
@@ -11,24 +11,66 @@
#include "base/macros.h"
#include "tools/gn/label.h"
#include "tools/gn/label_ptr.h"
+#include "tools/gn/scope.h"
+#include "tools/gn/source_file.h"
#include "tools/gn/substitution_list.h"
#include "tools/gn/substitution_pattern.h"
class ParseNode;
class Pool;
+class Target;
+class Toolchain;
+class CTool;
+class GeneralTool;
+class RustTool;
+
+// To add a new Tool category, create a subclass implementing SetComplete()
+// Add a new category to ToolCategories
+// Add a GetAs* function
class Tool {
public:
- enum DepsFormat { DEPS_GCC = 0, DEPS_MSVC = 1 };
+ static const char* kToolNone;
+
+ virtual ~Tool();
+
+ // Manual RTTI and required functions ---------------------------------------
+ //
+ // To implement a new tool category to compile binaries in a different way,
+ // inherit this class, implement the following functions, and add the
+ // appropriate ToolTypes and RTTI getters. New tools will also need to
+ // implement a corresponding class inheriting NinjaBinaryTargetWriter that
+ // does the actual rule writing.
+
+ // Initialize tool from a scope. Child classes should override this function
+ // and call the parent.
+ bool InitTool(Scope* block_scope, Toolchain* toolchain, Err* err);
+
+ // Validate the char* passed to the creation.
+ virtual bool ValidateName(const char* name) const = 0;
+
+ // Called when the toolchain is saving this tool, after everything is filled
+ // in.
+ virtual void SetComplete() = 0;
+
+ // Validate substitutions in this tool.
+ virtual bool ValidateSubstitution(const Substitution* sub_type) const = 0;
- enum PrecompiledHeaderType { PCH_NONE = 0, PCH_GCC = 1, PCH_MSVC = 2 };
+ // Manual RTTI
+ virtual CTool* AsC();
+ virtual const CTool* AsC() const;
+ virtual GeneralTool* AsGeneral();
+ virtual const GeneralTool* AsGeneral() const;
+ virtual RustTool* AsRust();
+ virtual const RustTool* AsRust() const;
- Tool();
- ~Tool();
+ // Basic information ---------------------------------------------------------
const ParseNode* defined_from() const { return defined_from_; }
void set_defined_from(const ParseNode* df) { defined_from_ = df; }
+ const char* name() const { return name_; }
+
// Getters/setters ----------------------------------------------------------
//
// After the tool has had its attributes set, the caller must call
@@ -41,6 +83,15 @@ class Tool {
command_ = std::move(cmd);
}
+ // Launcher for the command (e.g. goma)
+ const std::string& command_launcher() const {
+ return command_launcher_;
+ }
+ void set_command_launcher(std::string l) {
+ DCHECK(!complete_);
+ command_launcher_ = std::move(l);
+ }
+
// Should include a leading "." if nonempty.
const std::string& default_output_extension() const {
return default_output_extension_;
@@ -66,56 +117,18 @@ class Tool {
depfile_ = std::move(df);
}
- DepsFormat depsformat() const { return depsformat_; }
- void set_depsformat(DepsFormat f) {
- DCHECK(!complete_);
- depsformat_ = f;
- }
-
- PrecompiledHeaderType precompiled_header_type() const {
- return precompiled_header_type_;
- }
- void set_precompiled_header_type(PrecompiledHeaderType pch_type) {
- precompiled_header_type_ = pch_type;
- }
-
const SubstitutionPattern& description() const { return description_; }
void set_description(SubstitutionPattern desc) {
DCHECK(!complete_);
description_ = std::move(desc);
}
- const std::string& lib_switch() const { return lib_switch_; }
- void set_lib_switch(std::string s) {
- DCHECK(!complete_);
- lib_switch_ = std::move(s);
- }
-
- const std::string& lib_dir_switch() const { return lib_dir_switch_; }
- void set_lib_dir_switch(std::string s) {
- DCHECK(!complete_);
- lib_dir_switch_ = std::move(s);
- }
-
const SubstitutionList& outputs() const { return outputs_; }
void set_outputs(SubstitutionList out) {
DCHECK(!complete_);
outputs_ = std::move(out);
}
- // Should match files in the outputs() if nonempty.
- const SubstitutionPattern& link_output() const { return link_output_; }
- void set_link_output(SubstitutionPattern link_out) {
- DCHECK(!complete_);
- link_output_ = std::move(link_out);
- }
-
- const SubstitutionPattern& depend_output() const { return depend_output_; }
- void set_depend_output(SubstitutionPattern dep_out) {
- DCHECK(!complete_);
- depend_output_ = std::move(dep_out);
- }
-
const SubstitutionList& runtime_outputs() const { return runtime_outputs_; }
void set_runtime_outputs(SubstitutionList run_out) {
DCHECK(!complete_);
@@ -153,15 +166,8 @@ class Tool {
// Other functions ----------------------------------------------------------
- // Called when the toolchain is saving this tool, after everything is filled
- // in.
- void SetComplete();
-
- // Returns true if this tool has separate outputs for dependency tracking
- // and linking.
- bool has_separate_solink_files() const {
- return !link_output_.empty() || !depend_output_.empty();
- }
+ // Function for the above override to call to complete the tool.
+ void SetToolComplete();
// Substitutions required by this tool.
const SubstitutionBits& substitution_bits() const {
@@ -169,31 +175,66 @@ class Tool {
return substitution_bits_;
}
- bool OnResolved(Err* err);
+ // Create a tool based on given features.
+ static std::unique_ptr<Tool> CreateTool(const std::string& name);
+ static std::unique_ptr<Tool> CreateTool(const ParseNode* function,
+ const std::string& name,
+ Scope* scope,
+ Toolchain* toolchain,
+ Err* err);
+
+ static const char* GetToolTypeForSourceType(SourceFile::Type type);
+ static const char* GetToolTypeForTargetFinalOutput(const Target* target);
- private:
- const ParseNode* defined_from_;
+ protected:
+ explicit Tool(const char* t);
+
+ // Initialization functions -------------------------------------------------
+ //
+ // Initialization methods used by InitTool(). If successful, will set the
+ // field and return true, otherwise will return false. Must be called before
+ // SetComplete().
+ bool IsPatternInOutputList(const SubstitutionList& output_list,
+ const SubstitutionPattern& pattern) const;
+ bool ValidateSubstitutionList(const std::vector<const Substitution*>& list,
+ const Value* origin,
+ Err* err) const;
+ bool ValidateOutputs(Err* err) const;
+ bool ReadBool(Scope* scope, const char* var, bool* field, Err* err);
+ bool ReadString(Scope* scope, const char* var, std::string* field, Err* err);
+ bool ReadPattern(Scope* scope,
+ const char* var,
+ SubstitutionPattern* field,
+ Err* err);
+ bool ReadPatternList(Scope* scope,
+ const char* var,
+ SubstitutionList* field,
+ Err* err);
+ bool ReadLabel(Scope* scope,
+ const char* var,
+ const Label& current_toolchain,
+ LabelPtrPair<Pool>* field,
+ Err* err);
+ bool ReadOutputExtension(Scope* scope, Err* err);
+
+ const ParseNode* defined_from_ = nullptr;
+ const char* name_ = nullptr;
SubstitutionPattern command_;
+ std::string command_launcher_;
std::string default_output_extension_;
SubstitutionPattern default_output_dir_;
SubstitutionPattern depfile_;
- DepsFormat depsformat_;
- PrecompiledHeaderType precompiled_header_type_;
SubstitutionPattern description_;
- std::string lib_switch_;
- std::string lib_dir_switch_;
SubstitutionList outputs_;
- SubstitutionPattern link_output_;
- SubstitutionPattern depend_output_;
SubstitutionList runtime_outputs_;
std::string output_prefix_;
- bool restat_;
+ bool restat_ = false;
SubstitutionPattern rspfile_;
SubstitutionPattern rspfile_content_;
LabelPtrPair<Pool> pool_;
- bool complete_;
+ bool complete_ = false;
SubstitutionBits substitution_bits_;
diff --git a/gn/tools/gn/toolchain.cc b/gn/tools/gn/toolchain.cc
index d9aff43e060..906068d030a 100644
--- a/gn/tools/gn/toolchain.cc
+++ b/gn/tools/gn/toolchain.cc
@@ -12,22 +12,6 @@
#include "tools/gn/target.h"
#include "tools/gn/value.h"
-const char* Toolchain::kToolCc = "cc";
-const char* Toolchain::kToolCxx = "cxx";
-const char* Toolchain::kToolObjC = "objc";
-const char* Toolchain::kToolObjCxx = "objcxx";
-const char* Toolchain::kToolRc = "rc";
-const char* Toolchain::kToolAsm = "asm";
-const char* Toolchain::kToolAlink = "alink";
-const char* Toolchain::kToolSolink = "solink";
-const char* Toolchain::kToolSolinkModule = "solink_module";
-const char* Toolchain::kToolLink = "link";
-const char* Toolchain::kToolStamp = "stamp";
-const char* Toolchain::kToolCopy = "copy";
-const char* Toolchain::kToolCopyBundleData = "copy_bundle_data";
-const char* Toolchain::kToolCompileXCAssets = "compile_xcassets";
-const char* Toolchain::kToolAction = "action";
-
Toolchain::Toolchain(const Settings* settings,
const Label& label,
const std::set<SourceFile>& build_dependency_files)
@@ -43,170 +27,114 @@ const Toolchain* Toolchain::AsToolchain() const {
return this;
}
-// static
-Toolchain::ToolType Toolchain::ToolNameToType(const base::StringPiece& str) {
- if (str == kToolCc)
- return TYPE_CC;
- if (str == kToolCxx)
- return TYPE_CXX;
- if (str == kToolObjC)
- return TYPE_OBJC;
- if (str == kToolObjCxx)
- return TYPE_OBJCXX;
- if (str == kToolRc)
- return TYPE_RC;
- if (str == kToolAsm)
- return TYPE_ASM;
- if (str == kToolAlink)
- return TYPE_ALINK;
- if (str == kToolSolink)
- return TYPE_SOLINK;
- if (str == kToolSolinkModule)
- return TYPE_SOLINK_MODULE;
- if (str == kToolLink)
- return TYPE_LINK;
- if (str == kToolStamp)
- return TYPE_STAMP;
- if (str == kToolCopy)
- return TYPE_COPY;
- if (str == kToolCopyBundleData)
- return TYPE_COPY_BUNDLE_DATA;
- if (str == kToolCompileXCAssets)
- return TYPE_COMPILE_XCASSETS;
- if (str == kToolAction)
- return TYPE_ACTION;
- return TYPE_NONE;
-}
-
-// static
-std::string Toolchain::ToolTypeToName(ToolType type) {
- switch (type) {
- case TYPE_CC:
- return kToolCc;
- case TYPE_CXX:
- return kToolCxx;
- case TYPE_OBJC:
- return kToolObjC;
- case TYPE_OBJCXX:
- return kToolObjCxx;
- case TYPE_RC:
- return kToolRc;
- case TYPE_ASM:
- return kToolAsm;
- case TYPE_ALINK:
- return kToolAlink;
- case TYPE_SOLINK:
- return kToolSolink;
- case TYPE_SOLINK_MODULE:
- return kToolSolinkModule;
- case TYPE_LINK:
- return kToolLink;
- case TYPE_STAMP:
- return kToolStamp;
- case TYPE_COPY:
- return kToolCopy;
- case TYPE_COPY_BUNDLE_DATA:
- return kToolCopyBundleData;
- case TYPE_COMPILE_XCASSETS:
- return kToolCompileXCAssets;
- case TYPE_ACTION:
- return kToolAction;
- default:
- NOTREACHED();
- return std::string();
+Tool* Toolchain::GetTool(const char* name) {
+ DCHECK(name != Tool::kToolNone);
+ auto pair = tools_.find(name);
+ if (pair != tools_.end()) {
+ return pair->second.get();
+ }
+ return nullptr;
+}
+
+const Tool* Toolchain::GetTool(const char* name) const {
+ DCHECK(name != Tool::kToolNone);
+ auto pair = tools_.find(name);
+ if (pair != tools_.end()) {
+ return pair->second.get();
+ }
+ return nullptr;
+}
+
+GeneralTool* Toolchain::GetToolAsGeneral(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsGeneral();
+ }
+ return nullptr;
+}
+
+const GeneralTool* Toolchain::GetToolAsGeneral(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsGeneral();
+ }
+ return nullptr;
+}
+
+CTool* Toolchain::GetToolAsC(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsC();
}
+ return nullptr;
}
-Tool* Toolchain::GetTool(ToolType type) {
- DCHECK(type != TYPE_NONE);
- return tools_[static_cast<size_t>(type)].get();
+const CTool* Toolchain::GetToolAsC(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsC();
+ }
+ return nullptr;
}
-const Tool* Toolchain::GetTool(ToolType type) const {
- DCHECK(type != TYPE_NONE);
- return tools_[static_cast<size_t>(type)].get();
+RustTool* Toolchain::GetToolAsRust(const char* name) {
+ if (Tool* tool = GetTool(name)) {
+ return tool->AsRust();
+ }
+ return nullptr;
}
-void Toolchain::SetTool(ToolType type, std::unique_ptr<Tool> t) {
- DCHECK(type != TYPE_NONE);
- DCHECK(!tools_[type].get());
+const RustTool* Toolchain::GetToolAsRust(const char* name) const {
+ if (const Tool* tool = GetTool(name)) {
+ return tool->AsRust();
+ }
+ return nullptr;
+}
+
+void Toolchain::SetTool(std::unique_ptr<Tool> t) {
+ DCHECK(t->name() != Tool::kToolNone);
+ DCHECK(tools_.find(t->name()) == tools_.end());
t->SetComplete();
- tools_[type] = std::move(t);
+ tools_[t->name()] = std::move(t);
}
void Toolchain::ToolchainSetupComplete() {
// Collect required bits from all tools.
for (const auto& tool : tools_) {
- if (tool)
- substitution_bits_.MergeFrom(tool->substitution_bits());
+ substitution_bits_.MergeFrom(tool.second->substitution_bits());
}
-
setup_complete_ = true;
}
-// static
-Toolchain::ToolType Toolchain::GetToolTypeForSourceType(SourceFileType type) {
- switch (type) {
- case SOURCE_C:
- return TYPE_CC;
- case SOURCE_CPP:
- return TYPE_CXX;
- case SOURCE_M:
- return TYPE_OBJC;
- case SOURCE_MM:
- return TYPE_OBJCXX;
- case SOURCE_ASM:
- case SOURCE_S:
- return TYPE_ASM;
- case SOURCE_RC:
- return TYPE_RC;
- case SOURCE_UNKNOWN:
- case SOURCE_H:
- case SOURCE_O:
- case SOURCE_DEF:
- return TYPE_NONE;
- default:
- NOTREACHED();
- return TYPE_NONE;
- }
+const Tool* Toolchain::GetToolForSourceType(SourceFile::Type type) const {
+ return GetTool(Tool::GetToolTypeForSourceType(type));
}
-const Tool* Toolchain::GetToolForSourceType(SourceFileType type) {
- return tools_[GetToolTypeForSourceType(type)].get();
-}
-
-// static
-Toolchain::ToolType Toolchain::GetToolTypeForTargetFinalOutput(
- const Target* target) {
- // The contents of this list might be suprising (i.e. stamp tool for copy
- // rules). See the header for why.
- // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
- switch (target->output_type()) {
- case Target::GROUP:
- return TYPE_STAMP;
- case Target::EXECUTABLE:
- return Toolchain::TYPE_LINK;
- case Target::SHARED_LIBRARY:
- return Toolchain::TYPE_SOLINK;
- case Target::LOADABLE_MODULE:
- return Toolchain::TYPE_SOLINK_MODULE;
- case Target::STATIC_LIBRARY:
- return Toolchain::TYPE_ALINK;
- case Target::SOURCE_SET:
- return TYPE_STAMP;
- case Target::ACTION:
- case Target::ACTION_FOREACH:
- case Target::BUNDLE_DATA:
- case Target::CREATE_BUNDLE:
- case Target::COPY_FILES:
- case Target::GENERATED_FILE:
- return TYPE_STAMP;
- default:
- NOTREACHED();
- return Toolchain::TYPE_NONE;
- }
+const CTool* Toolchain::GetToolForSourceTypeAsC(SourceFile::Type type) const {
+ return GetToolAsC(Tool::GetToolTypeForSourceType(type));
+}
+
+const GeneralTool* Toolchain::GetToolForSourceTypeAsGeneral(
+ SourceFile::Type type) const {
+ return GetToolAsGeneral(Tool::GetToolTypeForSourceType(type));
+}
+
+const RustTool* Toolchain::GetToolForSourceTypeAsRust(
+ SourceFile::Type type) const {
+ return GetToolAsRust(Tool::GetToolTypeForSourceType(type));
}
const Tool* Toolchain::GetToolForTargetFinalOutput(const Target* target) const {
- return tools_[GetToolTypeForTargetFinalOutput(target)].get();
+ return GetTool(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const CTool* Toolchain::GetToolForTargetFinalOutputAsC(
+ const Target* target) const {
+ return GetToolAsC(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const GeneralTool* Toolchain::GetToolForTargetFinalOutputAsGeneral(
+ const Target* target) const {
+ return GetToolAsGeneral(Tool::GetToolTypeForTargetFinalOutput(target));
+}
+
+const RustTool* Toolchain::GetToolForTargetFinalOutputAsRust(
+ const Target* target) const {
+ return GetToolAsRust(Tool::GetToolTypeForTargetFinalOutput(target));
}
diff --git a/gn/tools/gn/toolchain.h b/gn/tools/gn/toolchain.h
index dd8637cf5ea..afab7f302c8 100644
--- a/gn/tools/gn/toolchain.h
+++ b/gn/tools/gn/toolchain.h
@@ -12,7 +12,6 @@
#include "tools/gn/item.h"
#include "tools/gn/label_ptr.h"
#include "tools/gn/scope.h"
-#include "tools/gn/source_file_type.h"
#include "tools/gn/substitution_type.h"
#include "tools/gn/tool.h"
#include "tools/gn/value.h"
@@ -31,43 +30,6 @@
// be accessed until this Item is resolved.
class Toolchain : public Item {
public:
- enum ToolType {
- TYPE_NONE = 0,
- TYPE_CC,
- TYPE_CXX,
- TYPE_OBJC,
- TYPE_OBJCXX,
- TYPE_RC,
- TYPE_ASM,
- TYPE_ALINK,
- TYPE_SOLINK,
- TYPE_SOLINK_MODULE,
- TYPE_LINK,
- TYPE_STAMP,
- TYPE_COPY,
- TYPE_COPY_BUNDLE_DATA,
- TYPE_COMPILE_XCASSETS,
- TYPE_ACTION,
-
- TYPE_NUMTYPES // Must be last.
- };
-
- static const char* kToolCc;
- static const char* kToolCxx;
- static const char* kToolObjC;
- static const char* kToolObjCxx;
- static const char* kToolRc;
- static const char* kToolAsm;
- static const char* kToolAlink;
- static const char* kToolSolink;
- static const char* kToolSolinkModule;
- static const char* kToolLink;
- static const char* kToolStamp;
- static const char* kToolCopy;
- static const char* kToolCopyBundleData;
- static const char* kToolCompileXCAssets;
- static const char* kToolAction;
-
// The Settings of an Item is always the context in which the Item was
// defined. For a toolchain this is confusing because this is NOT the
// settings object that applies to the things in the toolchain.
@@ -89,17 +51,21 @@ class Toolchain : public Item {
Toolchain* AsToolchain() override;
const Toolchain* AsToolchain() const override;
- // Returns TYPE_NONE on failure.
- static ToolType ToolNameToType(const base::StringPiece& str);
- static std::string ToolTypeToName(ToolType type);
-
// Returns null if the tool hasn't been defined.
- Tool* GetTool(ToolType type);
- const Tool* GetTool(ToolType type) const;
+ Tool* GetTool(const char* name);
+ const Tool* GetTool(const char* name) const;
+
+ // Returns null if the tool hasn't been defined or is not the correct type.
+ GeneralTool* GetToolAsGeneral(const char* name);
+ const GeneralTool* GetToolAsGeneral(const char* name) const;
+ CTool* GetToolAsC(const char* name);
+ const CTool* GetToolAsC(const char* name) const;
+ RustTool* GetToolAsRust(const char* name);
+ const RustTool* GetToolAsRust(const char* name) const;
// Set a tool. When all tools are configured, you should call
// ToolchainSetupComplete().
- void SetTool(ToolType type, std::unique_ptr<Tool> t);
+ void SetTool(std::unique_ptr<Tool> t);
// Does final setup on the toolchain once all tools are known.
void ToolchainSetupComplete();
@@ -123,23 +89,32 @@ class Toolchain : public Item {
}
// Returns the tool for compiling the given source file type.
- static ToolType GetToolTypeForSourceType(SourceFileType type);
- const Tool* GetToolForSourceType(SourceFileType type);
+ const Tool* GetToolForSourceType(SourceFile::Type type) const;
+ const CTool* GetToolForSourceTypeAsC(SourceFile::Type type) const;
+ const GeneralTool* GetToolForSourceTypeAsGeneral(SourceFile::Type type) const;
+ const RustTool* GetToolForSourceTypeAsRust(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
// will return the stamp tool instead since the final output of a copy
// target is to stamp the set of copies done so there is one output.
- static ToolType GetToolTypeForTargetFinalOutput(const Target* target);
const Tool* GetToolForTargetFinalOutput(const Target* target) const;
+ const CTool* GetToolForTargetFinalOutputAsC(const Target* target) const;
+ const GeneralTool* GetToolForTargetFinalOutputAsGeneral(
+ const Target* target) const;
+ const RustTool* GetToolForTargetFinalOutputAsRust(const Target* target) const;
const SubstitutionBits& substitution_bits() const {
DCHECK(setup_complete_);
return substitution_bits_;
}
+ const std::map<const char*, std::unique_ptr<Tool>>& tools() const {
+ return tools_;
+ }
+
private:
- std::unique_ptr<Tool> tools_[TYPE_NUMTYPES];
+ std::map<const char*, std::unique_ptr<Tool>> tools_;
bool setup_complete_ = false;
diff --git a/gn/tools/gn/trace.cc b/gn/tools/gn/trace.cc
index 184c4505a8f..58a8c3c72e4 100644
--- a/gn/tools/gn/trace.cc
+++ b/gn/tools/gn/trace.cc
@@ -24,6 +24,8 @@
namespace {
+constexpr uint64_t kNanosecondsToMicroseconds = 1'000;
+
class TraceLog {
public:
TraceLog() { events_.reserve(16384); }
@@ -244,7 +246,7 @@ void SaveTraces(const base::FilePath& file_name) {
// 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\":\"" << std::this_thread::get_id() << "\"";
out << ",\"ts\":0,\"ph\":\"M\",";
out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
@@ -254,8 +256,8 @@ void SaveTraces(const base::FilePath& file_name) {
if (i != 0)
out << ",";
- out << "{\"pid\":0,\"tid\":" << item.thread_id();
- out << ",\"ts\":" << item.begin();
+ out << "{\"pid\":0,\"tid\":\"" << 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/tools/gn/unique_vector.h b/gn/tools/gn/unique_vector.h
index 732b66344d6..ec8bb275b41 100644
--- a/gn/tools/gn/unique_vector.h
+++ b/gn/tools/gn/unique_vector.h
@@ -27,28 +27,20 @@ namespace internal {
template <typename T>
class UniquifyRef {
public:
- UniquifyRef()
- : value_(nullptr),
- vect_(nullptr),
- index_(static_cast<size_t>(-1)),
- hash_val_(0) {}
+ UniquifyRef() = default;
// Initialize with a pointer to a value.
- explicit UniquifyRef(const T* v)
- : value_(v), vect_(nullptr), index_(static_cast<size_t>(-1)) {
- FillHashValue();
- }
+ explicit UniquifyRef(const T* v) : value_(v) { FillHashValue(); }
// Initialize with an array + index.
- UniquifyRef(const std::vector<T>* v, size_t i)
- : value_(nullptr), vect_(v), index_(i) {
+ UniquifyRef(const std::vector<T>* v, size_t i) : vect_(v), index_(i) {
FillHashValue();
}
// Initialize with an array + index and a known hash value to prevent
// re-hashing.
UniquifyRef(const std::vector<T>* v, size_t i, size_t hash_value)
- : value_(nullptr), vect_(v), index_(i), hash_val_(hash_value) {}
+ : vect_(v), index_(i), hash_val_(hash_value) {}
const T& value() const { return value_ ? *value_ : (*vect_)[index_]; }
size_t hash_val() const { return hash_val_; }
@@ -61,13 +53,13 @@ class UniquifyRef {
}
// When non-null, points to the object.
- const T* value_;
+ const T* value_ = nullptr;
// When value is null these are used.
- const std::vector<T>* vect_;
- size_t index_;
+ const std::vector<T>* vect_ = nullptr;
+ size_t index_ = static_cast<size_t>(-1);
- size_t hash_val_;
+ size_t hash_val_ = 0;
};
template <typename T>
@@ -131,18 +123,15 @@ class UniqueVector {
return true;
}
- // Like push_back but swaps in the type to avoid a copy.
- bool PushBackViaSwap(T* t) {
- using std::swap;
-
- Ref ref(t);
+ bool push_back(T&& t) {
+ Ref ref(&t);
if (set_.find(ref) != set_.end())
return false; // Already have this one.
- size_t new_index = vector_.size();
- vector_.resize(new_index + 1);
- swap(vector_[new_index], *t);
- set_.insert(Ref(&vector_, vector_.size() - 1, ref.hash_val()));
+ auto ref_hash_val = ref.hash_val(); // Save across moving t.
+
+ vector_.push_back(std::move(t)); // Invalidates |ref|.
+ set_.insert(Ref(&vector_, vector_.size() - 1, ref_hash_val));
return true;
}
diff --git a/gn/tools/gn/unique_vector_unittest.cc b/gn/tools/gn/unique_vector_unittest.cc
index afea1e24a00..008d2846632 100644
--- a/gn/tools/gn/unique_vector_unittest.cc
+++ b/gn/tools/gn/unique_vector_unittest.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "tools/gn/unique_vector.h"
+
#include <stddef.h>
#include <algorithm>
-#include "tools/gn/unique_vector.h"
#include "util/test/test.h"
TEST(UniqueVector, PushBack) {
@@ -30,14 +31,14 @@ TEST(UniqueVector, PushBack) {
EXPECT_EQ(static_cast<size_t>(-1), foo.IndexOf(99));
}
-TEST(UniqueVector, PushBackViaSwap) {
+TEST(UniqueVector, PushBackMove) {
UniqueVector<std::string> vect;
std::string a("a");
- EXPECT_TRUE(vect.PushBackViaSwap(&a));
+ EXPECT_TRUE(vect.push_back(std::move(a)));
EXPECT_EQ("", a);
a = "a";
- EXPECT_FALSE(vect.PushBackViaSwap(&a));
+ EXPECT_FALSE(vect.push_back(std::move(a)));
EXPECT_EQ("a", a);
EXPECT_EQ(0u, vect.IndexOf("a"));
diff --git a/gn/tools/gn/variables.cc b/gn/tools/gn/variables.cc
index a809a1cc54d..74781aab7bb 100644
--- a/gn/tools/gn/variables.cc
+++ b/gn/tools/gn/variables.cc
@@ -4,6 +4,8 @@
#include "tools/gn/variables.h"
+#include "tools/gn/rust_variables.h"
+
namespace variables {
// Built-in variables ----------------------------------------------------------
@@ -1260,7 +1262,7 @@ const char kLdflags_Help[] =
#define COMMON_LIB_INHERITANCE_HELP \
"\n" \
" libs and lib_dirs work differently than other flags in two respects.\n" \
- " First, then are inherited across static library boundaries until a\n" \
+ " First, they are inherited across static library boundaries until a\n" \
" shared library or executable target is reached. Second, they are\n" \
" uniquified so each one is only passed once (the first instance of it\n" \
" will be the one used).\n"
@@ -1917,6 +1919,10 @@ Sources for binary targets
static library or source set will have no effect on the executable or shared
library they're linked into).
+ For Rust targets that do not specify a crate_root, then the crate_root will
+ look for a lib.rs file (or main.rs for executable) or a single file in
+ sources, if sources contains only one file.
+
Sources for non-binary targets
action_foreach
@@ -2036,13 +2042,13 @@ const char kWalkKeys_HelpShort[] =
const char kWalkKeys_Help[] =
R"(walk_keys: Key(s) for managing the metadata collection walk.
- Defaults to [].
+ Defaults to [""].
These keys are used to control the next step in a collection walk, acting as
barriers. If a specified key is defined in a target's metadata, the walk will
use the targets listed in that value to determine which targets are walked.
- If no walk_keys are specified for a generated_file target (i.e. "[]"), the
+ If no walk_keys are specified for a generated_file target (i.e. "[""]"), the
walk will touch all deps and data_deps of the specified target recursively.
See "gn help generated_file".
@@ -2065,7 +2071,7 @@ const char kWriteOutputConversion_Help[] =
R"("output_conversion: Data format for generated_file targets.
Controls how the "contents" of a generated_file target is formatted.
- See "gn help output_conversion".
+ See "gn help io_conversion".
)";
const char kWriteRuntimeDeps[] = "write_runtime_deps";
@@ -2203,6 +2209,7 @@ const VariableInfoMap& GetTargetVariables() {
INSERT_VARIABLE(WriteValueContents)
INSERT_VARIABLE(WriteRuntimeDeps)
INSERT_VARIABLE(XcodeExtraAttributes)
+ InsertRustVariables(&info_map);
}
return info_map;
}
diff --git a/gn/tools/gn/visual_studio_writer.cc b/gn/tools/gn/visual_studio_writer.cc
index 9870fbcf4f7..5f4380cdab5 100644
--- a/gn/tools/gn/visual_studio_writer.cc
+++ b/gn/tools/gn/visual_studio_writer.cc
@@ -24,7 +24,6 @@
#include "tools/gn/label_pattern.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/path_output.h"
-#include "tools/gn/source_file_type.h"
#include "tools/gn/standard_out.h"
#include "tools/gn/target.h"
#include "tools/gn/variables.h"
@@ -611,8 +610,8 @@ bool VisualStudioWriter::WriteProjectFileContents(
for (const SourceFile& file : target->sources()) {
const char* compile_type;
- Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
- if (target->GetOutputFilesForSource(file, &tool_type, &tool_outputs)) {
+ const char* tool_name = Tool::kToolNone;
+ if (target->GetOutputFilesForSource(file, &tool_name, &tool_outputs)) {
compile_type = "CustomBuild";
std::unique_ptr<XmlElementWriter> build = group->SubElement(
compile_type, "Include", SourceFileWriter(path_output, file));
@@ -818,7 +817,7 @@ void VisualStudioWriter::ResolveSolutionFolders() {
} else {
std::string folder_path_str = folder_path.as_string();
std::unique_ptr<SolutionEntry> folder = std::make_unique<SolutionEntry>(
- FindLastDirComponent(SourceDir(folder_path)).as_string(),
+ FindLastDirComponent(SourceDir(std::string(folder_path))).as_string(),
folder_path_str, MakeGuid(folder_path_str, kGuidSeedFolder));
project->parent_folder = folder.get();
processed_paths[folder_path] = folder.get();
@@ -864,7 +863,8 @@ void VisualStudioWriter::ResolveSolutionFolders() {
} else {
std::unique_ptr<SolutionEntry> new_folder =
std::make_unique<SolutionEntry>(
- FindLastDirComponent(SourceDir(parent_path)).as_string(),
+ FindLastDirComponent(SourceDir(std::string(parent_path)))
+ .as_string(),
parent_path.as_string(),
MakeGuid(parent_path.as_string(), kGuidSeedFolder));
processed_paths[parent_path] = new_folder.get();
diff --git a/gn/tools/gn/visual_studio_writer_unittest.cc b/gn/tools/gn/visual_studio_writer_unittest.cc
index 165bbb47d54..36b9bcce709 100644
--- a/gn/tools/gn/visual_studio_writer_unittest.cc
+++ b/gn/tools/gn/visual_studio_writer_unittest.cc
@@ -175,12 +175,12 @@ TEST_F(VisualStudioWriterTest, NoDotSlash) {
"base", path, MakeGuid(path, "project"), MakeTestPath("/foo"),
"Win32"));
- std::unique_ptr<Tool> tool = std::make_unique<Tool>();
+ std::unique_ptr<Tool> tool = Tool::CreateTool(CTool::kCToolAlink);
tool->set_outputs(SubstitutionList::MakeForTest(
"{{root_out_dir}}/{{target_output_name}}{{output_extension}}", ""));
Toolchain toolchain(setup_.settings(), Label(SourceDir("//tc/"), "tc"));
- toolchain.SetTool(Toolchain::TYPE_ALINK, std::move(tool));
+ toolchain.SetTool(std::move(tool));
Target target(setup_.settings(), Label(SourceDir("//baz/"), "baz"));
target.set_output_type(Target::STATIC_LIBRARY);
diff --git a/gn/util/exe_path.cc b/gn/util/exe_path.cc
index 2f80d5a1fa1..3c9c9deb35b 100644
--- a/gn/util/exe_path.cc
+++ b/gn/util/exe_path.cc
@@ -14,6 +14,7 @@
#elif defined(OS_WIN)
#include <windows.h>
#elif defined(OS_FREEBSD)
+#include <limits.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
diff --git a/gn/util/sys_info.cc b/gn/util/sys_info.cc
index a1ce3e92a22..5fefc9b4393 100644
--- a/gn/util/sys_info.cc
+++ b/gn/util/sys_info.cc
@@ -72,9 +72,7 @@ int NumberOfProcessors() {
return static_cast<int>(res);
#elif defined(OS_WIN)
- SYSTEM_INFO system_info = {};
- ::GetNativeSystemInfo(&system_info);
- return system_info.dwNumberOfProcessors;
+ return ::GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
#else
#error
#endif
diff --git a/gn/util/worker_pool.cc b/gn/util/worker_pool.cc
index 92fadd44eea..60bcfbf099e 100644
--- a/gn/util/worker_pool.cc
+++ b/gn/util/worker_pool.cc
@@ -7,10 +7,50 @@
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "tools/gn/switches.h"
+#include "util/build_config.h"
#include "util/sys_info.h"
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
namespace {
+#if defined(OS_WIN)
+class ProcessorGroupSetter {
+ public:
+ void SetProcessorGroup(std::thread* thread);
+
+ private:
+ int group_ = 0;
+ GROUP_AFFINITY group_affinity_;
+ int num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
+ const int num_groups_ = ::GetActiveProcessorGroupCount();
+};
+
+void ProcessorGroupSetter::SetProcessorGroup(std::thread* thread) {
+ if (num_groups_ <= 1)
+ return;
+
+ const HANDLE thread_handle = thread->native_handle();
+ ::GetThreadGroupAffinity(thread_handle, &group_affinity_);
+ group_affinity_.Group = group_;
+ const bool success =
+ ::SetThreadGroupAffinity(thread_handle, &group_affinity_, nullptr);
+ DCHECK(success);
+
+ // Move to next group once one thread has been assigned per core in |group_|.
+ num_available_cores_in_group_--;
+ if (num_available_cores_in_group_ <= 0) {
+ group_++;
+ if (group_ >= num_groups_) {
+ group_ = 0;
+ }
+ num_available_cores_in_group_ = ::GetActiveProcessorCount(group_) / 2;
+ }
+}
+#endif
+
int GetThreadCount() {
std::string thread_count =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
@@ -46,9 +86,22 @@ int GetThreadCount() {
WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {}
WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) {
+#if defined(OS_WIN)
+ ProcessorGroupSetter processor_group_setter;
+#endif
+
threads_.reserve(thread_count);
- for (size_t i = 0; i < thread_count; ++i)
+ for (size_t i = 0; i < thread_count; ++i) {
threads_.emplace_back([this]() { Worker(); });
+
+#if defined(OS_WIN)
+ // Set thread processor group. This is needed for systems with more than 64
+ // logical processors, wherein available processors are divided into groups,
+ // and applications that need to use more than one group's processors must
+ // manually assign their threads to groups.
+ processor_group_setter.SetProcessorGroup(&threads_.back());
+#endif
+ }
}
WorkerPool::~WorkerPool() {