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