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