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
15NinjaActionTargetWriter::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
23NinjaActionTargetWriter::~NinjaActionTargetWriter() {
24}
25
26void 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
89std::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
154void 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
200void 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
214void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
215  path_output_.WriteFile(out_,
216      SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
217          settings_, target_->action_values().depfile(), source));
218}
219