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/err.h"
      9 #include "tools/gn/file_template.h"
     10 #include "tools/gn/string_utils.h"
     11 #include "tools/gn/target.h"
     12 
     13 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
     14                                                  const Toolchain* toolchain,
     15                                                  std::ostream& out)
     16     : NinjaTargetWriter(target, toolchain, out),
     17       path_output_no_escaping_(
     18           target->settings()->build_settings()->build_dir(),
     19           ESCAPE_NONE) {
     20 }
     21 
     22 NinjaActionTargetWriter::~NinjaActionTargetWriter() {
     23 }
     24 
     25 void NinjaActionTargetWriter::Run() {
     26   FileTemplate args_template(target_->settings(),
     27                              target_->action_values().args());
     28   std::string custom_rule_name = WriteRuleDefinition(args_template);
     29 
     30   // Collect our deps to pass as "extra hard dependencies" for input deps. This
     31   // will force all of the action's dependencies to be completed before the
     32   // action is run. Usually, if an action has a dependency, it will be
     33   // operating on the result of that previous step, so we need to be sure to
     34   // serialize these.
     35   std::vector<const Target*> extra_hard_deps;
     36   for (size_t i = 0; i < target_->deps().size(); i++)
     37     extra_hard_deps.push_back(target_->deps()[i].ptr);
     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   std::string implicit_deps = 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, implicit_deps, args_template,
     54                      &output_files);
     55   } else {
     56     DCHECK(target_->output_type() == Target::ACTION);
     57 
     58     // Write a rule that invokes the script once with the outputs as outputs,
     59     // and the data as inputs.
     60     out_ << "build";
     61     const Target::FileList& outputs = target_->action_values().outputs();
     62     for (size_t i = 0; i < outputs.size(); i++) {
     63       OutputFile output_path(
     64           RemovePrefix(outputs[i].value(),
     65                        settings_->build_settings()->build_dir().value()));
     66       output_files.push_back(output_path);
     67       out_ << " ";
     68       path_output_.WriteFile(out_, output_path);
     69     }
     70 
     71     out_ << ": " << custom_rule_name << implicit_deps << 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   WriteStamp(output_files);
     81 }
     82 
     83 std::string NinjaActionTargetWriter::WriteRuleDefinition(
     84     const FileTemplate& args_template) {
     85   // Make a unique name for this rule.
     86   //
     87   // Use a unique name for the response file when there are multiple build
     88   // steps so that they don't stomp on each other. When there are no sources,
     89   // there will be only one invocation so we can use a simple name.
     90   std::string target_label = target_->label().GetUserVisibleName(true);
     91   std::string custom_rule_name(target_label);
     92   base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
     93   custom_rule_name.append("_rule");
     94 
     95   if (settings_->IsWin()) {
     96     // Send through gyp-win-tool and use a response file.
     97     std::string rspfile = custom_rule_name;
     98     if (has_sources())
     99       rspfile += ".$unique_name";
    100     rspfile += ".rsp";
    101 
    102     out_ << "rule " << custom_rule_name << std::endl;
    103     out_ << "  command = ";
    104     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    105     // TODO(brettw) this hardcodes "environment.x86" which is something that
    106     // the Chrome Windows toolchain writes. We should have a way to invoke
    107     // python without requiring this gyp_win_tool thing.
    108     out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile
    109          << std::endl;
    110     out_ << "  description = ACTION " << target_label << std::endl;
    111     out_ << "  restat = 1" << std::endl;
    112     out_ << "  rspfile = " << rspfile << std::endl;
    113 
    114     // The build command goes in the rsp file.
    115     out_ << "  rspfile_content = ";
    116     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    117     out_ << " ";
    118     path_output_.WriteFile(out_, target_->action_values().script());
    119     args_template.WriteWithNinjaExpansions(out_);
    120     out_ << std::endl;
    121   } else {
    122     // Posix can execute Python directly.
    123     out_ << "rule " << custom_rule_name << std::endl;
    124     out_ << "  command = ";
    125     path_output_.WriteFile(out_, settings_->build_settings()->python_path());
    126     out_ << " ";
    127     path_output_.WriteFile(out_, target_->action_values().script());
    128     args_template.WriteWithNinjaExpansions(out_);
    129     out_ << std::endl;
    130     out_ << "  description = ACTION " << target_label << std::endl;
    131     out_ << "  restat = 1" << std::endl;
    132   }
    133 
    134   return custom_rule_name;
    135 }
    136 
    137 void NinjaActionTargetWriter::WriteArgsSubstitutions(
    138     const SourceFile& source,
    139     const FileTemplate& args_template) {
    140   EscapeOptions template_escape_options;
    141   template_escape_options.mode = ESCAPE_NINJA_COMMAND;
    142 
    143   args_template.WriteNinjaVariablesForSubstitution(
    144       out_, target_->settings(), source, template_escape_options);
    145 }
    146 
    147 void NinjaActionTargetWriter::WriteSourceRules(
    148     const std::string& custom_rule_name,
    149     const std::string& implicit_deps,
    150     const FileTemplate& args_template,
    151     std::vector<OutputFile>* output_files) {
    152   FileTemplate output_template(GetOutputTemplate());
    153 
    154   const Target::FileList& sources = target_->sources();
    155   for (size_t i = 0; i < sources.size(); i++) {
    156     out_ << "build";
    157     WriteOutputFilesForBuildLine(output_template, sources[i], output_files);
    158 
    159     out_ << ": " << custom_rule_name << " ";
    160     path_output_.WriteFile(out_, sources[i]);
    161     out_ << implicit_deps << std::endl;
    162 
    163     // Windows needs a unique ID for the response file.
    164     if (target_->settings()->IsWin())
    165       out_ << "  unique_name = " << i << std::endl;
    166 
    167     if (args_template.has_substitutions())
    168       WriteArgsSubstitutions(sources[i], args_template);
    169 
    170     if (target_->action_values().has_depfile()) {
    171       out_ << "  depfile = ";
    172       WriteDepfile(sources[i]);
    173       out_ << std::endl;
    174     }
    175   }
    176 }
    177 
    178 void NinjaActionTargetWriter::WriteStamp(
    179     const std::vector<OutputFile>& output_files) {
    180   out_ << "build ";
    181   path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
    182   out_ << ": "
    183        << helper_.GetRulePrefix(target_->settings())
    184        << "stamp";
    185 
    186   // The action stamp depends on all output files from running the action.
    187   for (size_t i = 0; i < output_files.size(); i++) {
    188     out_ << " ";
    189     path_output_.WriteFile(out_, output_files[i]);
    190   }
    191 
    192   // It also depends on all datadeps. These are needed at runtime and should
    193   // be compiled when the action is, but don't need to be done before we run
    194   // the action.
    195   for (size_t i = 0; i < target_->datadeps().size(); i++) {
    196     out_ << " ";
    197     path_output_.WriteFile(out_,
    198         helper_.GetTargetOutputFile(target_->datadeps()[i].ptr));
    199   }
    200 
    201   out_ << std::endl;
    202 }
    203 
    204 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
    205     const FileTemplate& output_template,
    206     const SourceFile& source,
    207     std::vector<OutputFile>* output_files) {
    208   std::vector<std::string> output_template_result;
    209   output_template.Apply(source, &output_template_result);
    210   for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
    211     OutputFile output_path(output_template_result[out_i]);
    212     output_files->push_back(output_path);
    213     out_ << " ";
    214     path_output_.WriteFile(out_, output_path);
    215   }
    216 }
    217 
    218 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
    219   std::vector<std::string> result;
    220   GetDepfileTemplate().Apply(source, &result);
    221   path_output_.WriteFile(out_, OutputFile(result[0]));
    222 }
    223 
    224 FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const {
    225   std::vector<std::string> template_args;
    226   std::string depfile_relative_to_build_dir =
    227       RemovePrefix(target_->action_values().depfile().value(),
    228                    settings_->build_settings()->build_dir().value());
    229   template_args.push_back(depfile_relative_to_build_dir);
    230   return FileTemplate(settings_, template_args);
    231 }
    232