blob: 4f9309fe27ab9c4598201bccac46dcf073b0b110 [file] [log] [blame]
// Copyright (c) 2023 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_outputs_writer.h"
#include <algorithm>
#include <memory>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/json/string_escape.h"
#include "gn/builder.h"
#include "gn/commands.h"
#include "gn/filesystem_utils.h"
#include "gn/invoke_python.h"
#include "gn/settings.h"
#include "gn/string_output_buffer.h"
// NOTE: Intentional macro definition allows compile-time string concatenation.
// (see usage below).
#if defined(OS_WINDOWS)
#define LINE_ENDING "\r\n"
#else
#define LINE_ENDING "\n"
#endif
namespace {
using MapType = NinjaOutputsWriter::MapType;
// Sort the targets according to their human visible labels first.
struct TargetLabelPair {
TargetLabelPair(const Target* target, const Label& default_toolchain_label)
: target(target),
label(std::make_unique<std::string>(
target->label().GetUserVisibleName(default_toolchain_label))) {}
const Target* target;
std::unique_ptr<std::string> label;
bool operator<(const TargetLabelPair& other) const {
return *label < *other.label;
}
using List = std::vector<TargetLabelPair>;
// Create list of TargetLabelPairs sorted by their target labels.
static List CreateSortedList(const MapType& outputs_map,
const Label& default_toolchain_label) {
List result;
result.reserve(outputs_map.size());
for (const auto& output_pair : outputs_map)
result.emplace_back(output_pair.first, default_toolchain_label);
std::sort(result.begin(), result.end());
return result;
}
};
} // namespace
// static
StringOutputBuffer NinjaOutputsWriter::GenerateJSON(
const MapType& outputs_map) {
Label default_toolchain_label;
if (!outputs_map.empty()) {
default_toolchain_label =
outputs_map.begin()->first->settings()->default_toolchain_label();
}
auto sorted_pairs =
TargetLabelPair::CreateSortedList(outputs_map, default_toolchain_label);
StringOutputBuffer out;
out.Append('{');
auto escape = [](std::string_view str) -> std::string {
std::string result;
base::EscapeJSONString(str, true, &result);
return result;
};
bool first_label = true;
for (const auto& pair : sorted_pairs) {
const Target* target = pair.target;
const std::string& label = *pair.label;
auto it = outputs_map.find(target);
CHECK(it != outputs_map.end());
if (!first_label)
out.Append(',');
first_label = false;
out.Append("\n ");
out.Append(escape(label));
out.Append(": [");
bool first_path = true;
for (const auto& output : it->second) {
if (!first_path)
out.Append(',');
first_path = false;
out.Append("\n ");
out.Append(escape(output.value()));
}
out.Append("\n ]");
}
out.Append("\n}");
return out;
}
bool NinjaOutputsWriter::RunAndWriteFiles(
const MapType& outputs_map,
const BuildSettings* build_settings,
const std::string& file_name,
const std::string& exec_script,
const std::string& exec_script_extra_args,
bool quiet,
Err* err) {
SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
Value(nullptr, file_name), err);
if (output_file.is_null()) {
return false;
}
StringOutputBuffer outputs = GenerateJSON(outputs_map);
base::FilePath output_path = build_settings->GetFullPath(output_file);
if (!outputs.ContentsEqual(output_path)) {
if (!outputs.WriteToFile(output_path, err)) {
return false;
}
if (!exec_script.empty()) {
SourceFile script_file;
if (exec_script[0] != '/') {
// Relative path, assume the base is in build_dir.
script_file = build_settings->build_dir().ResolveRelativeFile(
Value(nullptr, exec_script), err);
if (script_file.is_null()) {
return false;
}
} else {
script_file = SourceFile(exec_script);
}
base::FilePath script_path = build_settings->GetFullPath(script_file);
return internal::InvokePython(build_settings, script_path,
exec_script_extra_args, output_path, quiet,
err);
}
}
return true;
}