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_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 
     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 
     34 NinjaTargetWriter::~NinjaTargetWriter() {
     35 }
     36 
     37 // static
     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 
     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 
    146 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 
    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