mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 06:16:30 +03:00
430 lines
15 KiB
C++
430 lines
15 KiB
C++
|
// 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/loader.h"
|
||
|
|
||
|
#include <memory>
|
||
|
|
||
|
#include "base/bind.h"
|
||
|
#include "base/threading/thread_task_runner_handle.h"
|
||
|
#include "tools/gn/build_settings.h"
|
||
|
#include "tools/gn/err.h"
|
||
|
#include "tools/gn/filesystem_utils.h"
|
||
|
#include "tools/gn/input_file_manager.h"
|
||
|
#include "tools/gn/parse_tree.h"
|
||
|
#include "tools/gn/scheduler.h"
|
||
|
#include "tools/gn/scope_per_file_provider.h"
|
||
|
#include "tools/gn/settings.h"
|
||
|
#include "tools/gn/source_dir.h"
|
||
|
#include "tools/gn/source_file.h"
|
||
|
#include "tools/gn/trace.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
struct SourceFileAndOrigin {
|
||
|
SourceFileAndOrigin(const SourceFile& f, const LocationRange& o)
|
||
|
: file(f), origin(o) {}
|
||
|
|
||
|
SourceFile file;
|
||
|
LocationRange origin;
|
||
|
};
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
// Identifies one time a file is loaded in a given toolchain so we don't load
|
||
|
// it more than once.
|
||
|
struct LoaderImpl::LoadID {
|
||
|
LoadID() = default;
|
||
|
LoadID(const SourceFile& f, const Label& tc_name)
|
||
|
: file(f), toolchain_name(tc_name) {}
|
||
|
|
||
|
bool operator<(const LoadID& other) const {
|
||
|
if (file.value() == other.file.value())
|
||
|
return toolchain_name < other.toolchain_name;
|
||
|
return file < other.file;
|
||
|
}
|
||
|
|
||
|
SourceFile file;
|
||
|
Label toolchain_name;
|
||
|
};
|
||
|
|
||
|
// Our tracking information for a toolchain.
|
||
|
struct LoaderImpl::ToolchainRecord {
|
||
|
// The default toolchain label can be empty for the first time the default
|
||
|
// toolchain is loaded, since we don't know it yet. This will be fixed up
|
||
|
// later. It should be valid in all other cases.
|
||
|
ToolchainRecord(const BuildSettings* build_settings,
|
||
|
const Label& toolchain_label,
|
||
|
const Label& default_toolchain_label)
|
||
|
: settings(
|
||
|
build_settings,
|
||
|
GetOutputSubdirName(toolchain_label,
|
||
|
toolchain_label == default_toolchain_label)),
|
||
|
is_toolchain_loaded(false),
|
||
|
is_config_loaded(false) {
|
||
|
settings.set_default_toolchain_label(default_toolchain_label);
|
||
|
settings.set_toolchain_label(toolchain_label);
|
||
|
}
|
||
|
|
||
|
Settings settings;
|
||
|
|
||
|
bool is_toolchain_loaded;
|
||
|
bool is_config_loaded;
|
||
|
|
||
|
std::vector<SourceFileAndOrigin> waiting_on_me;
|
||
|
};
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
const void* const Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
|
||
|
|
||
|
Loader::Loader() = default;
|
||
|
|
||
|
Loader::~Loader() = default;
|
||
|
|
||
|
void Loader::Load(const Label& label, const LocationRange& origin) {
|
||
|
Load(BuildFileForLabel(label), origin, label.GetToolchainLabel());
|
||
|
}
|
||
|
|
||
|
// static
|
||
|
SourceFile Loader::BuildFileForLabel(const Label& label) {
|
||
|
return SourceFile(label.dir().value() + "BUILD.gn");
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
|
||
|
: pending_loads_(0), build_settings_(build_settings) {
|
||
|
// There may not be an active TaskRunner at this point. When that's the case,
|
||
|
// the calling code is expected to call set_task_runner().
|
||
|
if (base::ThreadTaskRunnerHandle::IsSet())
|
||
|
task_runner_ = base::ThreadTaskRunnerHandle::Get();
|
||
|
}
|
||
|
|
||
|
LoaderImpl::~LoaderImpl() = default;
|
||
|
|
||
|
void LoaderImpl::Load(const SourceFile& file,
|
||
|
const LocationRange& origin,
|
||
|
const Label& in_toolchain_name) {
|
||
|
const Label& toolchain_name = in_toolchain_name.is_null()
|
||
|
? default_toolchain_label_
|
||
|
: in_toolchain_name;
|
||
|
LoadID load_id(file, toolchain_name);
|
||
|
if (!invocations_.insert(load_id).second)
|
||
|
return; // Already in set, so this file was already loaded or schedulerd.
|
||
|
|
||
|
if (toolchain_records_.empty()) {
|
||
|
// Nothing loaded, need to load the default build config. The initial load
|
||
|
// should not specify a toolchain.
|
||
|
DCHECK(toolchain_name.is_null());
|
||
|
|
||
|
std::unique_ptr<ToolchainRecord> new_record =
|
||
|
std::make_unique<ToolchainRecord>(build_settings_, Label(), Label());
|
||
|
ToolchainRecord* record = new_record.get();
|
||
|
toolchain_records_[Label()] = std::move(new_record);
|
||
|
|
||
|
// The default build config is no dependent on the toolchain definition,
|
||
|
// since we need to load the build config before we know what the default
|
||
|
// toolchain name is.
|
||
|
record->is_toolchain_loaded = true;
|
||
|
|
||
|
record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
|
||
|
ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ToolchainRecord* record;
|
||
|
if (toolchain_name.is_null())
|
||
|
record = toolchain_records_[default_toolchain_label_].get();
|
||
|
else
|
||
|
record = toolchain_records_[toolchain_name].get();
|
||
|
|
||
|
if (!record) {
|
||
|
DCHECK(!default_toolchain_label_.is_null());
|
||
|
|
||
|
// No reference to this toolchain found yet, make one.
|
||
|
std::unique_ptr<ToolchainRecord> new_record =
|
||
|
std::make_unique<ToolchainRecord>(build_settings_, toolchain_name,
|
||
|
default_toolchain_label_);
|
||
|
record = new_record.get();
|
||
|
toolchain_records_[toolchain_name] = std::move(new_record);
|
||
|
|
||
|
// Schedule a load of the toolchain using the default one.
|
||
|
Load(BuildFileForLabel(toolchain_name), origin, default_toolchain_label_);
|
||
|
}
|
||
|
|
||
|
if (record->is_config_loaded)
|
||
|
ScheduleLoadFile(&record->settings, origin, file);
|
||
|
else
|
||
|
record->waiting_on_me.push_back(SourceFileAndOrigin(file, origin));
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
|
||
|
ToolchainRecord* record = toolchain_records_[toolchain->label()].get();
|
||
|
if (!record) {
|
||
|
DCHECK(!default_toolchain_label_.is_null());
|
||
|
std::unique_ptr<ToolchainRecord> new_record =
|
||
|
std::make_unique<ToolchainRecord>(build_settings_, toolchain->label(),
|
||
|
default_toolchain_label_);
|
||
|
record = new_record.get();
|
||
|
toolchain_records_[toolchain->label()] = std::move(new_record);
|
||
|
}
|
||
|
record->is_toolchain_loaded = true;
|
||
|
|
||
|
// The default build config is loaded first, then its toolchain. Secondary
|
||
|
// ones are loaded in the opposite order so we can pass toolchain parameters
|
||
|
// to the build config. So we may or may not have a config at this point.
|
||
|
if (!record->is_config_loaded) {
|
||
|
ScheduleLoadBuildConfig(&record->settings, toolchain->args());
|
||
|
} else {
|
||
|
// There should be nobody waiting on this if the build config is already
|
||
|
// loaded.
|
||
|
DCHECK(record->waiting_on_me.empty());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Label LoaderImpl::GetDefaultToolchain() const {
|
||
|
return default_toolchain_label_;
|
||
|
}
|
||
|
|
||
|
const Settings* LoaderImpl::GetToolchainSettings(const Label& label) const {
|
||
|
ToolchainRecordMap::const_iterator found_toolchain;
|
||
|
if (label.is_null()) {
|
||
|
if (default_toolchain_label_.is_null())
|
||
|
return nullptr;
|
||
|
found_toolchain = toolchain_records_.find(default_toolchain_label_);
|
||
|
} else {
|
||
|
found_toolchain = toolchain_records_.find(label);
|
||
|
}
|
||
|
|
||
|
if (found_toolchain == toolchain_records_.end())
|
||
|
return nullptr;
|
||
|
return &found_toolchain->second->settings;
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::ScheduleLoadFile(const Settings* settings,
|
||
|
const LocationRange& origin,
|
||
|
const SourceFile& file) {
|
||
|
Err err;
|
||
|
pending_loads_++;
|
||
|
if (!AsyncLoadFile(origin, settings->build_settings(), file,
|
||
|
base::Bind(&LoaderImpl::BackgroundLoadFile, this,
|
||
|
settings, file, origin),
|
||
|
&err)) {
|
||
|
g_scheduler->FailWithError(err);
|
||
|
DecrementPendingLoads();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::ScheduleLoadBuildConfig(
|
||
|
Settings* settings,
|
||
|
const Scope::KeyValueMap& toolchain_overrides) {
|
||
|
Err err;
|
||
|
pending_loads_++;
|
||
|
if (!AsyncLoadFile(LocationRange(), settings->build_settings(),
|
||
|
settings->build_settings()->build_config_file(),
|
||
|
base::Bind(&LoaderImpl::BackgroundLoadBuildConfig,
|
||
|
this, settings, toolchain_overrides),
|
||
|
&err)) {
|
||
|
g_scheduler->FailWithError(err);
|
||
|
DecrementPendingLoads();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::BackgroundLoadFile(const Settings* settings,
|
||
|
const SourceFile& file_name,
|
||
|
const LocationRange& origin,
|
||
|
const ParseNode* root) {
|
||
|
if (!root) {
|
||
|
task_runner_->PostTask(
|
||
|
FROM_HERE, base::Bind(&LoaderImpl::DecrementPendingLoads, this));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (g_scheduler->verbose_logging()) {
|
||
|
g_scheduler->Log("Running", file_name.value() + " with toolchain " +
|
||
|
settings->toolchain_label().GetUserVisibleName(false));
|
||
|
}
|
||
|
|
||
|
Scope our_scope(settings->base_config());
|
||
|
ScopePerFileProvider per_file_provider(&our_scope, true);
|
||
|
our_scope.set_source_dir(file_name.GetDir());
|
||
|
our_scope.AddBuildDependencyFile(file_name);
|
||
|
|
||
|
// Targets, etc. generated as part of running this file will end up here.
|
||
|
Scope::ItemVector collected_items;
|
||
|
our_scope.set_item_collector(&collected_items);
|
||
|
|
||
|
ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value());
|
||
|
trace.SetToolchain(settings->toolchain_label());
|
||
|
|
||
|
Err err;
|
||
|
root->Execute(&our_scope, &err);
|
||
|
if (!err.has_error())
|
||
|
our_scope.CheckForUnusedVars(&err);
|
||
|
|
||
|
if (err.has_error()) {
|
||
|
if (!origin.is_null())
|
||
|
err.AppendSubErr(Err(origin, "which caused the file to be included."));
|
||
|
g_scheduler->FailWithError(err);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Pass all of the items that were defined off to the builder.
|
||
|
for (auto& item : collected_items)
|
||
|
settings->build_settings()->ItemDefined(std::move(item));
|
||
|
|
||
|
trace.Done();
|
||
|
|
||
|
task_runner_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::BackgroundLoadBuildConfig(
|
||
|
Settings* settings,
|
||
|
const Scope::KeyValueMap& toolchain_overrides,
|
||
|
const ParseNode* root) {
|
||
|
if (!root) {
|
||
|
task_runner_->PostTask(
|
||
|
FROM_HERE, base::Bind(&LoaderImpl::DecrementPendingLoads, this));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Scope* base_config = settings->base_config();
|
||
|
base_config->set_source_dir(SourceDir("//"));
|
||
|
base_config->AddBuildDependencyFile(
|
||
|
settings->build_settings()->build_config_file());
|
||
|
|
||
|
settings->build_settings()->build_args().SetupRootScope(base_config,
|
||
|
toolchain_overrides);
|
||
|
|
||
|
base_config->SetProcessingBuildConfig();
|
||
|
|
||
|
// See kDefaultToolchainKey in the header.
|
||
|
Label default_toolchain_label;
|
||
|
if (settings->is_default())
|
||
|
base_config->SetProperty(kDefaultToolchainKey, &default_toolchain_label);
|
||
|
|
||
|
ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
|
||
|
settings->build_settings()->build_config_file().value());
|
||
|
trace.SetToolchain(settings->toolchain_label());
|
||
|
|
||
|
Err err;
|
||
|
root->Execute(base_config, &err);
|
||
|
|
||
|
// Clear all private variables left in the scope. We want the root build
|
||
|
// config to be like a .gni file in that variables beginning with an
|
||
|
// underscore aren't exported.
|
||
|
base_config->RemovePrivateIdentifiers();
|
||
|
|
||
|
trace.Done();
|
||
|
|
||
|
if (err.has_error())
|
||
|
g_scheduler->FailWithError(err);
|
||
|
|
||
|
base_config->ClearProcessingBuildConfig();
|
||
|
if (settings->is_default()) {
|
||
|
// The default toolchain must have been set in the default build config
|
||
|
// file.
|
||
|
if (default_toolchain_label.is_null()) {
|
||
|
g_scheduler->FailWithError(Err(
|
||
|
Location(),
|
||
|
"The default build config file did not call set_default_toolchain()",
|
||
|
"If you don't call this, I can't figure out what toolchain to use\n"
|
||
|
"for all of this code."));
|
||
|
} else {
|
||
|
DCHECK(settings->toolchain_label().is_null());
|
||
|
settings->set_toolchain_label(default_toolchain_label);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
task_runner_->PostTask(FROM_HERE,
|
||
|
base::Bind(&LoaderImpl::DidLoadBuildConfig, this,
|
||
|
settings->toolchain_label()));
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::DidLoadFile() {
|
||
|
DecrementPendingLoads();
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::DidLoadBuildConfig(const Label& label) {
|
||
|
// Do not return early, we must call DecrementPendingLoads() at the bottom.
|
||
|
|
||
|
ToolchainRecordMap::iterator found_toolchain = toolchain_records_.find(label);
|
||
|
ToolchainRecord* record = nullptr;
|
||
|
if (found_toolchain == toolchain_records_.end()) {
|
||
|
// When loading the default build config, we'll insert it into the record
|
||
|
// map with an empty label since we don't yet know what to call it.
|
||
|
//
|
||
|
// In this case, we should have exactly one entry in the map with an empty
|
||
|
// label. We now need to fix up the naming so it refers to the "real" one.
|
||
|
CHECK_EQ(1U, toolchain_records_.size());
|
||
|
ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
|
||
|
CHECK(empty_label != toolchain_records_.end());
|
||
|
|
||
|
// Fix up the toolchain record.
|
||
|
std::unique_ptr<ToolchainRecord> moved_record =
|
||
|
std::move(empty_label->second);
|
||
|
record = moved_record.get();
|
||
|
toolchain_records_[label] = std::move(moved_record);
|
||
|
toolchain_records_.erase(empty_label);
|
||
|
|
||
|
// Save the default toolchain label.
|
||
|
default_toolchain_label_ = label;
|
||
|
DCHECK(record->settings.default_toolchain_label().is_null());
|
||
|
record->settings.set_default_toolchain_label(label);
|
||
|
|
||
|
// The settings object should have the toolchain label already set.
|
||
|
DCHECK(!record->settings.toolchain_label().is_null());
|
||
|
|
||
|
// Update any stored invocations that refer to the empty toolchain label.
|
||
|
// This will normally only be one, for the root build file, so brute-force
|
||
|
// is OK.
|
||
|
LoadIDSet old_loads;
|
||
|
invocations_.swap(old_loads);
|
||
|
for (const auto& load : old_loads) {
|
||
|
if (load.toolchain_name.is_null()) {
|
||
|
// Fix up toolchain label
|
||
|
invocations_.insert(LoadID(load.file, label));
|
||
|
} else {
|
||
|
// Can keep the old one.
|
||
|
invocations_.insert(load);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
record = found_toolchain->second.get();
|
||
|
}
|
||
|
|
||
|
DCHECK(!record->is_config_loaded);
|
||
|
DCHECK(record->is_toolchain_loaded);
|
||
|
record->is_config_loaded = true;
|
||
|
|
||
|
// Schedule all waiting file loads.
|
||
|
for (const auto& waiting : record->waiting_on_me)
|
||
|
ScheduleLoadFile(&record->settings, waiting.origin, waiting.file);
|
||
|
record->waiting_on_me.clear();
|
||
|
|
||
|
DecrementPendingLoads();
|
||
|
}
|
||
|
|
||
|
void LoaderImpl::DecrementPendingLoads() {
|
||
|
DCHECK_GT(pending_loads_, 0);
|
||
|
pending_loads_--;
|
||
|
if (pending_loads_ == 0 && !complete_callback_.is_null())
|
||
|
complete_callback_.Run();
|
||
|
}
|
||
|
|
||
|
bool LoaderImpl::AsyncLoadFile(
|
||
|
const LocationRange& origin,
|
||
|
const BuildSettings* build_settings,
|
||
|
const SourceFile& file_name,
|
||
|
const base::Callback<void(const ParseNode*)>& callback,
|
||
|
Err* err) {
|
||
|
if (async_load_file_.is_null()) {
|
||
|
return g_scheduler->input_file_manager()->AsyncLoadFile(
|
||
|
origin, build_settings, file_name, callback, err);
|
||
|
}
|
||
|
return async_load_file_.Run(origin, build_settings, file_name, callback, err);
|
||
|
}
|