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/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"
15 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target
* target
,
17 : NinjaTargetWriter(target
, out
),
18 path_output_no_escaping_(
19 target
->settings()->build_settings()->build_dir(),
23 NinjaActionTargetWriter::~NinjaActionTargetWriter() {
26 void NinjaActionTargetWriter::Run() {
27 std::string custom_rule_name
= WriteRuleDefinition();
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 (const auto& pair
: target_
->GetDeps(Target::DEPS_LINKED
))
36 extra_hard_deps
.push_back(pair
.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 OutputFile input_dep
= 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
, input_dep
, &output_files
);
54 DCHECK(target_
->output_type() == Target::ACTION
);
56 // Write a rule that invokes the script once with the outputs as outputs,
57 // and the data as inputs. It does not depend on the sources.
59 SubstitutionWriter::GetListAsOutputFiles(
60 settings_
, target_
->action_values().outputs(), &output_files
);
61 path_output_
.WriteFiles(out_
, output_files
);
63 out_
<< ": " << custom_rule_name
;
64 if (!input_dep
.value().empty()) {
65 // As in WriteSourceRules, we want to force this target to rebuild any
66 // time any of its dependencies change.
68 path_output_
.WriteFile(out_
, input_dep
);
71 if (target_
->action_values().has_depfile()) {
72 out_
<< " depfile = ";
73 WriteDepfile(SourceFile());
79 // Write the stamp, which also depends on all data deps. These are needed at
80 // runtime and should be compiled when the action is, but don't need to be
81 // done before we run the action.
82 std::vector
<OutputFile
> data_outs
;
83 for (const auto& dep
: target_
->data_deps())
84 data_outs
.push_back(dep
.ptr
->dependency_output_file());
85 WriteStampForTarget(output_files
, data_outs
);
88 std::string
NinjaActionTargetWriter::WriteRuleDefinition() {
89 // Make a unique name for this rule.
91 // Use a unique name for the response file when there are multiple build
92 // steps so that they don't stomp on each other. When there are no sources,
93 // there will be only one invocation so we can use a simple name.
94 std::string target_label
= target_
->label().GetUserVisibleName(true);
95 std::string
custom_rule_name(target_label
);
96 base::ReplaceChars(custom_rule_name
, ":/()", "_", &custom_rule_name
);
97 custom_rule_name
.append("_rule");
99 const SubstitutionList
& args
= target_
->action_values().args();
100 EscapeOptions args_escape_options
;
101 args_escape_options
.mode
= ESCAPE_NINJA_COMMAND
;
103 if (settings_
->IsWin()) {
104 // Send through gyp-win-tool and use a response file.
105 std::string rspfile
= custom_rule_name
;
106 if (!target_
->sources().empty())
107 rspfile
+= ".$unique_name";
110 out_
<< "rule " << custom_rule_name
<< std::endl
;
111 out_
<< " command = ";
112 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
113 // TODO(brettw) this hardcodes "environment.x86" which is something that
114 // the Chrome Windows toolchain writes. We should have a way to invoke
115 // python without requiring this gyp_win_tool thing.
116 out_
<< " gyp-win-tool action-wrapper environment.x86 " << rspfile
118 out_
<< " description = ACTION " << target_label
<< std::endl
;
119 out_
<< " restat = 1" << std::endl
;
120 out_
<< " rspfile = " << rspfile
<< std::endl
;
122 // The build command goes in the rsp file.
123 out_
<< " rspfile_content = ";
124 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
126 path_output_
.WriteFile(out_
, target_
->action_values().script());
127 for (const auto& arg
: args
.list()) {
129 SubstitutionWriter::WriteWithNinjaVariables(
130 arg
, args_escape_options
, out_
);
134 // Posix can execute Python directly.
135 out_
<< "rule " << custom_rule_name
<< std::endl
;
136 out_
<< " command = ";
137 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
139 path_output_
.WriteFile(out_
, target_
->action_values().script());
140 for (const auto& arg
: args
.list()) {
142 SubstitutionWriter::WriteWithNinjaVariables(
143 arg
, args_escape_options
, out_
);
146 out_
<< " description = ACTION " << target_label
<< std::endl
;
147 out_
<< " restat = 1" << std::endl
;
150 return custom_rule_name
;
153 void NinjaActionTargetWriter::WriteSourceRules(
154 const std::string
& custom_rule_name
,
155 const OutputFile
& input_dep
,
156 std::vector
<OutputFile
>* output_files
) {
157 EscapeOptions args_escape_options
;
158 args_escape_options
.mode
= ESCAPE_NINJA_COMMAND
;
159 // We're writing the substitution values, these should not be quoted since
160 // they will get pasted into the real command line.
161 args_escape_options
.inhibit_quoting
= true;
163 const std::vector
<SubstitutionType
>& args_substitutions_used
=
164 target_
->action_values().args().required_types();
166 const Target::FileList
& sources
= target_
->sources();
167 for (size_t i
= 0; i
< sources
.size(); i
++) {
169 WriteOutputFilesForBuildLine(sources
[i
], output_files
);
171 out_
<< ": " << custom_rule_name
<< " ";
172 path_output_
.WriteFile(out_
, sources
[i
]);
173 if (!input_dep
.value().empty()) {
174 // Using "|" for the dependencies forces all implicit dependencies to be
175 // fully up-to-date before running the action, and will re-run this
176 // action if any input dependencies change. This is important because
177 // this action may consume the outputs of previous steps.
179 path_output_
.WriteFile(out_
, input_dep
);
183 // Windows needs a unique ID for the response file.
184 if (target_
->settings()->IsWin())
185 out_
<< " unique_name = " << i
<< std::endl
;
187 SubstitutionWriter::WriteNinjaVariablesForSource(
188 settings_
, sources
[i
], args_substitutions_used
,
189 args_escape_options
, out_
);
191 if (target_
->action_values().has_depfile()) {
192 out_
<< " depfile = ";
193 WriteDepfile(sources
[i
]);
199 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
200 const SourceFile
& source
,
201 std::vector
<OutputFile
>* output_files
) {
202 size_t first_output_index
= output_files
->size();
204 SubstitutionWriter::ApplyListToSourceAsOutputFile(
205 settings_
, target_
->action_values().outputs(), source
, output_files
);
207 for (size_t i
= first_output_index
; i
< output_files
->size(); i
++) {
209 path_output_
.WriteFile(out_
, (*output_files
)[i
]);
213 void NinjaActionTargetWriter::WriteDepfile(const SourceFile
& source
) {
214 path_output_
.WriteFile(out_
,
215 SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
216 settings_
, target_
->action_values().depfile(), source
));