• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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