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_script_target_writer.h"
6
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"
12
NinjaScriptTargetWriter(const Target * target,const Toolchain * toolchain,std::ostream & out)13 NinjaScriptTargetWriter::NinjaScriptTargetWriter(const Target* target,
14 const Toolchain* toolchain,
15 std::ostream& out)
16 : NinjaTargetWriter(target, toolchain, out),
17 path_output_no_escaping_(
18 target->settings()->build_settings()->build_dir(),
19 ESCAPE_NONE, false) {
20 }
21
~NinjaScriptTargetWriter()22 NinjaScriptTargetWriter::~NinjaScriptTargetWriter() {
23 }
24
Run()25 void NinjaScriptTargetWriter::Run() {
26 FileTemplate args_template(target_->script_values().args());
27 std::string custom_rule_name = WriteRuleDefinition(args_template);
28 std::string implicit_deps = GetSourcesImplicitDeps();
29
30 // Collects all output files for writing below.
31 std::vector<OutputFile> output_files;
32
33 if (has_sources()) {
34 // Write separate build lines for each input source file.
35 WriteSourceRules(custom_rule_name, implicit_deps, args_template,
36 &output_files);
37 } else {
38 // No sources, write a rule that invokes the script once with the
39 // outputs as outputs, and the data as inputs.
40 out_ << "build";
41 if (target_->script_values().has_depfile()) {
42 out_ << " ";
43 WriteDepfile(SourceFile());
44 }
45 const Target::FileList& outputs = target_->script_values().outputs();
46 for (size_t i = 0; i < outputs.size(); i++) {
47 OutputFile output_path(
48 RemovePrefix(outputs[i].value(),
49 settings_->build_settings()->build_dir().value()));
50 output_files.push_back(output_path);
51 out_ << " ";
52 path_output_.WriteFile(out_, output_path);
53 }
54 out_ << ": " << custom_rule_name << implicit_deps << std::endl;
55 if (target_->script_values().has_depfile()) {
56 out_ << " depfile = ";
57 WriteDepfile(SourceFile());
58 out_ << std::endl;
59 }
60 }
61 out_ << std::endl;
62
63 WriteStamp(output_files);
64 }
65
WriteRuleDefinition(const FileTemplate & args_template)66 std::string NinjaScriptTargetWriter::WriteRuleDefinition(
67 const FileTemplate& args_template) {
68 // Make a unique name for this rule.
69 //
70 // Use a unique name for the response file when there are multiple build
71 // steps so that they don't stomp on each other. When there are no sources,
72 // there will be only one invocation so we can use a simple name.
73 std::string target_label = target_->label().GetUserVisibleName(true);
74 std::string custom_rule_name(target_label);
75 base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name);
76 custom_rule_name.append("_rule");
77
78 if (settings_->IsWin()) {
79 // Send through gyp-win-tool and use a response file.
80 std::string rspfile = custom_rule_name;
81 if (has_sources())
82 rspfile += ".$unique_name";
83 rspfile += ".rsp";
84
85 out_ << "rule " << custom_rule_name << std::endl;
86 out_ << " command = ";
87 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
88 // TODO(brettw) this hardcodes "environment.x86" which is something that
89 // the Chrome Windows toolchain writes. We should have a way to invoke
90 // python without requiring this gyp_win_tool thing.
91 out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile
92 << std::endl;
93 out_ << " description = CUSTOM " << target_label << std::endl;
94 out_ << " restat = 1" << std::endl;
95 out_ << " rspfile = " << rspfile << std::endl;
96
97 // The build command goes in the rsp file.
98 out_ << " rspfile_content = ";
99 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
100 out_ << " ";
101 path_output_.WriteFile(out_, target_->script_values().script());
102 args_template.WriteWithNinjaExpansions(out_);
103 out_ << std::endl;
104 } else {
105 // Posix can execute Python directly.
106 out_ << "rule " << custom_rule_name << std::endl;
107 out_ << " command = ";
108 path_output_.WriteFile(out_, settings_->build_settings()->python_path());
109 out_ << " ";
110 path_output_.WriteFile(out_, target_->script_values().script());
111 args_template.WriteWithNinjaExpansions(out_);
112 out_ << std::endl;
113 out_ << " description = CUSTOM " << target_label << std::endl;
114 out_ << " restat = 1" << std::endl;
115 }
116
117 out_ << std::endl;
118 return custom_rule_name;
119 }
120
WriteArgsSubstitutions(const SourceFile & source,const FileTemplate & args_template)121 void NinjaScriptTargetWriter::WriteArgsSubstitutions(
122 const SourceFile& source,
123 const FileTemplate& args_template) {
124 std::ostringstream source_file_stream;
125 path_output_no_escaping_.WriteFile(source_file_stream, source);
126
127 EscapeOptions template_escape_options;
128 template_escape_options.mode = ESCAPE_NINJA_SHELL;
129 template_escape_options.inhibit_quoting = true;
130
131 args_template.WriteNinjaVariablesForSubstitution(
132 out_, source_file_stream.str(), template_escape_options);
133 }
134
WriteSourceRules(const std::string & custom_rule_name,const std::string & implicit_deps,const FileTemplate & args_template,std::vector<OutputFile> * output_files)135 void NinjaScriptTargetWriter::WriteSourceRules(
136 const std::string& custom_rule_name,
137 const std::string& implicit_deps,
138 const FileTemplate& args_template,
139 std::vector<OutputFile>* output_files) {
140 FileTemplate output_template(GetOutputTemplate());
141
142 const Target::FileList& sources = target_->sources();
143 for (size_t i = 0; i < sources.size(); i++) {
144 out_ << "build";
145 WriteOutputFilesForBuildLine(output_template, sources[i], output_files);
146
147 out_ << ": " << custom_rule_name << " ";
148 path_output_.WriteFile(out_, sources[i]);
149 out_ << implicit_deps << std::endl;
150
151 // Windows needs a unique ID for the response file.
152 if (target_->settings()->IsWin())
153 out_ << " unique_name = " << i << std::endl;
154
155 if (args_template.has_substitutions())
156 WriteArgsSubstitutions(sources[i], args_template);
157
158 if (target_->script_values().has_depfile()) {
159 out_ << " depfile = ";
160 WriteDepfile(sources[i]);
161 out_ << std::endl;
162 }
163 }
164 }
165
WriteStamp(const std::vector<OutputFile> & output_files)166 void NinjaScriptTargetWriter::WriteStamp(
167 const std::vector<OutputFile>& output_files) {
168 out_ << "build ";
169 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
170 out_ << ": "
171 << helper_.GetRulePrefix(target_->settings())
172 << "stamp";
173 for (size_t i = 0; i < output_files.size(); i++) {
174 out_ << " ";
175 path_output_.WriteFile(out_, output_files[i]);
176 }
177 out_ << std::endl;
178 }
179
WriteOutputFilesForBuildLine(const FileTemplate & output_template,const SourceFile & source,std::vector<OutputFile> * output_files)180 void NinjaScriptTargetWriter::WriteOutputFilesForBuildLine(
181 const FileTemplate& output_template,
182 const SourceFile& source,
183 std::vector<OutputFile>* output_files) {
184 // If there is a depfile specified we need to list it as the first output as
185 // that is what ninja will expect the depfile to refer to itself as.
186 if (target_->script_values().has_depfile()) {
187 out_ << " ";
188 WriteDepfile(source);
189 }
190 std::vector<std::string> output_template_result;
191 output_template.ApplyString(source.value(), &output_template_result);
192 for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
193 OutputFile output_path(output_template_result[out_i]);
194 output_files->push_back(output_path);
195 out_ << " ";
196 path_output_.WriteFile(out_, output_path);
197 }
198 }
199
WriteDepfile(const SourceFile & source)200 void NinjaScriptTargetWriter::WriteDepfile(const SourceFile& source) {
201 std::vector<std::string> result;
202 GetDepfileTemplate().ApplyString(source.value(), &result);
203 path_output_.WriteFile(out_, OutputFile(result[0]));
204 }
205
GetDepfileTemplate() const206 FileTemplate NinjaScriptTargetWriter::GetDepfileTemplate() const {
207 std::vector<std::string> template_args;
208 std::string depfile_relative_to_build_dir =
209 RemovePrefix(target_->script_values().depfile().value(),
210 settings_->build_settings()->build_dir().value());
211 template_args.push_back(depfile_relative_to_build_dir);
212 return FileTemplate(template_args);
213 }
214