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/deps_iterator.h" 9 #include "tools/gn/err.h" 10 #include "tools/gn/settings.h" 11 #include "tools/gn/string_utils.h" 12 #include "tools/gn/substitution_writer.h" 13 #include "tools/gn/target.h" 14 15 NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target, 16 std::ostream& out) 17 : NinjaTargetWriter(target, out), 18 path_output_no_escaping_( 19 target->settings()->build_settings()->build_dir(), 20 ESCAPE_NONE) { 21 } 22 23 NinjaActionTargetWriter::~NinjaActionTargetWriter() { 24 } 25 26 void NinjaActionTargetWriter::Run() { 27 std::string custom_rule_name = WriteRuleDefinition(); 28 29 // Collect our deps to pass as "extra hard dependencies" for input deps. This 30 // will force all of the action's dependencies to be completed before the 31 // action is run. Usually, if an action has a dependency, it will be 32 // operating on the result of that previous step, so we need to be sure to 33 // serialize these. 34 std::vector<const Target*> extra_hard_deps; 35 for (DepsIterator iter(target_, DepsIterator::LINKED_ONLY); 36 !iter.done(); iter.Advance()) 37 extra_hard_deps.push_back(iter.target()); 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 OutputFile input_dep = 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, input_dep, &output_files); 54 } else { 55 DCHECK(target_->output_type() == Target::ACTION); 56 57 // Write a rule that invokes the script once with the outputs as outputs, 58 // and the data as inputs. It does not depend on the sources. 59 out_ << "build"; 60 SubstitutionWriter::GetListAsOutputFiles( 61 settings_, target_->action_values().outputs(), &output_files); 62 path_output_.WriteFiles(out_, output_files); 63 64 out_ << ": " << custom_rule_name; 65 if (!input_dep.value().empty()) { 66 // As in WriteSourceRules, we want to force this target to rebuild any 67 // time any of its dependencies change. 68 out_ << " | "; 69 path_output_.WriteFile(out_, input_dep); 70 } 71 out_ << 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 // Write the stamp, which also depends on all data deps. These are needed at 81 // runtime and should be compiled when the action is, but don't need to be 82 // done before we run the action. 83 std::vector<OutputFile> data_outs; 84 for (size_t i = 0; i < target_->data_deps().size(); i++) 85 data_outs.push_back(target_->data_deps()[i].ptr->dependency_output_file()); 86 WriteStampForTarget(output_files, data_outs); 87 } 88 89 std::string NinjaActionTargetWriter::WriteRuleDefinition() { 90 // Make a unique name for this rule. 91 // 92 // Use a unique name for the response file when there are multiple build 93 // steps so that they don't stomp on each other. When there are no sources, 94 // there will be only one invocation so we can use a simple name. 95 std::string target_label = target_->label().GetUserVisibleName(true); 96 std::string custom_rule_name(target_label); 97 base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name); 98 custom_rule_name.append("_rule"); 99 100 const SubstitutionList& args = target_->action_values().args(); 101 EscapeOptions args_escape_options; 102 args_escape_options.mode = ESCAPE_NINJA_COMMAND; 103 104 if (settings_->IsWin()) { 105 // Send through gyp-win-tool and use a response file. 106 std::string rspfile = custom_rule_name; 107 if (!target_->sources().empty()) 108 rspfile += ".$unique_name"; 109 rspfile += ".rsp"; 110 111 out_ << "rule " << custom_rule_name << std::endl; 112 out_ << " command = "; 113 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 114 // TODO(brettw) this hardcodes "environment.x86" which is something that 115 // the Chrome Windows toolchain writes. We should have a way to invoke 116 // python without requiring this gyp_win_tool thing. 117 out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile 118 << std::endl; 119 out_ << " description = ACTION " << target_label << std::endl; 120 out_ << " restat = 1" << std::endl; 121 out_ << " rspfile = " << rspfile << std::endl; 122 123 // The build command goes in the rsp file. 124 out_ << " rspfile_content = "; 125 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 126 out_ << " "; 127 path_output_.WriteFile(out_, target_->action_values().script()); 128 for (size_t i = 0; i < args.list().size(); i++) { 129 out_ << " "; 130 SubstitutionWriter::WriteWithNinjaVariables( 131 args.list()[i], args_escape_options, out_); 132 } 133 out_ << std::endl; 134 } else { 135 // Posix can execute Python directly. 136 out_ << "rule " << custom_rule_name << std::endl; 137 out_ << " command = "; 138 path_output_.WriteFile(out_, settings_->build_settings()->python_path()); 139 out_ << " "; 140 path_output_.WriteFile(out_, target_->action_values().script()); 141 for (size_t i = 0; i < args.list().size(); i++) { 142 out_ << " "; 143 SubstitutionWriter::WriteWithNinjaVariables( 144 args.list()[i], args_escape_options, out_); 145 } 146 out_ << std::endl; 147 out_ << " description = ACTION " << target_label << std::endl; 148 out_ << " restat = 1" << std::endl; 149 } 150 151 return custom_rule_name; 152 } 153 154 void NinjaActionTargetWriter::WriteSourceRules( 155 const std::string& custom_rule_name, 156 const OutputFile& input_dep, 157 std::vector<OutputFile>* output_files) { 158 EscapeOptions args_escape_options; 159 args_escape_options.mode = ESCAPE_NINJA_COMMAND; 160 // We're writing the substitution values, these should not be quoted since 161 // they will get pasted into the real command line. 162 args_escape_options.inhibit_quoting = true; 163 164 const std::vector<SubstitutionType>& args_substitutions_used = 165 target_->action_values().args().required_types(); 166 167 const Target::FileList& sources = target_->sources(); 168 for (size_t i = 0; i < sources.size(); i++) { 169 out_ << "build"; 170 WriteOutputFilesForBuildLine(sources[i], output_files); 171 172 out_ << ": " << custom_rule_name << " "; 173 path_output_.WriteFile(out_, sources[i]); 174 if (!input_dep.value().empty()) { 175 // Using "|" for the dependencies forces all implicit dependencies to be 176 // fully up-to-date before running the action, and will re-run this 177 // action if any input dependencies change. This is important because 178 // this action may consume the outputs of previous steps. 179 out_ << " | "; 180 path_output_.WriteFile(out_, input_dep); 181 } 182 out_ << std::endl; 183 184 // Windows needs a unique ID for the response file. 185 if (target_->settings()->IsWin()) 186 out_ << " unique_name = " << i << std::endl; 187 188 SubstitutionWriter::WriteNinjaVariablesForSource( 189 settings_, sources[i], args_substitutions_used, 190 args_escape_options, out_); 191 192 if (target_->action_values().has_depfile()) { 193 out_ << " depfile = "; 194 WriteDepfile(sources[i]); 195 out_ << std::endl; 196 } 197 } 198 } 199 200 void NinjaActionTargetWriter::WriteOutputFilesForBuildLine( 201 const SourceFile& source, 202 std::vector<OutputFile>* output_files) { 203 size_t first_output_index = output_files->size(); 204 205 SubstitutionWriter::ApplyListToSourceAsOutputFile( 206 settings_, target_->action_values().outputs(), source, output_files); 207 208 for (size_t i = first_output_index; i < output_files->size(); i++) { 209 out_ << " "; 210 path_output_.WriteFile(out_, (*output_files)[i]); 211 } 212 } 213 214 void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) { 215 path_output_.WriteFile(out_, 216 SubstitutionWriter::ApplyPatternToSourceAsOutputFile( 217 settings_, target_->action_values().depfile(), source)); 218 } 219