• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gn/ninja_action_target_writer.h"
6 
7 #include <stddef.h>
8 
9 #include "base/strings/string_util.h"
10 #include "gn/deps_iterator.h"
11 #include "gn/err.h"
12 #include "gn/general_tool.h"
13 #include "gn/pool.h"
14 #include "gn/settings.h"
15 #include "gn/string_utils.h"
16 #include "gn/substitution_writer.h"
17 #include "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 additional "hard dependencies" for input deps.
33   // This will force all of the action's dependencies to be completed before
34   // the 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*> additional_hard_deps;
38   std::vector<OutputFile> order_only_deps;
39   const auto& target_deps = resolved().GetTargetDeps(target_);
40 
41   for (const Target* dep : target_deps.linked_deps()) {
42     if (dep->IsDataOnly()) {
43       order_only_deps.push_back(dep->dependency_output_file());
44     } else {
45       additional_hard_deps.push_back(dep);
46     }
47   }
48 
49   // Add all data-deps to the order-only-deps for the action.  The data_deps
50   // field is used to implement different use-cases, including:
51   //
52   //  - Files needed at only runtime by the outputs of the action, and therefore
53   //    need be built if ninja is building the action's outputs.  But they do
54   //    not "dirty" the action's outputs if the data_deps alone are "dirty".
55   //    If ninja had the concept of "weak" dependencies, that would be used
56   //    instead, but that isn't available, so order-only dependencies are used.
57   //
58   //  - Files that _may_ need to be used to perform the action, and a depfile
59   //    will be used to promote these order-only deps to implicit dependencies,
60   //    and on an incremental build, if the now-implicit dependencies are
61   //    'dirty', this action will be considered 'dirty' as well.
62   //
63   for (const Target* data_dep : target_deps.data_deps())
64     order_only_deps.push_back(data_dep->dependency_output_file());
65 
66   // For ACTIONs, the input deps appear only once in the generated ninja
67   // file, so WriteInputDepsStampAndGetDep() won't create a stamp file
68   // and the action will just depend on all the input deps directly.
69   size_t num_stamp_uses =
70       target_->output_type() == Target::ACTION ? 1u : target_->sources().size();
71   std::vector<OutputFile> input_deps =
72       WriteInputDepsStampAndGetDep(additional_hard_deps, num_stamp_uses);
73   out_ << std::endl;
74 
75   // Collects all output files for writing below.
76   std::vector<OutputFile> output_files;
77 
78   if (target_->output_type() == Target::ACTION_FOREACH) {
79     // Write separate build lines for each input source file.
80     WriteSourceRules(custom_rule_name, input_deps, order_only_deps,
81                      &output_files);
82   } else {
83     DCHECK(target_->output_type() == Target::ACTION);
84 
85     // Write a rule that invokes the script once with the outputs as outputs,
86     // and the data as inputs. It does not depend on the sources.
87     out_ << "build";
88     SubstitutionWriter::GetListAsOutputFiles(
89         settings_, target_->action_values().outputs(), &output_files);
90     WriteOutputs(output_files);
91 
92     out_ << ": " << custom_rule_name;
93     if (!input_deps.empty()) {
94       // As in WriteSourceRules, we want to force this target to rebuild any
95       // time any of its dependencies change.
96       out_ << " |";
97       path_output_.WriteFiles(out_, input_deps);
98     }
99     if (!order_only_deps.empty()) {
100       // Write any order-only deps out for actions just like they are for
101       // binaries.
102       out_ << " ||";
103       path_output_.WriteFiles(out_, order_only_deps);
104     }
105 
106     out_ << std::endl;
107     if (target_->action_values().has_depfile()) {
108       WriteDepfile(SourceFile());
109     }
110 
111     WriteNinjaVariablesForAction();
112 
113     if (target_->pool().ptr) {
114       out_ << "  pool = ";
115       out_ << target_->pool().ptr->GetNinjaName(
116           settings_->default_toolchain_label());
117       out_ << std::endl;
118     }
119   }
120   out_ << std::endl;
121 
122   // Write the stamp, which doesn't need to depend on the data deps because they
123   // have been added as order-only deps of the action output itself.
124   //
125   // TODO(thakis): If the action has just a single output, make things depend
126   // on that output directly without writing a stamp file.
127   std::vector<OutputFile> stamp_file_order_only_deps;
128   WriteStampForTarget(output_files, stamp_file_order_only_deps);
129 }
130 
WriteRuleDefinition()131 std::string NinjaActionTargetWriter::WriteRuleDefinition() {
132   // Make a unique name for this rule.
133   //
134   // Use a unique name for the response file when there are multiple build
135   // steps so that they don't stomp on each other. When there are no sources,
136   // there will be only one invocation so we can use a simple name.
137   std::string target_label = target_->label().GetUserVisibleName(true);
138   std::string custom_rule_name(target_label);
139   base::ReplaceChars(custom_rule_name, ":/()+", "_", &custom_rule_name);
140   custom_rule_name.append("_rule");
141 
142   const SubstitutionList& args = target_->action_values().args();
143   EscapeOptions args_escape_options;
144   args_escape_options.mode = ESCAPE_NINJA_COMMAND;
145 
146   out_ << "rule " << custom_rule_name << std::endl;
147 
148   if (target_->action_values().uses_rsp_file()) {
149     // Needs a response file. The unique_name part is for action_foreach so
150     // each invocation of the rule gets a different response file. This isn't
151     // strictly necessary for regular one-shot actions, but it's easier to
152     // just always define unique_name.
153     std::string rspfile = custom_rule_name;
154     if (!target_->sources().empty())
155       rspfile += ".$unique_name";
156     rspfile += ".rsp";
157     out_ << "  rspfile = " << rspfile << std::endl;
158 
159     // Response file contents.
160     out_ << "  rspfile_content =";
161     for (const auto& arg :
162          target_->action_values().rsp_file_contents().list()) {
163       out_ << " ";
164       SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options,
165                                                   out_);
166     }
167     out_ << std::endl;
168   }
169 
170   // The command line requires shell escaping to properly handle filenames
171   // with spaces.
172   PathOutput command_output(path_output_.current_dir(),
173                             settings_->build_settings()->root_path_utf8(),
174                             ESCAPE_NINJA_COMMAND);
175 
176   out_ << "  command = ";
177   command_output.WriteFile(out_, settings_->build_settings()->python_path());
178   out_ << " ";
179   command_output.WriteFile(out_, target_->action_values().script());
180   for (const auto& arg : args.list()) {
181     out_ << " ";
182     SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
183   }
184   out_ << std::endl;
185   auto mnemonic = target_->action_values().mnemonic();
186   if (mnemonic.empty())
187     mnemonic = "ACTION";
188   out_ << "  description = " << mnemonic << " " << target_label << std::endl;
189   out_ << "  restat = 1" << std::endl;
190   const Tool* tool =
191       target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
192   if (tool && tool->pool().ptr) {
193     out_ << "  pool = ";
194     out_ << tool->pool().ptr->GetNinjaName(
195         settings_->default_toolchain_label());
196     out_ << std::endl;
197   }
198 
199   return custom_rule_name;
200 }
201 
WriteSourceRules(const std::string & custom_rule_name,const std::vector<OutputFile> & input_deps,const std::vector<OutputFile> & order_only_deps,std::vector<OutputFile> * output_files)202 void NinjaActionTargetWriter::WriteSourceRules(
203     const std::string& custom_rule_name,
204     const std::vector<OutputFile>& input_deps,
205     const std::vector<OutputFile>& order_only_deps,
206     std::vector<OutputFile>* output_files) {
207   EscapeOptions args_escape_options;
208   args_escape_options.mode = ESCAPE_NINJA_COMMAND;
209   // We're writing the substitution values, these should not be quoted since
210   // they will get pasted into the real command line.
211   args_escape_options.inhibit_quoting = true;
212 
213   const Target::FileList& sources = target_->sources();
214   for (size_t i = 0; i < sources.size(); i++) {
215     out_ << "build";
216     WriteOutputFilesForBuildLine(sources[i], output_files);
217 
218     out_ << ": " << custom_rule_name << " ";
219     path_output_.WriteFile(out_, sources[i]);
220     if (!input_deps.empty()) {
221       // Using "|" for the dependencies forces all implicit dependencies to be
222       // fully up to date before running the action, and will re-run this
223       // action if any input dependencies change. This is important because
224       // this action may consume the outputs of previous steps.
225       out_ << " |";
226       path_output_.WriteFiles(out_, input_deps);
227     }
228     if (!order_only_deps.empty()) {
229       // Write any order-only deps out for actions just like they are written
230       // out for binaries.
231       out_ << " ||";
232       path_output_.WriteFiles(out_, order_only_deps);
233     }
234     out_ << std::endl;
235 
236     // Response files require a unique name be defined.
237     if (target_->action_values().uses_rsp_file())
238       out_ << "  unique_name = " << i << std::endl;
239 
240     // The required types is the union of the args and response file. This
241     // might theoretically duplicate a definition if the same substitution is
242     // used in both the args and the response file. However, this should be
243     // very unusual (normally the substitutions will go in one place or the
244     // other) and the redundant assignment won't bother Ninja.
245     SubstitutionWriter::WriteNinjaVariablesForSource(
246         target_, settings_, sources[i],
247         target_->action_values().args().required_types(), args_escape_options,
248         out_);
249     SubstitutionWriter::WriteNinjaVariablesForSource(
250         target_, settings_, sources[i],
251         target_->action_values().rsp_file_contents().required_types(),
252         args_escape_options, out_);
253     WriteNinjaVariablesForAction();
254 
255     if (target_->action_values().has_depfile()) {
256       WriteDepfile(sources[i]);
257     }
258     if (target_->pool().ptr) {
259       out_ << "  pool = ";
260       out_ << target_->pool().ptr->GetNinjaName(
261           settings_->default_toolchain_label());
262       out_ << std::endl;
263     }
264   }
265 }
266 
WriteOutputFilesForBuildLine(const SourceFile & source,std::vector<OutputFile> * output_files)267 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
268     const SourceFile& source,
269     std::vector<OutputFile>* output_files) {
270   size_t first_output_index = output_files->size();
271 
272   SubstitutionWriter::ApplyListToSourceAsOutputFile(
273       target_, settings_, target_->action_values().outputs(), source,
274       output_files);
275 
276   for (size_t i = first_output_index; i < output_files->size(); i++) {
277     out_ << " ";
278     WriteOutput((*output_files)[i]);
279   }
280 }
281 
WriteDepfile(const SourceFile & source)282 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) {
283   out_ << "  depfile = ";
284   path_output_.WriteFile(
285       out_,
286       SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
287           target_, settings_, target_->action_values().depfile(), source));
288   out_ << std::endl;
289   // Using "deps = gcc" allows Ninja to read and store the depfile content in
290   // its internal database which improves performance, especially for large
291   // depfiles. The use of this feature with depfiles that contain multiple
292   // outputs require Ninja version 1.9.0 or newer.
293   if (settings_->build_settings()->ninja_required_version() >=
294       Version{1, 9, 0}) {
295     out_ << "  deps = gcc" << std::endl;
296   }
297 }
298 
WriteNinjaVariablesForAction()299 void NinjaActionTargetWriter::WriteNinjaVariablesForAction() {
300   SubstitutionBits subst;
301   target_->action_values().args().FillRequiredTypes(&subst);
302   WriteRustCompilerVars(subst, /*indent=*/true, /*always_write=*/false);
303   WriteCCompilerVars(subst, /*indent=*/true, /*respect_source_types=*/false);
304 }
305