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 13NinjaScriptTargetWriter::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 22NinjaScriptTargetWriter::~NinjaScriptTargetWriter() { 23} 24 25void 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 66std::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 121void 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 135void 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 166void 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 180void 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 200void 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 206FileTemplate 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