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_action_target_writer.h"
      6 
      7 #include "base/strings/string_util.h"
      8 #include "tools/gn/deps_iterator.h"
      9 #include "tools/gn/err.h"
     10 #include "tools/gn/settings.h"
     11 #include "tools/gn/string_utils.h"
     12 #include "tools/gn/substitution_writer.h"
     13 #include "tools/gn/target.h"
     14 
     15 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
     16                                                  std::ostream& out)
     17     : NinjaTargetWriter(target, out),
     18       path_output_no_escaping_(
     19           target->settings()->build_settings()->build_dir(),
     20           ESCAPE_NONE) {
     21 }
     22 
     23 NinjaActionTargetWriter::~NinjaActionTargetWriter() {
     24 }
     25 
     26 void NinjaActionTargetWriter::Run() {
     27   std::string custom_rule_name = WriteRuleDefinition();
     28 
     29   // Collect our deps to pass as "extra hard dependencies" for input deps. This
     30   // will force all of the action's dependencies to be completed before the
     31   // action is run. Usually, if an action has a dependency, it will be
     32   // operating on the result of that previous step, so we need to be sure to
     33   // serialize these.
     34   std::vector<const Target*> extra_hard_deps;
     35   for (DepsIterator iter(target_, DepsIterator::LINKED_ONLY);
     36        !iter.done(); iter.Advance())
     37     extra_hard_deps.push_back(iter.target());
     38 
     39   // For ACTIONs this is a bit inefficient since it creates an input dep
     40   // stamp file even though we're only going to use it once. It would save a
     41   // build step to skip this and write the order-only deps directly on the
     42   // build rule. This should probably be handled by WriteInputDepsStampAndGetDep
     43   // automatically if we supply a count of sources (so it can optimize based on
     44   // how many times things would be duplicated).
     45   OutputFile input_dep = WriteInputDepsStampAndGetDep(extra_hard_deps);
     46   out_ << std::endl;
     47 
     48   // Collects all output files for writing below.
     49   std::vector<OutputFile> output_files;
     50 
     51   if (target_->output_type() == Target::ACTION_FOREACH) {
     52     // Write separate build lines for each input source file.
     53     WriteSourceRules(custom_rule_name, input_dep, &output_files);
     54   } else {
     55     DCHECK(target_->output_type() == Target::ACTION);
     56 
     57     // Write a rule that invokes the script once with the outputs as outputs,
     58     // and the data as inputs. It does not depend on the sources.
     59     out_ << "build";
     60     SubstitutionWriter::GetListAsOutputFiles(
     61         settings_, target_->action_values().outputs(), &output_files);
     62     path_output_.WriteFiles(out_, output_files);
     63 
     64     out_ << ": " << custom_rule_name;
     65     if (!input_dep.value().empty()) {
     66       // As in WriteSourceRules, we want to force this target to rebuild any
     67       // time any of its dependencies change.
     68       out_ << " | ";
     69       path_output_.WriteFile(out_, input_dep);
     70     }
     71     out_ << std::endl;
     72     if (target_->action_values().has_depfile()) {
     73       out_ << "  depfile = ";
     74       WriteDepfile(SourceFile());
     75       out_ << std::endl;
     76     }
     77   }
     78   out_ << std::endl;
     79 
     80   // Write the stamp, which also depends on all data deps. These are needed at
     81   // runtime and should be compiled when the action is, but don't need to be
     82   // done before we run the action.
     83   std::vector<OutputFile> data_outs;
     84   for (size_t i = 0; i < target_->data_deps().size(); i++)
     85     data_outs.push_back(target_->data_deps()[i].ptr->dependency_output_file());
     86   WriteStampForTarget(output_files, data_outs);
     87 }
     88 
     89 std::string NinjaActionTargetWriter::WriteRuleDefinition() {
     90   // Make a unique name for this rule.
     91   //
     92   // Use a unique name for the response file when there are multiple build
     93   // steps so that they don't stomp on each other. When there are no sources,
     94   // there will be only one invocation so we can use a simple name.
     95   std::string target_label = target_->label().GetUserVisibleName(true);
     96   std::string custom_rule_name(target_label);
     97   base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
     98   custom_rule_name.append("_rule");
     99 
    100   const SubstitutionList& args = target_->action_values().args();
    101   EscapeOptions args_escape_options;
    102   args_escape_options.mode = ESCAPE_NINJA_COMMAND;
    103 
    104   if (settings_->IsWin()) {
    105     // Send through gyp-win-tool and use a response file.
    106     std::string rspfile = custom_rule_name;
    107     if (!target_->sources().empty())
    108       rspfile += ".$unique_name";
    109     rspfile += ".rsp";
    110 
    111     out_ << "rule " << custom_rule_name << std::endl;
    112     out_ << "  command = ";
    113     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    114     // TODO(brettw) this hardcodes "environment.x86" which is something that
    115     // the Chrome Windows toolchain writes. We should have a way to invoke
    116     // python without requiring this gyp_win_tool thing.
    117     out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile
    118          << std::endl;
    119     out_ << "  description = ACTION " << target_label << std::endl;
    120     out_ << "  restat = 1" << std::endl;
    121     out_ << "  rspfile = " << rspfile << std::endl;
    122 
    123     // The build command goes in the rsp file.
    124     out_ << "  rspfile_content = ";
    125     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    126     out_ << " ";
    127     path_output_.WriteFile(out_, target_->action_values().script());
    128     for (size_t i = 0; i < args.list().size(); i++) {
    129       out_ << " ";
    130       SubstitutionWriter::WriteWithNinjaVariables(
    131           args.list()[i], args_escape_options, out_);
    132     }
    133     out_ << std::endl;
    134   } else {
    135     // Posix can execute Python directly.
    136     out_ << "rule " << custom_rule_name << std::endl;
    137     out_ << "  command = ";
    138     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    139     out_ << " ";
    140     path_output_.WriteFile(out_, target_->action_values().script());
    141     for (size_t i = 0; i < args.list().size(); i++) {
    142       out_ << " ";
    143       SubstitutionWriter::WriteWithNinjaVariables(
    144           args.list()[i], args_escape_options, out_);
    145     }
    146     out_ << std::endl;
    147     out_ << "  description = ACTION " << target_label << std::endl;
    148     out_ << "  restat = 1" << std::endl;
    149   }
    150 
    151   return custom_rule_name;
    152 }
    153 
    154 void NinjaActionTargetWriter::WriteSourceRules(
    155     const std::string& custom_rule_name,
    156     const OutputFile& input_dep,
    157     std::vector<OutputFile>* output_files) {
    158   EscapeOptions args_escape_options;
    159   args_escape_options.mode = ESCAPE_NINJA_COMMAND;
    160   // We're writing the substitution values, these should not be quoted since
    161   // they will get pasted into the real command line.
    162   args_escape_options.inhibit_quoting = true;
    163 
    164   const std::vector<SubstitutionType>& args_substitutions_used =
    165       target_->action_values().args().required_types();
    166 
    167   const Target::FileList& sources = target_->sources();
    168   for (size_t i = 0; i < sources.size(); i++) {
    169     out_ << "build";
    170     WriteOutputFilesForBuildLine(sources[i], output_files);
    171 
    172     out_ << ": " << custom_rule_name << " ";
    173     path_output_.WriteFile(out_, sources[i]);
    174     if (!input_dep.value().empty()) {
    175       // Using "|" for the dependencies forces all implicit dependencies to be
    176       // fully up-to-date before running the action, and will re-run this
    177       // action if any input dependencies change. This is important because
    178       // this action may consume the outputs of previous steps.
    179       out_ << " | ";
    180       path_output_.WriteFile(out_, input_dep);
    181     }
    182     out_ << std::endl;
    183 
    184     // Windows needs a unique ID for the response file.
    185     if (target_->settings()->IsWin())
    186       out_ << "  unique_name = " << i << std::endl;
    187 
    188     SubstitutionWriter::WriteNinjaVariablesForSource(
    189         settings_, sources[i], args_substitutions_used,
    190         args_escape_options, out_);
    191 
    192     if (target_->action_values().has_depfile()) {
    193       out_ << "  depfile = ";
    194       WriteDepfile(sources[i]);
    195       out_ << std::endl;
    196     }
    197   }
    198 }
    199 
    200 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
    201     const SourceFile& source,
    202     std::vector<OutputFile>* output_files) {
    203   size_t first_output_index = output_files->size();
    204 
    205   SubstitutionWriter::ApplyListToSourceAsOutputFile(
    206       settings_, target_->action_values().outputs(), source, output_files);
    207 
    208   for (size_t i = first_output_index; i < output_files->size(); i++) {
    209     out_ << " ";
    210     path_output_.WriteFile(out_, (*output_files)[i]);
    211   }
    212 }
    213 
    214 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
    215   path_output_.WriteFile(out_,
    216       SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
    217           settings_, target_->action_values().depfile(), source));
    218 }
    219