blob: f2004e3303e7c22a07114591d1170597daac6911 [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 "tools/gn/ninja_script_target_writer.h"
#include "base/strings/string_util.h"
#include "tools/gn/err.h"
#include "tools/gn/file_template.h"
#include "tools/gn/string_utils.h"
#include "tools/gn/target.h"
NinjaScriptTargetWriter::NinjaScriptTargetWriter(const Target* target,
const Toolchain* toolchain,
std::ostream& out)
: NinjaTargetWriter(target, toolchain, out),
path_output_no_escaping_(
target->settings()->build_settings()->build_dir(),
ESCAPE_NONE, false) {
}
NinjaScriptTargetWriter::~NinjaScriptTargetWriter() {
}
void NinjaScriptTargetWriter::Run() {
FileTemplate args_template(target_->script_values().args());
std::string custom_rule_name = WriteRuleDefinition(args_template);
std::string implicit_deps = GetSourcesImplicitDeps();
// Collects all output files for writing below.
std::vector<OutputFile> output_files;
if (has_sources()) {
// Write separate build lines for each input source file.
WriteSourceRules(custom_rule_name, implicit_deps, args_template,
&output_files);
} else {
// No sources, write a rule that invokes the script once with the
// outputs as outputs, and the data as inputs.
out_ << "build";
if (target_->script_values().has_depfile()) {
out_ << " ";
WriteDepfile(SourceFile());
}
const Target::FileList& outputs = target_->script_values().outputs();
for (size_t i = 0; i < outputs.size(); i++) {
OutputFile output_path(
RemovePrefix(outputs[i].value(),
settings_->build_settings()->build_dir().value()));
output_files.push_back(output_path);
out_ << " ";
path_output_.WriteFile(out_, output_path);
}
out_ << ": " << custom_rule_name << implicit_deps << std::endl;
if (target_->script_values().has_depfile()) {
out_ << " depfile = ";
WriteDepfile(SourceFile());
out_ << std::endl;
}
}
out_ << std::endl;
WriteStamp(output_files);
}
std::string NinjaScriptTargetWriter::WriteRuleDefinition(
const FileTemplate& args_template) {
// 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");
if (settings_->IsWin()) {
// Send through gyp-win-tool and use a response file.
std::string rspfile = custom_rule_name;
if (has_sources())
rspfile += ".$unique_name";
rspfile += ".rsp";
out_ << "rule " << custom_rule_name << std::endl;
out_ << " command = ";
path_output_.WriteFile(out_, settings_->build_settings()->python_path());
// TODO(brettw) this hardcodes "environment.x86" which is something that
// the Chrome Windows toolchain writes. We should have a way to invoke
// python without requiring this gyp_win_tool thing.
out_ << " gyp-win-tool action-wrapper environment.x86 " << rspfile
<< std::endl;
out_ << " description = CUSTOM " << target_label << std::endl;
out_ << " restat = 1" << std::endl;
out_ << " rspfile = " << rspfile << std::endl;
// The build command goes in the rsp file.
out_ << " rspfile_content = ";
path_output_.WriteFile(out_, settings_->build_settings()->python_path());
out_ << " ";
path_output_.WriteFile(out_, target_->script_values().script());
args_template.WriteWithNinjaExpansions(out_);
out_ << std::endl;
} else {
// Posix can execute Python directly.
out_ << "rule " << custom_rule_name << std::endl;
out_ << " command = ";
path_output_.WriteFile(out_, settings_->build_settings()->python_path());
out_ << " ";
path_output_.WriteFile(out_, target_->script_values().script());
args_template.WriteWithNinjaExpansions(out_);
out_ << std::endl;
out_ << " description = CUSTOM " << target_label << std::endl;
out_ << " restat = 1" << std::endl;
}
out_ << std::endl;
return custom_rule_name;
}
void NinjaScriptTargetWriter::WriteArgsSubstitutions(
const SourceFile& source,
const FileTemplate& args_template) {
std::ostringstream source_file_stream;
path_output_no_escaping_.WriteFile(source_file_stream, source);
EscapeOptions template_escape_options;
template_escape_options.mode = ESCAPE_NINJA_SHELL;
template_escape_options.inhibit_quoting = true;
args_template.WriteNinjaVariablesForSubstitution(
out_, source_file_stream.str(), template_escape_options);
}
void NinjaScriptTargetWriter::WriteSourceRules(
const std::string& custom_rule_name,
const std::string& implicit_deps,
const FileTemplate& args_template,
std::vector<OutputFile>* output_files) {
FileTemplate output_template(GetOutputTemplate());
const Target::FileList& sources = target_->sources();
for (size_t i = 0; i < sources.size(); i++) {
out_ << "build";
WriteOutputFilesForBuildLine(output_template, sources[i], output_files);
out_ << ": " << custom_rule_name << " ";
path_output_.WriteFile(out_, sources[i]);
out_ << implicit_deps << std::endl;
// Windows needs a unique ID for the response file.
if (target_->settings()->IsWin())
out_ << " unique_name = " << i << std::endl;
if (args_template.has_substitutions())
WriteArgsSubstitutions(sources[i], args_template);
if (target_->script_values().has_depfile()) {
out_ << " depfile = ";
WriteDepfile(sources[i]);
out_ << std::endl;
}
}
}
void NinjaScriptTargetWriter::WriteStamp(
const std::vector<OutputFile>& output_files) {
out_ << "build ";
path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_));
out_ << ": "
<< helper_.GetRulePrefix(target_->settings())
<< "stamp";
for (size_t i = 0; i < output_files.size(); i++) {
out_ << " ";
path_output_.WriteFile(out_, output_files[i]);
}
out_ << std::endl;
}
void NinjaScriptTargetWriter::WriteOutputFilesForBuildLine(
const FileTemplate& output_template,
const SourceFile& source,
std::vector<OutputFile>* output_files) {
// If there is a depfile specified we need to list it as the first output as
// that is what ninja will expect the depfile to refer to itself as.
if (target_->script_values().has_depfile()) {
out_ << " ";
WriteDepfile(source);
}
std::vector<std::string> output_template_result;
output_template.ApplyString(source.value(), &output_template_result);
for (size_t out_i = 0; out_i < output_template_result.size(); out_i++) {
OutputFile output_path(output_template_result[out_i]);
output_files->push_back(output_path);
out_ << " ";
path_output_.WriteFile(out_, output_path);
}
}
void NinjaScriptTargetWriter::WriteDepfile(const SourceFile& source) {
std::vector<std::string> result;
GetDepfileTemplate().ApplyString(source.value(), &result);
path_output_.WriteFile(out_, OutputFile(result[0]));
}
FileTemplate NinjaScriptTargetWriter::GetDepfileTemplate() const {
std::vector<std::string> template_args;
std::string depfile_relative_to_build_dir =
RemovePrefix(target_->script_values().depfile().value(),
settings_->build_settings()->build_dir().value());
template_args.push_back(depfile_relative_to_build_dir);
return FileTemplate(template_args);
}