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_action_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 13 NinjaActionTargetWriter::NinjaActionTargetWriter(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) { 20 } 21 22 NinjaActionTargetWriter::~NinjaActionTargetWriter() { 23 } 24 25 void NinjaActionTargetWriter::Run() { 26 FileTemplate args_template(target_->settings(), 27 target_->action_values().args()); 28 std::string custom_rule_name = WriteRuleDefinition(args_template); 29 30 // Collect our deps to pass as "extra hard dependencies" for input deps. This 31 // will force all of the action's dependencies to be completed before the 32 // action is run. Usually, if an action has a dependency, it will be 33 // operating on the result of that previous step, so we need to be sure to 34 // serialize these. 35 std::vector<const Target*> extra_hard_deps; 36 for (size_t i = 0; i < target_->deps().size(); i++) 37 extra_hard_deps.push_back(target_->deps()[i].ptr); 38 39 // For ACTIONs this is a bit inefficient since it creates an input dep 40 // stamp file even though we're only going to use it once. It would save a 41 // build step to skip this and write the order-only deps directly on the 42 // build rule. This should probably be handled by WriteInputDepsStampAndGetDep 43 // automatically if we supply a count of sources (so it can optimize based on 44 // how many times things would be duplicated). 45 std::string implicit_deps = WriteInputDepsStampAndGetDep(extra_hard_deps); 46 out_ << std::endl; 47 48 // Collects all output files for writing below. 49 std::vector<OutputFile> output_files; 50 51 if (target_->output_type() == Target::ACTION_FOREACH) { 52 // Write separate build lines for each input source file. 53 WriteSourceRules(custom_rule_name, implicit_deps, args_template, 54 &output_files); 55 } else { 56 DCHECK(target_->output_type() == Target::ACTION); 57 58 // Write a rule that invokes the script once with the outputs as outputs, 59 // and the data as inputs. 60 out_ << "build"; 61 const Target::FileList& outputs = target_->action_values().outputs(); 62 for (size_t i = 0; i < outputs.size(); i++) { 63 OutputFile output_path( 64 RemovePrefix(outputs[i].value(), 65 settings_->build_settings()->build_dir().value())); 66 output_files.push_back(output_path); 67 out_ << " "; 68 path_output_.WriteFile(out_, output_path); 69 } 70 71 out_ << ": " << custom_rule_name << implicit_deps << std::endl; 72 if (target_->action_values().has_depfile()) { 73 out_ << " depfile = "; 74 WriteDepfile(SourceFile()); 75 out_ << std::endl; 76 } 77 } 78 out_ << std::endl; 79 80 WriteStamp(output_files); 81 } 82 83 std::string NinjaActionTargetWriter::WriteRuleDefinition( 84 const FileTemplate& args_template) { 85 // Make a unique name for this rule. 86 // 87 // Use a unique name for the response file when there are multiple build 88 // steps so that they don't stomp on each other. When there are no sources, 89 // there will be only one invocation so we can use a simple name. 90 std::string target_label = target_->label().GetUserVisibleName(true); 91 std::string custom_rule_name(target_label); 92 base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name); 93 custom_rule_name.append("_rule"); 94 95 if (settings_->IsWin()) { 96 // Send through gyp-win-tool and use a response file. 97 std::string rspfile = custom_rule_name; 98 if (has_sources()) 99 rspfile += ".$unique_name"; 100 rspfile += ".rsp"; 101 102 out_ << "rule " << custom_rule_name << std::endl; 103 out_ << " command = "; 104 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 105 // TODO(brettw) this hardcodes "environment.x86" which is something that 106 // the Chrome Windows toolchain writes. We should have a way to invoke 107 // python without requiring this gyp_win_tool thing. 108 out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile 109 << std::endl; 110 out_ << " description = ACTION " << target_label << std::endl; 111 out_ << " restat = 1" << std::endl; 112 out_ << " rspfile = " << rspfile << std::endl; 113 114 // The build command goes in the rsp file. 115 out_ << " rspfile_content = "; 116 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 117 out_ << " "; 118 path_output_.WriteFile(out_, target_->action_values().script()); 119 args_template.WriteWithNinjaExpansions(out_); 120 out_ << std::endl; 121 } else { 122 // Posix can execute Python directly. 123 out_ << "rule " << custom_rule_name << std::endl; 124 out_ << " command = "; 125 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 126 out_ << " "; 127 path_output_.WriteFile(out_, target_->action_values().script()); 128 args_template.WriteWithNinjaExpansions(out_); 129 out_ << std::endl; 130 out_ << " description = ACTION " << target_label << std::endl; 131 out_ << " restat = 1" << std::endl; 132 } 133 134 return custom_rule_name; 135 } 136 137 void NinjaActionTargetWriter::WriteArgsSubstitutions( 138 const SourceFile& source, 139 const FileTemplate& args_template) { 140 EscapeOptions template_escape_options; 141 template_escape_options.mode = ESCAPE_NINJA_COMMAND; 142 143 args_template.WriteNinjaVariablesForSubstitution( 144 out_, target_->settings(), source, template_escape_options); 145 } 146 147 void NinjaActionTargetWriter::WriteSourceRules( 148 const std::string& custom_rule_name, 149 const std::string& implicit_deps, 150 const FileTemplate& args_template, 151 std::vector<OutputFile>* output_files) { 152 FileTemplate output_template(GetOutputTemplate()); 153 154 const Target::FileList& sources = target_->sources(); 155 for (size_t i = 0; i < sources.size(); i++) { 156 out_ << "build"; 157 WriteOutputFilesForBuildLine(output_template, sources[i], output_files); 158 159 out_ << ": " << custom_rule_name << " "; 160 path_output_.WriteFile(out_, sources[i]); 161 out_ << implicit_deps << std::endl; 162 163 // Windows needs a unique ID for the response file. 164 if (target_->settings()->IsWin()) 165 out_ << " unique_name = " << i << std::endl; 166 167 if (args_template.has_substitutions()) 168 WriteArgsSubstitutions(sources[i], args_template); 169 170 if (target_->action_values().has_depfile()) { 171 out_ << " depfile = "; 172 WriteDepfile(sources[i]); 173 out_ << std::endl; 174 } 175 } 176 } 177 178 void NinjaActionTargetWriter::WriteStamp( 179 const std::vector<OutputFile>& output_files) { 180 out_ << "build "; 181 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_)); 182 out_ << ": " 183 << helper_.GetRulePrefix(target_->settings()) 184 << "stamp"; 185 186 // The action stamp depends on all output files from running the action. 187 for (size_t i = 0; i < output_files.size(); i++) { 188 out_ << " "; 189 path_output_.WriteFile(out_, output_files[i]); 190 } 191 192 // It also depends on all datadeps. These are needed at runtime and should 193 // be compiled when the action is, but don't need to be done before we run 194 // the action. 195 for (size_t i = 0; i < target_->datadeps().size(); i++) { 196 out_ << " "; 197 path_output_.WriteFile(out_, 198 helper_.GetTargetOutputFile(target_->datadeps()[i].ptr)); 199 } 200 201 out_ << std::endl; 202 } 203 204 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine( 205 const FileTemplate& output_template, 206 const SourceFile& source, 207 std::vector<OutputFile>* output_files) { 208 std::vector<std::string> output_template_result; 209 output_template.Apply(source, &output_template_result); 210 for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) { 211 OutputFile output_path(output_template_result[out_i]); 212 output_files->push_back(output_path); 213 out_ << " "; 214 path_output_.WriteFile(out_, output_path); 215 } 216 } 217 218 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) { 219 std::vector<std::string> result; 220 GetDepfileTemplate().Apply(source, &result); 221 path_output_.WriteFile(out_, OutputFile(result[0])); 222 } 223 224 FileTemplate NinjaActionTargetWriter::GetDepfileTemplate() const { 225 std::vector<std::string> template_args; 226 std::string depfile_relative_to_build_dir = 227 RemovePrefix(target_->action_values().depfile().value(), 228 settings_->build_settings()->build_dir().value()); 229 template_args.push_back(depfile_relative_to_build_dir); 230 return FileTemplate(settings_, template_args); 231 } 232