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_binary_target_writer.h"
6
7 #include <set>
8 #include <sstream>
9
10 #include "base/strings/string_util.h"
11 #include "tools/gn/config_values_extractors.h"
12 #include "tools/gn/deps_iterator.h"
13 #include "tools/gn/err.h"
14 #include "tools/gn/escape.h"
15 #include "tools/gn/ninja_utils.h"
16 #include "tools/gn/settings.h"
17 #include "tools/gn/string_utils.h"
18 #include "tools/gn/substitution_writer.h"
19 #include "tools/gn/target.h"
20
21 namespace {
22
23 // Returns the proper escape options for writing compiler and linker flags.
GetFlagOptions()24 EscapeOptions GetFlagOptions() {
25 EscapeOptions opts;
26 opts.mode = ESCAPE_NINJA_COMMAND;
27
28 // Some flag strings are actually multiple flags that expect to be just
29 // added to the command line. We assume that quoting is done by the
30 // buildfiles if it wants such things quoted.
31 opts.inhibit_quoting = true;
32
33 return opts;
34 }
35
36 struct DefineWriter {
DefineWriter__anon5b128fd00111::DefineWriter37 DefineWriter() {
38 options.mode = ESCAPE_NINJA_COMMAND;
39 }
40
operator ()__anon5b128fd00111::DefineWriter41 void operator()(const std::string& s, std::ostream& out) const {
42 out << " -D";
43 EscapeStringToStream(out, s, options);
44 }
45
46 EscapeOptions options;
47 };
48
49 struct IncludeWriter {
IncludeWriter__anon5b128fd00111::IncludeWriter50 IncludeWriter(PathOutput& path_output) : path_output_(path_output) {
51 }
~IncludeWriter__anon5b128fd00111::IncludeWriter52 ~IncludeWriter() {
53 }
54
operator ()__anon5b128fd00111::IncludeWriter55 void operator()(const SourceDir& d, std::ostream& out) const {
56 std::ostringstream path_out;
57 path_output_.WriteDir(path_out, d, PathOutput::DIR_NO_LAST_SLASH);
58 const std::string& path = path_out.str();
59 if (path[0] == '"')
60 out << " \"-I" << path.substr(1);
61 else
62 out << " -I" << path;
63 }
64
65 PathOutput& path_output_;
66 };
67
68 } // namespace
69
NinjaBinaryTargetWriter(const Target * target,std::ostream & out)70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
71 std::ostream& out)
72 : NinjaTargetWriter(target, out),
73 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) {
74 }
75
~NinjaBinaryTargetWriter()76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
77 }
78
Run()79 void NinjaBinaryTargetWriter::Run() {
80 WriteCompilerVars();
81
82 std::vector<OutputFile> obj_files;
83 WriteSources(&obj_files);
84
85 if (target_->output_type() == Target::SOURCE_SET)
86 WriteSourceSetStamp(obj_files);
87 else
88 WriteLinkerStuff(obj_files);
89 }
90
WriteCompilerVars()91 void NinjaBinaryTargetWriter::WriteCompilerVars() {
92 const SubstitutionBits& subst = target_->toolchain()->substitution_bits();
93
94 // Defines.
95 if (subst.used[SUBSTITUTION_DEFINES]) {
96 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " =";
97 RecursiveTargetConfigToStream<std::string>(
98 target_, &ConfigValues::defines, DefineWriter(), out_);
99 out_ << std::endl;
100 }
101
102 // Include directories.
103 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) {
104 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " =";
105 PathOutput include_path_output(path_output_.current_dir(),
106 ESCAPE_NINJA_COMMAND);
107 RecursiveTargetConfigToStream<SourceDir>(
108 target_, &ConfigValues::include_dirs,
109 IncludeWriter(include_path_output), out_);
110 out_ << std::endl;
111 }
112
113 // C flags and friends.
114 EscapeOptions flag_escape_options = GetFlagOptions();
115 #define WRITE_FLAGS(name, subst_enum) \
116 if (subst.used[subst_enum]) { \
117 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \
118 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \
119 flag_escape_options, out_); \
120 out_ << std::endl; \
121 }
122
123 WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS)
124 WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C)
125 WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC)
126 WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC)
127 WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC)
128
129 #undef WRITE_FLAGS
130
131 WriteSharedVars(subst);
132 }
133
WriteSources(std::vector<OutputFile> * object_files)134 void NinjaBinaryTargetWriter::WriteSources(
135 std::vector<OutputFile>* object_files) {
136 const Target::FileList& sources = target_->sources();
137 object_files->reserve(sources.size());
138
139 OutputFile input_dep =
140 WriteInputDepsStampAndGetDep(std::vector<const Target*>());
141
142 std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
143
144 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop.
145 for (size_t i = 0; i < sources.size(); i++) {
146 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
147 if (!GetOutputFilesForSource(target_, sources[i],
148 &tool_type, &tool_outputs))
149 continue; // No output for this source.
150
151 if (tool_type != Toolchain::TYPE_NONE) {
152 out_ << "build";
153 path_output_.WriteFiles(out_, tool_outputs);
154 out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type);
155 out_ << " ";
156 path_output_.WriteFile(out_, sources[i]);
157 if (!input_dep.value().empty()) {
158 // Write out the input dependencies as an order-only dependency. This
159 // will cause Ninja to make sure the inputs are up-to-date before
160 // compiling this source, but changes in the inputs deps won't cause
161 // the file to be recompiled.
162 //
163 // This is important to prevent changes in unrelated actions that
164 // are upstream of this target from causing everything to be recompiled.
165 //
166 // Why can we get away with this rather than using implicit deps ("|",
167 // which will force rebuilds when the inputs change)? For source code,
168 // the computed dependencies of all headers will be computed by the
169 // compiler, which will cause source rebuilds if any "real" upstream
170 // dependencies change.
171 //
172 // If a .cc file is generated by an input dependency, Ninja will see
173 // the input to the build rule doesn't exist, and that it is an output
174 // from a previous step, and build the previous step first. This is a
175 // "real" dependency and doesn't need | or || to express.
176 //
177 // The only case where this rule matters is for the first build where
178 // no .d files exist, and Ninja doesn't know what that source file
179 // depends on. In this case it's sufficient to ensure that the upstream
180 // dependencies are built first. This is exactly what Ninja's order-
181 // only dependencies expresses.
182 out_ << " || ";
183 path_output_.WriteFile(out_, input_dep);
184 }
185 out_ << std::endl;
186 }
187
188 // It's theoretically possible for a compiler to produce more than one
189 // output, but we'll only link to the first output.
190 object_files->push_back(tool_outputs[0]);
191 }
192 out_ << std::endl;
193 }
194
WriteLinkerStuff(const std::vector<OutputFile> & object_files)195 void NinjaBinaryTargetWriter::WriteLinkerStuff(
196 const std::vector<OutputFile>& object_files) {
197 std::vector<OutputFile> output_files;
198 SubstitutionWriter::ApplyListToLinkerAsOutputFile(
199 target_, tool_, tool_->outputs(), &output_files);
200
201 out_ << "build";
202 path_output_.WriteFiles(out_, output_files);
203
204 out_ << ": "
205 << GetNinjaRulePrefixForToolchain(settings_)
206 << Toolchain::ToolTypeToName(
207 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_));
208
209 UniqueVector<OutputFile> extra_object_files;
210 UniqueVector<const Target*> linkable_deps;
211 UniqueVector<const Target*> non_linkable_deps;
212 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
213
214 // Object files.
215 for (size_t i = 0; i < object_files.size(); i++) {
216 out_ << " ";
217 path_output_.WriteFile(out_, object_files[i]);
218 }
219 for (size_t i = 0; i < extra_object_files.size(); i++) {
220 out_ << " ";
221 path_output_.WriteFile(out_, extra_object_files[i]);
222 }
223
224 std::vector<OutputFile> implicit_deps;
225 std::vector<OutputFile> solibs;
226
227 for (size_t i = 0; i < linkable_deps.size(); i++) {
228 const Target* cur = linkable_deps[i];
229
230 // All linkable deps should have a link output file.
231 DCHECK(!cur->link_output_file().value().empty())
232 << "No link output file for "
233 << target_->label().GetUserVisibleName(false);
234
235 if (cur->dependency_output_file().value() !=
236 cur->link_output_file().value()) {
237 // This is a shared library with separate link and deps files. Save for
238 // later.
239 implicit_deps.push_back(cur->dependency_output_file());
240 solibs.push_back(cur->link_output_file());
241 } else {
242 // Normal case, just link to this target.
243 out_ << " ";
244 path_output_.WriteFile(out_, cur->link_output_file());
245 }
246 }
247
248 // Append implicit dependencies collected above.
249 if (!implicit_deps.empty()) {
250 out_ << " |";
251 path_output_.WriteFiles(out_, implicit_deps);
252 }
253
254 // Append data dependencies as order-only dependencies.
255 //
256 // This will include data dependencies and input dependencies (like when
257 // this target depends on an action). Having the data dependencies in this
258 // list ensures that the data is available at runtime when the user builds
259 // this target.
260 //
261 // The action dependencies are not strictly necessary in this case. They
262 // should also have been collected via the input deps stamp that each source
263 // file has for an order-only dependency, and since this target depends on
264 // the sources, there is already an implicit order-only dependency. However,
265 // it's extra work to separate these out and there's no disadvantage to
266 // listing them again.
267 WriteOrderOnlyDependencies(non_linkable_deps);
268
269 // End of the link "build" line.
270 out_ << std::endl;
271
272 // These go in the inner scope of the link line.
273 WriteLinkerFlags();
274 WriteLibs();
275 WriteOutputExtension();
276 WriteSolibs(solibs);
277 }
278
WriteLinkerFlags()279 void NinjaBinaryTargetWriter::WriteLinkerFlags() {
280 out_ << " ldflags =";
281
282 // First the ldflags from the target and its config.
283 EscapeOptions flag_options = GetFlagOptions();
284 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags,
285 flag_options, out_);
286
287 // Followed by library search paths that have been recursively pushed
288 // through the dependency tree.
289 const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs();
290 if (!all_lib_dirs.empty()) {
291 // Since we're passing these on the command line to the linker and not
292 // to Ninja, we need to do shell escaping.
293 PathOutput lib_path_output(path_output_.current_dir(),
294 ESCAPE_NINJA_COMMAND);
295 for (size_t i = 0; i < all_lib_dirs.size(); i++) {
296 out_ << " " << tool_->lib_dir_switch();
297 lib_path_output.WriteDir(out_, all_lib_dirs[i],
298 PathOutput::DIR_NO_LAST_SLASH);
299 }
300 }
301 out_ << std::endl;
302 }
303
WriteLibs()304 void NinjaBinaryTargetWriter::WriteLibs() {
305 out_ << " libs =";
306
307 // Libraries that have been recursively pushed through the dependency tree.
308 EscapeOptions lib_escape_opts;
309 lib_escape_opts.mode = ESCAPE_NINJA_COMMAND;
310 const OrderedSet<std::string> all_libs = target_->all_libs();
311 const std::string framework_ending(".framework");
312 for (size_t i = 0; i < all_libs.size(); i++) {
313 if (settings_->IsMac() && EndsWith(all_libs[i], framework_ending, false)) {
314 // Special-case libraries ending in ".framework" on Mac. Add the
315 // -framework switch and don't add the extension to the output.
316 out_ << " -framework ";
317 EscapeStringToStream(out_,
318 all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()),
319 lib_escape_opts);
320 } else {
321 out_ << " " << tool_->lib_switch();
322 EscapeStringToStream(out_, all_libs[i], lib_escape_opts);
323 }
324 }
325 out_ << std::endl;
326 }
327
WriteOutputExtension()328 void NinjaBinaryTargetWriter::WriteOutputExtension() {
329 out_ << " output_extension = ";
330 if (target_->output_extension().empty()) {
331 // Use the default from the tool.
332 out_ << tool_->default_output_extension();
333 } else {
334 // Use the one specified in the target. Note that the one in the target
335 // does not include the leading dot, so add that.
336 out_ << "." << target_->output_extension();
337 }
338 out_ << std::endl;
339 }
340
WriteSolibs(const std::vector<OutputFile> & solibs)341 void NinjaBinaryTargetWriter::WriteSolibs(
342 const std::vector<OutputFile>& solibs) {
343 if (solibs.empty())
344 return;
345
346 out_ << " solibs =";
347 path_output_.WriteFiles(out_, solibs);
348 out_ << std::endl;
349 }
350
WriteSourceSetStamp(const std::vector<OutputFile> & object_files)351 void NinjaBinaryTargetWriter::WriteSourceSetStamp(
352 const std::vector<OutputFile>& object_files) {
353 // The stamp rule for source sets is generally not used, since targets that
354 // depend on this will reference the object files directly. However, writing
355 // this rule allows the user to type the name of the target and get a build
356 // which can be convenient for development.
357 UniqueVector<OutputFile> extra_object_files;
358 UniqueVector<const Target*> linkable_deps;
359 UniqueVector<const Target*> non_linkable_deps;
360 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps);
361
362 // The classifier should never put extra object files in a source set:
363 // any source sets that we depend on should appear in our non-linkable
364 // deps instead.
365 DCHECK(extra_object_files.empty());
366
367 std::vector<OutputFile> order_only_deps;
368 for (size_t i = 0; i < non_linkable_deps.size(); i++)
369 order_only_deps.push_back(non_linkable_deps[i]->dependency_output_file());
370
371 WriteStampForTarget(object_files, order_only_deps);
372 }
373
GetDeps(UniqueVector<OutputFile> * extra_object_files,UniqueVector<const Target * > * linkable_deps,UniqueVector<const Target * > * non_linkable_deps) const374 void NinjaBinaryTargetWriter::GetDeps(
375 UniqueVector<OutputFile>* extra_object_files,
376 UniqueVector<const Target*>* linkable_deps,
377 UniqueVector<const Target*>* non_linkable_deps) const {
378 const UniqueVector<const Target*>& inherited =
379 target_->inherited_libraries();
380
381 // Normal public/private deps.
382 for (DepsIterator iter(target_, DepsIterator::LINKED_ONLY); !iter.done();
383 iter.Advance()) {
384 ClassifyDependency(iter.target(), extra_object_files,
385 linkable_deps, non_linkable_deps);
386 }
387
388 // Inherited libraries.
389 for (size_t i = 0; i < inherited.size(); i++) {
390 ClassifyDependency(inherited[i], extra_object_files,
391 linkable_deps, non_linkable_deps);
392 }
393
394 // Data deps.
395 const LabelTargetVector& data_deps = target_->data_deps();
396 for (size_t i = 0; i < data_deps.size(); i++)
397 non_linkable_deps->push_back(data_deps[i].ptr);
398 }
399
ClassifyDependency(const Target * dep,UniqueVector<OutputFile> * extra_object_files,UniqueVector<const Target * > * linkable_deps,UniqueVector<const Target * > * non_linkable_deps) const400 void NinjaBinaryTargetWriter::ClassifyDependency(
401 const Target* dep,
402 UniqueVector<OutputFile>* extra_object_files,
403 UniqueVector<const Target*>* linkable_deps,
404 UniqueVector<const Target*>* non_linkable_deps) const {
405 // Only the following types of outputs have libraries linked into them:
406 // EXECUTABLE
407 // SHARED_LIBRARY
408 // _complete_ STATIC_LIBRARY
409 //
410 // Child deps of intermediate static libraries get pushed up the
411 // dependency tree until one of these is reached, and source sets
412 // don't link at all.
413 bool can_link_libs = target_->IsFinal();
414
415 if (dep->output_type() == Target::SOURCE_SET) {
416 // Source sets have their object files linked into final targets
417 // (shared libraries, executables, and complete static
418 // libraries). Intermediate static libraries and other source sets
419 // just forward the dependency, otherwise the files in the source
420 // set can easily get linked more than once which will cause
421 // multiple definition errors.
422 if (can_link_libs) {
423 // Linking in a source set to an executable, shared library, or
424 // complete static library, so copy its object files.
425 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop.
426 for (size_t i = 0; i < dep->sources().size(); i++) {
427 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE;
428 if (GetOutputFilesForSource(dep, dep->sources()[i], &tool_type,
429 &tool_outputs)) {
430 // Only link the first output if there are more than one.
431 extra_object_files->push_back(tool_outputs[0]);
432 }
433 }
434 }
435 } else if (can_link_libs && dep->IsLinkable()) {
436 linkable_deps->push_back(dep);
437 } else {
438 non_linkable_deps->push_back(dep);
439 }
440 }
441
WriteOrderOnlyDependencies(const UniqueVector<const Target * > & non_linkable_deps)442 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies(
443 const UniqueVector<const Target*>& non_linkable_deps) {
444 const std::vector<SourceFile>& data = target_->data();
445 if (!non_linkable_deps.empty() || !data.empty()) {
446 out_ << " ||";
447
448 // Non-linkable targets.
449 for (size_t i = 0; i < non_linkable_deps.size(); i++) {
450 out_ << " ";
451 path_output_.WriteFile(
452 out_, non_linkable_deps[i]->dependency_output_file());
453 }
454 }
455 }
456
GetOutputFilesForSource(const Target * target,const SourceFile & source,Toolchain::ToolType * computed_tool_type,std::vector<OutputFile> * outputs) const457 bool NinjaBinaryTargetWriter::GetOutputFilesForSource(
458 const Target* target,
459 const SourceFile& source,
460 Toolchain::ToolType* computed_tool_type,
461 std::vector<OutputFile>* outputs) const {
462 outputs->clear();
463 *computed_tool_type = Toolchain::TYPE_NONE;
464
465 SourceFileType file_type = GetSourceFileType(source);
466 if (file_type == SOURCE_UNKNOWN)
467 return false;
468 if (file_type == SOURCE_O) {
469 // Object files just get passed to the output and not compiled.
470 outputs->push_back(OutputFile(settings_->build_settings(), source));
471 return true;
472 }
473
474 *computed_tool_type =
475 target->toolchain()->GetToolTypeForSourceType(file_type);
476 if (*computed_tool_type == Toolchain::TYPE_NONE)
477 return false; // No tool for this file (it's a header file or something).
478 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type);
479 if (!tool)
480 return false; // Tool does not apply for this toolchain.file.
481
482 // Figure out what output(s) this compiler produces.
483 SubstitutionWriter::ApplyListToCompilerAsOutputFile(
484 target, source, tool->outputs(), outputs);
485 return !outputs->empty();
486 }
487