blob: 644b84557ebfa7cc952244c63fc5edb053abb34f [file] [log] [blame]
// 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 "gn/ninja_action_target_writer.h"
#include <stddef.h>
#include "base/strings/string_util.h"
#include "gn/deps_iterator.h"
#include "gn/err.h"
#include "gn/general_tool.h"
#include "gn/pool.h"
#include "gn/settings.h"
#include "gn/string_utils.h"
#include "gn/substitution_writer.h"
#include "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 additional "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*> additional_hard_deps;
std::vector<OutputFile> data_outs;
const auto& target_deps = resolved().GetTargetDeps(target_);
for (const Target* dep : target_deps.linked_deps()) {
if (dep->IsDataOnly()) {
data_outs.push_back(dep->dependency_output_file());
} else {
additional_hard_deps.push_back(dep);
}
}
// 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(additional_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()) {
WriteDepfile(SourceFile());
}
WriteNinjaVariablesForAction();
if (target_->pool().ptr) {
out_ << " pool = ";
out_ << target_->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.
for (const Target* data_dep : target_deps.data_deps())
data_outs.push_back(data_dep->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;
}
// The command line requires shell escaping to properly handle filenames
// with spaces.
PathOutput command_output(path_output_.current_dir(),
settings_->build_settings()->root_path_utf8(),
ESCAPE_NINJA_COMMAND);
out_ << " command = ";
command_output.WriteFile(out_, settings_->build_settings()->python_path());
out_ << " ";
command_output.WriteFile(out_, target_->action_values().script());
for (const auto& arg : args.list()) {
out_ << " ";
SubstitutionWriter::WriteWithNinjaVariables(arg, args_escape_options, out_);
}
out_ << std::endl;
auto mnemonic = target_->action_values().mnemonic();
if (mnemonic.empty())
mnemonic = "ACTION";
out_ << " description = " << mnemonic << " " << target_label << std::endl;
out_ << " restat = 1" << std::endl;
const Tool* tool =
target_->toolchain()->GetTool(GeneralTool::kGeneralToolAction);
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_);
WriteNinjaVariablesForAction();
if (target_->action_values().has_depfile()) {
WriteDepfile(sources[i]);
}
if (target_->pool().ptr) {
out_ << " pool = ";
out_ << target_->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) {
out_ << " depfile = ";
path_output_.WriteFile(
out_,
SubstitutionWriter::ApplyPatternToSourceAsOutputFile(
target_, settings_, target_->action_values().depfile(), source));
out_ << std::endl;
// Using "deps = gcc" allows Ninja to read and store the depfile content in
// its internal database which improves performance, especially for large
// depfiles. The use of this feature with depfiles that contain multiple
// outputs require Ninja version 1.9.0 or newer.
if (settings_->build_settings()->ninja_required_version() >=
Version{1, 9, 0}) {
out_ << " deps = gcc" << std::endl;
}
}
void NinjaActionTargetWriter::WriteNinjaVariablesForAction() {
SubstitutionBits subst;
target_->action_values().args().FillRequiredTypes(&subst);
WriteRustCompilerVars(subst, /*indent=*/true, /*always_write=*/false);
WriteCCompilerVars(subst, /*indent=*/true, /*respect_source_types=*/false);
}