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
);
28 std::string implicit_deps
= GetSourcesImplicitDeps();
30 // Collects all output files for writing below.
31 std::vector
<OutputFile
> output_files
;
33 if (target_
->output_type() == Target::ACTION_FOREACH
) {
34 // Write separate build lines for each input source file.
35 WriteSourceRules(custom_rule_name
, implicit_deps
, args_template
,
38 DCHECK(target_
->output_type() == Target::ACTION
);
40 // Write a rule that invokes the script once with the outputs as outputs,
41 // and the data as inputs.
43 if (target_
->action_values().has_depfile()) {
45 WriteDepfile(SourceFile());
47 const Target::FileList
& outputs
= target_
->action_values().outputs();
48 for (size_t i
= 0; i
< outputs
.size(); i
++) {
49 OutputFile
output_path(
50 RemovePrefix(outputs
[i
].value(),
51 settings_
->build_settings()->build_dir().value()));
52 output_files
.push_back(output_path
);
54 path_output_
.WriteFile(out_
, output_path
);
57 out_
<< ": " << custom_rule_name
<< implicit_deps
;
59 // In the case of running the script once, we allow you to write the input
60 // dependencies in both sources and source_prereqs. source_prereqs are
61 // already in the implicit deps written above, but the sources aren't
62 // (since treating the sources this was is unique to an action).
63 const Target::FileList
& sources
= target_
->sources();
64 for (size_t i
= 0; i
< sources
.size(); i
++) {
66 path_output_
.WriteFile(out_
, sources
[i
]);
70 if (target_
->action_values().has_depfile()) {
71 out_
<< " depfile = ";
72 WriteDepfile(SourceFile());
78 WriteStamp(output_files
);
81 std::string
NinjaActionTargetWriter::WriteRuleDefinition(
82 const FileTemplate
& args_template
) {
83 // Make a unique name for this rule.
85 // Use a unique name for the response file when there are multiple build
86 // steps so that they don't stomp on each other. When there are no sources,
87 // there will be only one invocation so we can use a simple name.
88 std::string target_label
= target_
->label().GetUserVisibleName(true);
89 std::string
custom_rule_name(target_label
);
90 base::ReplaceChars(custom_rule_name
, ":/()", "_", &custom_rule_name
);
91 custom_rule_name
.append("_rule");
93 if (settings_
->IsWin()) {
94 // Send through gyp-win-tool and use a response file.
95 std::string rspfile
= custom_rule_name
;
97 rspfile
+= ".$unique_name";
100 out_
<< "rule " << custom_rule_name
<< std::endl
;
101 out_
<< " command = ";
102 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
103 // TODO(brettw) this hardcodes "environment.x86" which is something that
104 // the Chrome Windows toolchain writes. We should have a way to invoke
105 // python without requiring this gyp_win_tool thing.
106 out_
<< " gyp-win-tool action-wrapper environment.x86 " << rspfile
108 out_
<< " description = ACTION " << target_label
<< std::endl
;
109 out_
<< " restat = 1" << std::endl
;
110 out_
<< " rspfile = " << rspfile
<< std::endl
;
112 // The build command goes in the rsp file.
113 out_
<< " rspfile_content = ";
114 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
116 path_output_
.WriteFile(out_
, target_
->action_values().script());
117 args_template
.WriteWithNinjaExpansions(out_
);
120 // Posix can execute Python directly.
121 out_
<< "rule " << custom_rule_name
<< std::endl
;
122 out_
<< " command = ";
123 path_output_
.WriteFile(out_
, settings_
->build_settings()->python_path());
125 path_output_
.WriteFile(out_
, target_
->action_values().script());
126 args_template
.WriteWithNinjaExpansions(out_
);
128 out_
<< " description = ACTION " << target_label
<< std::endl
;
129 out_
<< " restat = 1" << std::endl
;
133 return custom_rule_name
;
136 void NinjaActionTargetWriter::WriteArgsSubstitutions(
137 const SourceFile
& source
,
138 const FileTemplate
& args_template
) {
139 std::ostringstream source_file_stream
;
140 path_output_no_escaping_
.WriteFile(source_file_stream
, source
);
142 EscapeOptions template_escape_options
;
143 template_escape_options
.mode
= ESCAPE_NINJA_SHELL
;
144 template_escape_options
.inhibit_quoting
= true;
146 args_template
.WriteNinjaVariablesForSubstitution(
147 out_
, source_file_stream
.str(), template_escape_options
);
150 void NinjaActionTargetWriter::WriteSourceRules(
151 const std::string
& custom_rule_name
,
152 const std::string
& implicit_deps
,
153 const FileTemplate
& args_template
,
154 std::vector
<OutputFile
>* output_files
) {
155 FileTemplate
output_template(GetOutputTemplate());
157 const Target::FileList
& sources
= target_
->sources();
158 for (size_t i
= 0; i
< sources
.size(); i
++) {
160 WriteOutputFilesForBuildLine(output_template
, sources
[i
], output_files
);
162 out_
<< ": " << custom_rule_name
<< " ";
163 path_output_
.WriteFile(out_
, sources
[i
]);
164 out_
<< implicit_deps
<< std::endl
;
166 // Windows needs a unique ID for the response file.
167 if (target_
->settings()->IsWin())
168 out_
<< " unique_name = " << i
<< std::endl
;
170 if (args_template
.has_substitutions())
171 WriteArgsSubstitutions(sources
[i
], args_template
);
173 if (target_
->action_values().has_depfile()) {
174 out_
<< " depfile = ";
175 WriteDepfile(sources
[i
]);
181 void NinjaActionTargetWriter::WriteStamp(
182 const std::vector
<OutputFile
>& output_files
) {
184 path_output_
.WriteFile(out_
, helper_
.GetTargetOutputFile(target_
));
186 << helper_
.GetRulePrefix(target_
->settings())
188 for (size_t i
= 0; i
< output_files
.size(); i
++) {
190 path_output_
.WriteFile(out_
, output_files
[i
]);
195 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine(
196 const FileTemplate
& output_template
,
197 const SourceFile
& source
,
198 std::vector
<OutputFile
>* output_files
) {
199 // If there is a depfile specified we need to list it as the first output as
200 // that is what ninja will expect the depfile to refer to itself as.
201 if (target_
->action_values().has_depfile()) {
203 WriteDepfile(source
);
205 std::vector
<std::string
> output_template_result
;
206 output_template
.ApplyString(source
.value(), &output_template_result
);
207 for (size_t out_i
= 0; out_i
< output_template_result
.size(); out_i
++) {
208 OutputFile
output_path(output_template_result
[out_i
]);
209 output_files
->push_back(output_path
);
211 path_output_
.WriteFile(out_
, output_path
);
215 void NinjaActionTargetWriter::WriteDepfile(const SourceFile
& source
) {
216 std::vector
<std::string
> result
;
217 GetDepfileTemplate().ApplyString(source
.value(), &result
);
218 path_output_
.WriteFile(out_
, OutputFile(result
[0]));
221 FileTemplate
NinjaActionTargetWriter::GetDepfileTemplate() const {
222 std::vector
<std::string
> template_args
;
223 std::string depfile_relative_to_build_dir
=
224 RemovePrefix(target_
->action_values().depfile().value(),
225 settings_
->build_settings()->build_dir().value());
226 template_args
.push_back(depfile_relative_to_build_dir
);
227 return FileTemplate(template_args
);