Home | History | Annotate | Download | only in gn
      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.
     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 {
     37   DefineWriter() {
     38     options.mode = ESCAPE_NINJA_COMMAND;
     39   }
     40 
     41   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 {
     50   IncludeWriter(PathOutput& path_output) : path_output_(path_output) {
     51   }
     52   ~IncludeWriter() {
     53   }
     54 
     55   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 
     70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target,
     71                                                  std::ostream& out)
     72     : NinjaTargetWriter(target, out),
     73       tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) {
     74 }
     75 
     76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() {
     77 }
     78 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    374 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 
    400 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 
    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 
    457 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