Add ICU message format support
[chromium-blink-merge.git] / tools / gn / ninja_target_writer.cc
blob7913735421450842c71a20627d8db601089a8e49
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/ninja_target_writer.h"
7 #include <sstream>
9 #include "base/files/file_util.h"
10 #include "base/strings/string_util.h"
11 #include "tools/gn/err.h"
12 #include "tools/gn/filesystem_utils.h"
13 #include "tools/gn/ninja_action_target_writer.h"
14 #include "tools/gn/ninja_binary_target_writer.h"
15 #include "tools/gn/ninja_copy_target_writer.h"
16 #include "tools/gn/ninja_group_target_writer.h"
17 #include "tools/gn/ninja_utils.h"
18 #include "tools/gn/output_file.h"
19 #include "tools/gn/scheduler.h"
20 #include "tools/gn/string_utils.h"
21 #include "tools/gn/substitution_writer.h"
22 #include "tools/gn/target.h"
23 #include "tools/gn/trace.h"
25 NinjaTargetWriter::NinjaTargetWriter(const Target* target,
26 std::ostream& out)
27 : settings_(target->settings()),
28 target_(target),
29 out_(out),
30 path_output_(settings_->build_settings()->build_dir(),
31 settings_->build_settings()->root_path_utf8(),
32 ESCAPE_NINJA) {
35 NinjaTargetWriter::~NinjaTargetWriter() {
38 // static
39 void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
40 const Settings* settings = target->settings();
42 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE,
43 target->label().GetUserVisibleName(false));
44 trace.SetToolchain(settings->toolchain_label());
46 base::FilePath ninja_file(settings->build_settings()->GetFullPath(
47 GetNinjaFileForTarget(target)));
49 if (g_scheduler->verbose_logging())
50 g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
52 base::CreateDirectory(ninja_file.DirName());
54 // It's rediculously faster to write to a string and then write that to
55 // disk in one operation than to use an fstream here.
56 std::stringstream file;
58 // Call out to the correct sub-type of writer.
59 if (target->output_type() == Target::COPY_FILES) {
60 NinjaCopyTargetWriter writer(target, file);
61 writer.Run();
62 } else if (target->output_type() == Target::ACTION ||
63 target->output_type() == Target::ACTION_FOREACH) {
64 NinjaActionTargetWriter writer(target, file);
65 writer.Run();
66 } else if (target->output_type() == Target::GROUP) {
67 NinjaGroupTargetWriter writer(target, file);
68 writer.Run();
69 } else if (target->output_type() == Target::EXECUTABLE ||
70 target->output_type() == Target::STATIC_LIBRARY ||
71 target->output_type() == Target::SHARED_LIBRARY ||
72 target->output_type() == Target::SOURCE_SET) {
73 NinjaBinaryTargetWriter writer(target, file);
74 writer.Run();
75 } else {
76 CHECK(0);
79 std::string contents = file.str();
80 base::WriteFile(ninja_file, contents.c_str(),
81 static_cast<int>(contents.size()));
84 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) {
85 bool written_anything = false;
87 // Target label.
88 if (bits.used[SUBSTITUTION_LABEL]) {
89 out_ << kSubstitutionNinjaNames[SUBSTITUTION_LABEL] << " = "
90 << SubstitutionWriter::GetTargetSubstitution(
91 target_, SUBSTITUTION_LABEL)
92 << std::endl;
93 written_anything = true;
96 // Root gen dir.
97 if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) {
98 out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_GEN_DIR] << " = "
99 << SubstitutionWriter::GetTargetSubstitution(
100 target_, SUBSTITUTION_ROOT_GEN_DIR)
101 << std::endl;
102 written_anything = true;
105 // Root out dir.
106 if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) {
107 out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_OUT_DIR] << " = "
108 << SubstitutionWriter::GetTargetSubstitution(
109 target_, SUBSTITUTION_ROOT_OUT_DIR)
110 << std::endl;
111 written_anything = true;
114 // Target gen dir.
115 if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) {
116 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_GEN_DIR] << " = "
117 << SubstitutionWriter::GetTargetSubstitution(
118 target_, SUBSTITUTION_TARGET_GEN_DIR)
119 << std::endl;
120 written_anything = true;
123 // Target out dir.
124 if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) {
125 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUT_DIR] << " = "
126 << SubstitutionWriter::GetTargetSubstitution(
127 target_, SUBSTITUTION_TARGET_OUT_DIR)
128 << std::endl;
129 written_anything = true;
132 // Target output name.
133 if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) {
134 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUTPUT_NAME] << " = "
135 << SubstitutionWriter::GetTargetSubstitution(
136 target_, SUBSTITUTION_TARGET_OUTPUT_NAME)
137 << std::endl;
138 written_anything = true;
141 // If we wrote any vars, separate them from the rest of the file that follows
142 // with a blank line.
143 if (written_anything)
144 out_ << std::endl;
147 OutputFile NinjaTargetWriter::WriteInputDepsStampAndGetDep(
148 const std::vector<const Target*>& extra_hard_deps) const {
149 CHECK(target_->toolchain())
150 << "Toolchain not set on target "
151 << target_->label().GetUserVisibleName(true);
153 // For an action (where we run a script only once) the sources are the same
154 // as the source prereqs.
155 bool list_sources_as_input_deps = (target_->output_type() == Target::ACTION);
157 // Actions get implicit dependencies on the script itself.
158 bool add_script_source_as_dep =
159 (target_->output_type() == Target::ACTION) ||
160 (target_->output_type() == Target::ACTION_FOREACH);
162 if (!add_script_source_as_dep &&
163 extra_hard_deps.empty() &&
164 target_->inputs().empty() &&
165 target_->recursive_hard_deps().empty() &&
166 (!list_sources_as_input_deps || target_->sources().empty()) &&
167 target_->toolchain()->deps().empty())
168 return OutputFile(); // No input/hard deps.
170 // One potential optimization is if there are few input dependencies (or
171 // potentially few sources that depend on these) it's better to just write
172 // all hard deps on each sources line than have this intermediate stamp. We
173 // do the stamp file because duplicating all the order-only deps for each
174 // source file can really explode the ninja file but this won't be the most
175 // optimal thing in all cases.
177 OutputFile input_stamp_file(
178 RebasePath(GetTargetOutputDir(target_).value(),
179 settings_->build_settings()->build_dir(),
180 settings_->build_settings()->root_path_utf8()));
181 input_stamp_file.value().append(target_->label().name());
182 input_stamp_file.value().append(".inputdeps.stamp");
184 out_ << "build ";
185 path_output_.WriteFile(out_, input_stamp_file);
186 out_ << ": "
187 << GetNinjaRulePrefixForToolchain(settings_)
188 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
190 // Script file (if applicable).
191 if (add_script_source_as_dep) {
192 out_ << " ";
193 path_output_.WriteFile(out_, target_->action_values().script());
196 // Input files are order-only deps.
197 for (const auto& input : target_->inputs()) {
198 out_ << " ";
199 path_output_.WriteFile(out_, input);
201 if (list_sources_as_input_deps) {
202 for (const auto& source : target_->sources()) {
203 out_ << " ";
204 path_output_.WriteFile(out_, source);
208 // The different souces of input deps may duplicate some targets, so uniquify
209 // them (ordering doesn't matter for this case).
210 std::set<const Target*> unique_deps;
212 // Hard dependencies that are direct or indirect dependencies.
213 const std::set<const Target*>& hard_deps = target_->recursive_hard_deps();
214 for (const auto& dep : hard_deps)
215 unique_deps.insert(dep);
217 // Extra hard dependencies passed in.
218 unique_deps.insert(extra_hard_deps.begin(), extra_hard_deps.end());
220 // Toolchain dependencies. These must be resolved before doing anything.
221 // This just writs all toolchain deps for simplicity. If we find that
222 // toolchains often have more than one dependency, we could consider writing
223 // a toolchain-specific stamp file and only include the stamp here.
224 const LabelTargetVector& toolchain_deps = target_->toolchain()->deps();
225 for (const auto& toolchain_dep : toolchain_deps)
226 unique_deps.insert(toolchain_dep.ptr);
228 for (const auto& dep : unique_deps) {
229 DCHECK(!dep->dependency_output_file().value().empty());
230 out_ << " ";
231 path_output_.WriteFile(out_, dep->dependency_output_file());
234 out_ << "\n";
235 return input_stamp_file;
238 void NinjaTargetWriter::WriteStampForTarget(
239 const std::vector<OutputFile>& files,
240 const std::vector<OutputFile>& order_only_deps) {
241 const OutputFile& stamp_file = target_->dependency_output_file();
243 // First validate that the target's dependency is a stamp file. Otherwise,
244 // we shouldn't have gotten here!
245 CHECK(base::EndsWith(stamp_file.value(), ".stamp",
246 base::CompareCase::INSENSITIVE_ASCII))
247 << "Output should end in \".stamp\" for stamp file output. Instead got: "
248 << "\"" << stamp_file.value() << "\"";
250 out_ << "build ";
251 path_output_.WriteFile(out_, stamp_file);
253 out_ << ": "
254 << GetNinjaRulePrefixForToolchain(settings_)
255 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP);
256 path_output_.WriteFiles(out_, files);
258 if (!order_only_deps.empty()) {
259 out_ << " ||";
260 path_output_.WriteFiles(out_, order_only_deps);
262 out_ << std::endl;