|  | // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "tools/gn/ninja_action_target_writer.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include "base/strings/string_util.h" | 
|  | #include "tools/gn/deps_iterator.h" | 
|  | #include "tools/gn/err.h" | 
|  | #include "tools/gn/pool.h" | 
|  | #include "tools/gn/settings.h" | 
|  | #include "tools/gn/string_utils.h" | 
|  | #include "tools/gn/substitution_writer.h" | 
|  | #include "tools/gn/target.h" | 
|  |  | 
|  | NinjaActionTargetWriter::NinjaActionTargetWriter(const Target* target, | 
|  | std::ostream& out) | 
|  | : NinjaTargetWriter(target, out), | 
|  | path_output_no_escaping_( | 
|  | target->settings()->build_settings()->build_dir(), | 
|  | target->settings()->build_settings()->root_path_utf8(), | 
|  | ESCAPE_NONE) { | 
|  | } | 
|  |  | 
|  | NinjaActionTargetWriter::~NinjaActionTargetWriter() = default; | 
|  |  | 
|  | void NinjaActionTargetWriter::Run() { | 
|  | std::string custom_rule_name = WriteRuleDefinition(); | 
|  |  | 
|  | // Collect our deps to pass as "extra hard dependencies" for input deps. This | 
|  | // will force all of the action's dependencies to be completed before the | 
|  | // action is run. Usually, if an action has a dependency, it will be | 
|  | // operating on the result of that previous step, so we need to be sure to | 
|  | // serialize these. | 
|  | std::vector<const Target*> extra_hard_deps; | 
|  | for (const auto& pair : target_->GetDeps(Target::DEPS_LINKED)) | 
|  | extra_hard_deps.push_back(pair.ptr); | 
|  |  | 
|  | // For ACTIONs, the input deps appear only once in the generated ninja | 
|  | // file, so WriteInputDepsStampAndGetDep() won't create a stamp file | 
|  | // and the action will just depend on all the input deps directly. | 
|  | size_t num_stamp_uses = | 
|  | target_->output_type() == Target::ACTION ? 1u : target_->sources().size(); | 
|  | std::vector<OutputFile> input_deps = | 
|  | WriteInputDepsStampAndGetDep(extra_hard_deps, num_stamp_uses); | 
|  | out_ << std::endl; | 
|  |  | 
|  | // Collects all output files for writing below. | 
|  | std::vector<OutputFile> output_files; | 
|  |  | 
|  | if (target_->output_type() == Target::ACTION_FOREACH) { | 
|  | // Write separate build lines for each input source file. | 
|  | WriteSourceRules(custom_rule_name, input_deps, &output_files); | 
|  | } else { | 
|  | DCHECK(target_->output_type() == Target::ACTION); | 
|  |  | 
|  | // Write a rule that invokes the script once with the outputs as outputs, | 
|  | // and the data as inputs. It does not depend on the sources. | 
|  | out_ << "build"; | 
|  | SubstitutionWriter::GetListAsOutputFiles( | 
|  | settings_, target_->action_values().outputs(), &output_files); | 
|  | path_output_.WriteFiles(out_, output_files); | 
|  |  | 
|  | out_ << ": " << custom_rule_name; | 
|  | if (!input_deps.empty()) { | 
|  | // As in WriteSourceRules, we want to force this target to rebuild any | 
|  | // time any of its dependencies change. | 
|  | out_ << " |"; | 
|  | path_output_.WriteFiles(out_, input_deps); | 
|  | } | 
|  | out_ << std::endl; | 
|  | if (target_->action_values().has_depfile()) { | 
|  | out_ << "  depfile = "; | 
|  | WriteDepfile(SourceFile()); | 
|  | out_ << std::endl; | 
|  | } | 
|  | if (target_->action_values().pool().ptr) { | 
|  | out_ << "  pool = "; | 
|  | out_ << target_->action_values().pool().ptr->GetNinjaName( | 
|  | settings_->default_toolchain_label()); | 
|  | out_ << std::endl; | 
|  | } | 
|  | } | 
|  | out_ << std::endl; | 
|  |  | 
|  | // Write the stamp, which also depends on all data deps. These are needed at | 
|  | // runtime and should be compiled when the action is, but don't need to be | 
|  | // done before we run the action. | 
|  | // TODO(thakis): If the action has just a single output, make things depend | 
|  | // on that output directly without writing a stamp file. | 
|  | std::vector<OutputFile> data_outs; | 
|  | for (const auto& dep : target_->data_deps()) | 
|  | data_outs.push_back(dep.ptr->dependency_output_file()); | 
|  | WriteStampForTarget(output_files, data_outs); | 
|  | } | 
|  |  | 
|  | std::string NinjaActionTargetWriter::WriteRuleDefinition() { | 
|  | // Make a unique name for this rule. | 
|  | // | 
|  | // Use a unique name for the response file when there are multiple build | 
|  | // steps so that they don't stomp on each other. When there are no sources, | 
|  | // there will be only one invocation so we can use a simple name. | 
|  | std::string target_label = target_->label().GetUserVisibleName(true); | 
|  | std::string custom_rule_name(target_label); | 
|  | base::ReplaceChars(custom_rule_name, ":/()", "_", &custom_rule_name); | 
|  | custom_rule_name.append("_rule"); | 
|  |  | 
|  | const SubstitutionList& args = target_->action_values().args(); | 
|  | EscapeOptions args_escape_options; | 
|  | args_escape_options.mode = ESCAPE_NINJA_COMMAND; | 
|  |  | 
|  | out_ << "rule " << custom_rule_name << std::endl; | 
|  |  | 
|  | if (target_->action_values().uses_rsp_file()) { | 
|  | // Needs a response file. The unique_name part is for action_foreach so | 
|  | // each invocation of the rule gets a different response file. This isn't | 
|  | // strictly necessary for regular one-shot actions, but it's easier to | 
|  | // just always define unique_name. | 
|  | std::string rspfile = custom_rule_name; | 
|  | if (!target_->sources().empty()) | 
|  | rspfile += ".$unique_name"; | 
|  | rspfile += ".rsp"; | 
|  | out_ << "  rspfile = " << rspfile << std::endl; | 
|  |  | 
|  | // Response file contents. | 
|  | out_ << "  rspfile_content ="; | 
|  | for (const auto& arg : | 
|  | target_->action_values().rsp_file_contents().list()) { | 
|  | out_ << " "; | 
|  | SubstitutionWriter::WriteWithNinjaVariables( | 
|  | arg, args_escape_options, out_); | 
|  | } | 
|  | out_ << std::endl; | 
|  | } | 
|  |  | 
|  | out_ << "  command = "; | 
|  | path_output_.WriteFile(out_, settings_->build_settings()->python_path()); | 
|  | out_ << " "; | 
|  | path_output_.WriteFile(out_, target_->action_values().script()); | 
|  | for (const auto& arg : args.list()) { | 
|  | out_ << " "; | 
|  | SubstitutionWriter::WriteWithNinjaVariables( | 
|  | arg, args_escape_options, out_); | 
|  | } | 
|  | out_ << std::endl; | 
|  | out_ << "  description = ACTION " << target_label << std::endl; | 
|  | out_ << "  restat = 1" << std::endl; | 
|  | const Tool* tool = target_->toolchain()->GetTool(Toolchain::TYPE_ACTION); | 
|  | if (tool && tool->pool().ptr) { | 
|  | out_ << "  pool = "; | 
|  | out_ << tool->pool().ptr->GetNinjaName( | 
|  | settings_->default_toolchain_label()); | 
|  | out_ << std::endl; | 
|  | } | 
|  |  | 
|  | return custom_rule_name; | 
|  | } | 
|  |  | 
|  | void NinjaActionTargetWriter::WriteSourceRules( | 
|  | const std::string& custom_rule_name, | 
|  | const std::vector<OutputFile>& input_deps, | 
|  | std::vector<OutputFile>* output_files) { | 
|  | EscapeOptions args_escape_options; | 
|  | args_escape_options.mode = ESCAPE_NINJA_COMMAND; | 
|  | // We're writing the substitution values, these should not be quoted since | 
|  | // they will get pasted into the real command line. | 
|  | args_escape_options.inhibit_quoting = true; | 
|  |  | 
|  | const Target::FileList& sources = target_->sources(); | 
|  | for (size_t i = 0; i < sources.size(); i++) { | 
|  | out_ << "build"; | 
|  | WriteOutputFilesForBuildLine(sources[i], output_files); | 
|  |  | 
|  | out_ << ": " << custom_rule_name << " "; | 
|  | path_output_.WriteFile(out_, sources[i]); | 
|  | if (!input_deps.empty()) { | 
|  | // Using "|" for the dependencies forces all implicit dependencies to be | 
|  | // fully up to date before running the action, and will re-run this | 
|  | // action if any input dependencies change. This is important because | 
|  | // this action may consume the outputs of previous steps. | 
|  | out_ << " |"; | 
|  | path_output_.WriteFiles(out_, input_deps); | 
|  | } | 
|  | out_ << std::endl; | 
|  |  | 
|  | // Response files require a unique name be defined. | 
|  | if (target_->action_values().uses_rsp_file()) | 
|  | out_ << "  unique_name = " << i << std::endl; | 
|  |  | 
|  | // The required types is the union of the args and response file. This | 
|  | // might theoretically duplicate a definition if the same substitution is | 
|  | // used in both the args and the response file. However, this should be | 
|  | // very unusual (normally the substitutions will go in one place or the | 
|  | // other) and the redundant assignment won't bother Ninja. | 
|  | SubstitutionWriter::WriteNinjaVariablesForSource( | 
|  | target_, settings_, sources[i], | 
|  | target_->action_values().args().required_types(), | 
|  | args_escape_options, out_); | 
|  | SubstitutionWriter::WriteNinjaVariablesForSource( | 
|  | target_, settings_, sources[i], | 
|  | target_->action_values().rsp_file_contents().required_types(), | 
|  | args_escape_options, out_); | 
|  |  | 
|  | if (target_->action_values().has_depfile()) { | 
|  | out_ << "  depfile = "; | 
|  | WriteDepfile(sources[i]); | 
|  | out_ << std::endl; | 
|  | } | 
|  | if (target_->action_values().pool().ptr) { | 
|  | out_ << "  pool = "; | 
|  | out_ << target_->action_values().pool().ptr->GetNinjaName( | 
|  | settings_->default_toolchain_label()); | 
|  | out_ << std::endl; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void NinjaActionTargetWriter::WriteOutputFilesForBuildLine( | 
|  | const SourceFile& source, | 
|  | std::vector<OutputFile>* output_files) { | 
|  | size_t first_output_index = output_files->size(); | 
|  |  | 
|  | SubstitutionWriter::ApplyListToSourceAsOutputFile( | 
|  | target_, settings_, target_->action_values().outputs(), source, | 
|  | output_files); | 
|  |  | 
|  | for (size_t i = first_output_index; i < output_files->size(); i++) { | 
|  | out_ << " "; | 
|  | path_output_.WriteFile(out_, (*output_files)[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void NinjaActionTargetWriter::WriteDepfile(const SourceFile& source) { | 
|  | path_output_.WriteFile(out_, | 
|  | SubstitutionWriter::ApplyPatternToSourceAsOutputFile( | 
|  | target_, settings_, target_->action_values().depfile(), source)); | 
|  | } |