• 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 "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