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 <stddef.h>
8
9 #include "base/strings/string_util.h"
10 #include "tools/gn/deps_iterator.h"
11 #include "tools/gn/err.h"
12 #include "tools/gn/general_tool.h"
13 #include "tools/gn/pool.h"
14 #include "tools/gn/settings.h"
15 #include "tools/gn/string_utils.h"
16 #include "tools/gn/substitution_writer.h"
17 #include "tools/gn/target.h"
18
NinjaActionTargetWriter(const Target * target,std::ostream & out)19 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target,
20 std::ostream& out)
21 : NinjaTargetWriter(target, out),
22 path_output_no_escaping_(
23 target->settings()->build_settings()->build_dir(),
24 target->settings()->build_settings()->root_path_utf8(),
25 ESCAPE_NONE) {}
26
27 NinjaActionTargetWriter::~NinjaActionTargetWriter() = default;
28
Run()29 void NinjaActionTargetWriter::Run() {
30 std::string custom_rule_name = WriteRuleDefinition();
31
32 // Collect our deps to pass as "extra hard dependencies" for input deps. This
33 // will force all of the action's dependencies to be completed before the
34 // action is run. Usually, if an action has a dependency, it will be
35 // operating on the result of that previous step, so we need to be sure to
36 // serialize these.
37 std::vector<const Target*> extra_hard_deps;
38 for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED))
39 extra_hard_deps.push_back(pair.ptr);
40
41 // For ACTIONs, the input deps appear only once in the generated ninja
42 // file, so WriteInputDepsStampAndGetDep() won't create a stamp file
43 // and the action will just depend on all the input deps directly.
44 size_t num_stamp_uses =
45 target_->output_type() == Target::ACTION ? 1u : target_->sources().size();
46 std::vector<OutputFile> input_deps =
47 WriteInputDepsStampAndGetDep(extra_hard_deps, num_stamp_uses);
48 out_ << std::endl;
49
50 // Collects all output files for writing below.
51 std::vector<OutputFile> output_files;
52
53 if (target_->output_type() == Target::ACTION_FOREACH) {
54 // Write separate build lines for each input source file.
55 WriteSourceRules(custom_rule_name, input_deps, &output_files);
56 } else {
57 DCHECK(target_->output_type() == Target::ACTION);
58
59 // Write a rule that invokes the script once with the outputs as outputs,
60 // and the data as inputs. It does not depend on the sources.
61 out_ << "build";
62 SubstitutionWriter::GetListAsOutputFiles(
63 settings_, target_->action_values().outputs(), &output_files);
64 path_output_.WriteFiles(out_, output_files);
65
66 out_ << ": " << custom_rule_name;
67 if (!input_deps.empty()) {
68 // As in WriteSourceRules, we want to force this target to rebuild any
69 // time any of its dependencies change.
70 out_ << " |";
71 path_output_.WriteFiles(out_, input_deps);
72 }
73 out_ << std::endl;
74 if (target_->action_values().has_depfile()) {
75 out_ << " depfile = ";
76 WriteDepfile(SourceFile());
77 out_ << std::endl;
78 }
79 if (target_->action_values().pool().ptr) {
80 out_ << " pool = ";
81 out_ << target_->action_values().pool().ptr->GetNinjaName(
82 settings_->default_toolchain_label());
83 out_ << std::endl;
84 }
85 }
86 out_ << std::endl;
87
88 // Write the stamp, which also depends on all data deps. These are needed at
89 // runtime and should be compiled when the action is, but don't need to be
90 // done before we run the action.
91 // TODO(thakis): If the action has just a single output, make things depend
92 // on that output directly without writing a stamp file.
93 std::vector<OutputFile> data_outs;
94 for (const auto& dep : target_->data_deps())
95 data_outs.push_back(dep.ptr->dependency_output_file());
96 WriteStampForTarget(output_files, data_outs);
97 }
98
WriteRuleDefinition()99 std::string NinjaActionTargetWriter::WriteRuleDefinition() {
100 // Make a unique name for this rule.
101 //
102 // Use a unique name for the response file when there are multiple build
103 // steps so that they don't stomp on each other. When there are no sources,
104 // there will be only one invocation so we can use a simple name.
105 std::string target_label = target_->label().GetUserVisibleName(true);
106 std::string custom_rule_name(target_label);
107 base::ReplaceChars(custom_rule_name, "+:/()", "_", &custom_rule_name);
108 custom_rule_name.append("_rule");
109
110 const SubstitutionList& args = target_->action_values().args();
111 EscapeOptions args_escape_options;
112 args_escape_options.mode = ESCAPE_NINJA_COMMAND;
113
114 out_ << "rule " << custom_rule_name << std::endl;
115
116 if (target_->action_values().uses_rsp_file()) {
117 // Needs a response file. The unique_name part is for action_foreach so
118 // each invocation of the rule gets a different response file. This isn't
119 // strictly necessary for regular one-shot actions, but it's easier to
120 // just always define unique_name.
121 std::string rspfile = custom_rule_name;
122 if (!target_->sources().empty())
123 rspfile += ".$unique_name";
124 rspfile += ".rsp";
125 out_ << " rspfile = " << rspfile << std::endl;
126
127 // Response file contents.
128 out_ << " rspfile_content =";
129 for (const auto& arg :
130 target_->action_values().rsp_file_contents().list()) {
131 out_ << " ";
132 SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options,
133 out_);
134 }
135 out_ << std::endl;
136 }
137
138 out_ << " command = ";
139 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
140 out_ << " ";
141 path_output_.WriteFile(out_, target_->action_values().script());
142 for (const auto& arg : args.list()) {
143 out_ << " ";
144 SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
145 }
146 out_ << std::endl;
147 out_ << " description = ACTION " << target_label << std::endl;
148 out_ << " restat = 1" << std::endl;
149 const Tool* tool = target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
150 if (tool && tool->pool().ptr) {
151 out_ << " pool = ";
152 out_ << tool->pool().ptr->GetNinjaName(
153 settings_->default_toolchain_label());
154 out_ << std::endl;
155 }
156
157 return custom_rule_name;
158 }
159
WriteSourceRules(const std::string & custom_rule_name,const std::vector<OutputFile> & input_deps,std::vector<OutputFile> * output_files)160 void NinjaActionTargetWriter::WriteSourceRules(
161 const std::string& custom_rule_name,
162 const std::vector<OutputFile>& input_deps,
163 std::vector<OutputFile>* output_files) {
164 EscapeOptions args_escape_options;
165 args_escape_options.mode = ESCAPE_NINJA_COMMAND;
166 // We're writing the substitution values, these should not be quoted since
167 // they will get pasted into the real command line.
168 args_escape_options.inhibit_quoting = true;
169
170 const Target::FileList& sources = target_->sources();
171 for (size_t i = 0; i < sources.size(); i++) {
172 out_ << "build";
173 WriteOutputFilesForBuildLine(sources[i], output_files);
174
175 out_ << ": " << custom_rule_name << " ";
176 path_output_.WriteFile(out_, sources[i]);
177 if (!input_deps.empty()) {
178 // Using "|" for the dependencies forces all implicit dependencies to be
179 // fully up to date before running the action, and will re-run this
180 // action if any input dependencies change. This is important because
181 // this action may consume the outputs of previous steps.
182 out_ << " |";
183 path_output_.WriteFiles(out_, input_deps);
184 }
185 out_ << std::endl;
186
187 // Response files require a unique name be defined.
188 if (target_->action_values().uses_rsp_file())
189 out_ << " unique_name = " << i << std::endl;
190
191 // The required types is the union of the args and response file. This
192 // might theoretically duplicate a definition if the same substitution is
193 // used in both the args and the response file. However, this should be
194 // very unusual (normally the substitutions will go in one place or the
195 // other) and the redundant assignment won't bother Ninja.
196 SubstitutionWriter::WriteNinjaVariablesForSource(
197 target_, settings_, sources[i],
198 target_->action_values().args().required_types(), args_escape_options,
199 out_);
200 SubstitutionWriter::WriteNinjaVariablesForSource(
201 target_, settings_, sources[i],
202 target_->action_values().rsp_file_contents().required_types(),
203 args_escape_options, out_);
204
205 if (target_->action_values().has_depfile()) {
206 out_ << " depfile = ";
207 WriteDepfile(sources[i]);
208 out_ << std::endl;
209 }
210 if (target_->action_values().pool().ptr) {
211 out_ << " pool = ";
212 out_ << target_->action_values().pool().ptr->GetNinjaName(
213 settings_->default_toolchain_label());
214 out_ << std::endl;
215 }
216 }
217 }
218
WriteOutputFilesForBuildLine(const SourceFile & source,std::vector<OutputFile> * output_files)219 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
220 const SourceFile& source,
221 std::vector<OutputFile>* output_files) {
222 size_t first_output_index = output_files->size();
223
224 SubstitutionWriter::ApplyListToSourceAsOutputFile(
225 target_, settings_, target_->action_values().outputs(), source,
226 output_files);
227
228 for (size_t i = first_output_index; i < output_files->size(); i++) {
229 out_ << " ";
230 path_output_.WriteFile(out_, (*output_files)[i]);
231 }
232 }
233
WriteDepfile(const SourceFile & source)234 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
235 path_output_.WriteFile(
236 out_,
237 SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
238 target_, settings_, target_->action_values().depfile(), source));
239 }
240