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.
5 #include "tools/gn/ninja_action_target_writer.h"
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"
13 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
14 const Toolchain* toolchain,
16 : NinjaTargetWriter(target, toolchain, out),
17 path_output_no_escaping_(
18 target->settings()->build_settings()->build_dir(),
22 NinjaActionTargetWriter::~NinjaActionTargetWriter() {
25 void NinjaActionTargetWriter::Run() {
26 FileTemplate args_template(target_->action_values().args());
27 std::string custom_rule_name = WriteRuleDefinition(args_template);
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
34 std::vector<const Target*> extra_hard_deps;
35 for (size_t i = 0; i < target_->deps().size(); i++)
36 extra_hard_deps.push_back(target_->deps()[i].ptr);
38 // For ACTIONs this is a bit inefficient since it creates an input dep
39 // stamp file even though we're only going to use it once. It would save a
40 // build step to skip this and write the order-only deps directly on the
41 // build rule. This should probably be handled by WriteInputDepsStampAndGetDep
42 // automatically if we supply a count of sources (so it can optimize based on
43 // how many times things would be duplicated).
44 std::string implicit_deps = WriteInputDepsStampAndGetDep(extra_hard_deps);
47 // Collects all output files for writing below.
48 std::vector<OutputFile> output_files;
50 if (target_->output_type() == Target::ACTION_FOREACH) {
51 // Write separate build lines for each input source file.
52 WriteSourceRules(custom_rule_name, implicit_deps, args_template,
55 DCHECK(target_->output_type() == Target::ACTION);
57 // Write a rule that invokes the script once with the outputs as outputs,
58 // and the data as inputs.
60 if (target_->action_values().has_depfile()) {
62 WriteDepfile(SourceFile());
64 const Target::FileList& outputs = target_->action_values().outputs();
65 for (size_t i = 0; i < outputs.size(); i++) {
66 OutputFile output_path(
67 RemovePrefix(outputs[i].value(),
68 settings_->build_settings()->build_dir().value()));
69 output_files.push_back(output_path);
71 path_output_.WriteFile(out_, output_path);
74 out_ << ": " << custom_rule_name << implicit_deps << std::endl;
75 if (target_->action_values().has_depfile()) {
76 out_ << " depfile = ";
77 WriteDepfile(SourceFile());
83 WriteStamp(output_files);
86 std::string NinjaActionTargetWriter::WriteRuleDefinition(
87 const FileTemplate& args_template) {
88 // Make a unique name for this rule.
90 // Use a unique name for the response file when there are multiple build
91 // steps so that they don't stomp on each other. When there are no sources,
92 // there will be only one invocation so we can use a simple name.
93 std::string target_label = target_->label().GetUserVisibleName(true);
94 std::string custom_rule_name(target_label);
95 base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
96 custom_rule_name.append("_rule");
98 if (settings_->IsWin()) {
99 // Send through gyp-win-tool and use a response file.
100 std::string rspfile = custom_rule_name;
102 rspfile += ".$unique_name";
105 out_ << "rule " << custom_rule_name << std::endl;
106 out_ << " command = ";
107 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
108 // TODO(brettw) this hardcodes "environment.x86" which is something that
109 // the Chrome Windows toolchain writes. We should have a way to invoke
110 // python without requiring this gyp_win_tool thing.
111 out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile
113 out_ << " description = ACTION " << target_label << std::endl;
114 out_ << " restat = 1" << std::endl;
115 out_ << " rspfile = " << rspfile << std::endl;
117 // The build command goes in the rsp file.
118 out_ << " rspfile_content = ";
119 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
121 path_output_.WriteFile(out_, target_->action_values().script());
122 args_template.WriteWithNinjaExpansions(out_);
125 // Posix can execute Python directly.
126 out_ << "rule " << custom_rule_name << std::endl;
127 out_ << " command = ";
128 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
130 path_output_.WriteFile(out_, target_->action_values().script());
131 args_template.WriteWithNinjaExpansions(out_);
133 out_ << " description = ACTION " << target_label << std::endl;
134 out_ << " restat = 1" << std::endl;
137 return custom_rule_name;
140 void NinjaActionTargetWriter::WriteArgsSubstitutions(
141 const SourceFile& source,
142 const FileTemplate& args_template) {
143 std::ostringstream source_file_stream;
144 path_output_no_escaping_.WriteFile(source_file_stream, source);
146 EscapeOptions template_escape_options;
147 template_escape_options.mode = ESCAPE_NINJA_SHELL;
148 template_escape_options.inhibit_quoting = true;
150 args_template.WriteNinjaVariablesForSubstitution(
151 out_, source_file_stream.str(), template_escape_options);
154 void NinjaActionTargetWriter::WriteSourceRules(
155 const std::string& custom_rule_name,
156 const std::string& implicit_deps,
157 const FileTemplate& args_template,
158 std::vector<OutputFile>* output_files) {
159 FileTemplate output_template(GetOutputTemplate());
161 const Target::FileList& sources = target_->sources();
162 for (size_t i = 0; i < sources.size(); i++) {
164 WriteOutputFilesForBuildLine(output_template, sources[i], output_files);
166 out_ << ": " << custom_rule_name << " ";
167 path_output_.WriteFile(out_, sources[i]);
168 out_ << implicit_deps << std::endl;
170 // Windows needs a unique ID for the response file.
171 if (target_->settings()->IsWin())
172 out_ << " unique_name = " << i << std::endl;
174 if (args_template.has_substitutions())
175 WriteArgsSubstitutions(sources[i], args_template);
177 if (target_->action_values().has_depfile()) {
178 out_ << " depfile = ";
179 WriteDepfile(sources[i]);
185 void NinjaActionTargetWriter::WriteStamp(
186 const std::vector<OutputFile>& output_files) {
188 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
190 << helper_.GetRulePrefix(target_->settings())
193 // The action stamp depends on all output files from running the action.
194 for (size_t i = 0; i < output_files.size(); i++) {
196 path_output_.WriteFile(out_, output_files[i]);
199 // It also depends on all datadeps. These are needed at runtime and should
200 // be compiled when the action is, but don't need to be done before we run
202 for (size_t i = 0; i < target_->datadeps().size(); i++) {
204 path_output_.WriteFile(out_,
205 helper_.GetTargetOutputFile(target_->datadeps()[i].ptr));
211 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
212 const FileTemplate& output_template,
213 const SourceFile& source,
214 std::vector<OutputFile>* output_files) {
215 // If there is a depfile specified we need to list it as the first output as
216 // that is what ninja will expect the depfile to refer to itself as.
217 if (target_->action_values().has_depfile()) {
219 WriteDepfile(source);
221 std::vector<std::string> output_template_result;
222 output_template.ApplyString(source.value(), &output_template_result);
223 for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
224 OutputFile output_path(output_template_result[out_i]);
225 output_files->push_back(output_path);
227 path_output_.WriteFile(out_, output_path);
231 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
232 std::vector<std::string> result;
233 GetDepfileTemplate().ApplyString(source.value(), &result);
234 path_output_.WriteFile(out_, OutputFile(result[0]));
237 FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const {
238 std::vector<std::string> template_args;
239 std::string depfile_relative_to_build_dir =
240 RemovePrefix(target_->action_values().depfile().value(),
241 settings_->build_settings()->build_dir().value());
242 template_args.push_back(depfile_relative_to_build_dir);
243 return FileTemplate(template_args);