Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(21)

Unified Diff: tools/gn/loader.cc

Issue 56433003: GN threading refactor (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/loader.h ('k') | tools/gn/loader_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/loader.cc
diff --git a/tools/gn/loader.cc b/tools/gn/loader.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6a0a32fca7ac441047e12570aba33ebcb570f7c2
--- /dev/null
+++ b/tools/gn/loader.cc
@@ -0,0 +1,398 @@
+// 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 "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.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 {
+
+std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) {
+ // The default toolchain has no subdir.
+ if (is_default)
+ return std::string();
+
+ // For now just assume the toolchain name is always a valid dir name. We may
+ // want to clean up the in the future.
+ return toolchain_label.name();
+}
+
+} // 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() {}
+ 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<SourceFile> waiting_on_me;
+};
+
+// -----------------------------------------------------------------------------
+
+const void* Loader::kDefaultToolchainKey = &kDefaultToolchainKey;
+
+Loader::Loader() {
+}
+
+Loader::~Loader() {
+}
+
+void Loader::Load(const Label& label) {
+ Load(BuildFileForLabel(label), label.GetToolchainLabel());
+}
+
+// static
+SourceFile Loader::BuildFileForLabel(const Label& label) {
+ return SourceFile(label.dir().value() + "BUILD.gn");
+}
+
+// -----------------------------------------------------------------------------
+
+LoaderImpl::LoaderImpl(const BuildSettings* build_settings)
+ : main_loop_(base::MessageLoop::current()),
+ pending_loads_(0),
+ build_settings_(build_settings) {
+}
+
+LoaderImpl::~LoaderImpl() {
+ STLDeleteContainerPairSecondPointers(toolchain_records_.begin(),
+ toolchain_records_.end());
+}
+
+void LoaderImpl::Load(const SourceFile& file,
+ 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 intial load
+ // should not specify a toolchain.
+ DCHECK(toolchain_name.is_null());
+
+ ToolchainRecord* record =
+ new ToolchainRecord(build_settings_, Label(), Label());
+ toolchain_records_[Label()] = 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(file);
+ ScheduleLoadBuildConfig(&record->settings, Scope::KeyValueMap());
+ return;
+ }
+
+ ToolchainRecord* record;
+ if (toolchain_name.is_null())
+ record = toolchain_records_[default_toolchain_label_];
+ else
+ record = toolchain_records_[toolchain_name];
+
+ if (!record) {
+ DCHECK(!default_toolchain_label_.is_null());
+
+ // No reference to this toolchain found yet, make one.
+ record = new ToolchainRecord(build_settings_, toolchain_name,
+ default_toolchain_label_);
+ toolchain_records_[toolchain_name] = record;
+
+ // Schedule a load of the toolchain using the default one.
+ Load(BuildFileForLabel(toolchain_name), default_toolchain_label_);
+ }
+
+ if (record->is_config_loaded)
+ ScheduleLoadFile(&record->settings, file);
+ else
+ record->waiting_on_me.push_back(file);
+}
+
+void LoaderImpl::ToolchainLoaded(const Toolchain* toolchain) {
+ ToolchainRecord* record = toolchain_records_[toolchain->label()];
+ if (!record) {
+ DCHECK(!default_toolchain_label_.is_null());
+ record = new ToolchainRecord(build_settings_, toolchain->label(),
+ default_toolchain_label_);
+ toolchain_records_[toolchain->label()] = 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) {
+ ToolchainRecordMap::iterator found_toolchain;
+ if (label.is_null()) {
+ if (default_toolchain_label_.is_null())
+ return NULL;
+ found_toolchain = toolchain_records_.find(default_toolchain_label_);
+ } else {
+ found_toolchain = toolchain_records_.find(label);
+ }
+
+ if (found_toolchain == toolchain_records_.end())
+ return NULL;
+ return &found_toolchain->second->settings;
+}
+
+void LoaderImpl::ScheduleLoadFile(const Settings* settings,
+ const SourceFile& file) {
+ Err err;
+ pending_loads_++;
+ if (!AsyncLoadFile(LocationRange(), settings->build_settings(), file,
+ base::Bind(&LoaderImpl::BackgroundLoadFile, this,
+ settings, file),
+ &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 ParseNode* root) {
+ if (!root) {
+ main_loop_->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);
+ our_scope.set_source_dir(file_name.GetDir());
+
+ 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())
+ g_scheduler->FailWithError(err);
+
+ trace.Done();
+
+ main_loop_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this));
+}
+
+void LoaderImpl::BackgroundLoadBuildConfig(
+ Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides,
+ const ParseNode* root) {
+ if (!root) {
+ main_loop_->PostTask(FROM_HERE,
+ base::Bind(&LoaderImpl::DecrementPendingLoads, this));
+ return;
+ }
+
+ Scope* base_config = settings->base_config();
+ base_config->set_source_dir(SourceDir("//"));
+
+ 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());
+
+ const BlockNode* root_block = root->AsBlock();
+ Err err;
+ root_block->ExecuteBlockInScope(base_config, &err);
+
+ trace.Done();
+
+ 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);
+ }
+ }
+
+ main_loop_->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 = NULL;
+ 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(toolchain_records_.size() == 1);
+ ToolchainRecordMap::iterator empty_label = toolchain_records_.find(Label());
+ CHECK(empty_label != toolchain_records_.end());
+
+ // Fix up the toolchain record.
+ record = empty_label->second;
+ toolchain_records_[label] = 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 (LoadIDSet::iterator i = old_loads.begin();
+ i != old_loads.end(); ++i) {
+ if (i->toolchain_name.is_null()) {
+ // Fix up toolchain label
+ invocations_.insert(LoadID(i->file, label));
+ } else {
+ // Can keep the old one.
+ invocations_.insert(*i);
+ }
+ }
+ } else {
+ record = found_toolchain->second;
+ }
+
+ DCHECK(!record->is_config_loaded);
+ DCHECK(record->is_toolchain_loaded);
+ record->is_config_loaded = true;
+
+ // Schedule all waiting file loads.
+ for (size_t i = 0; i < record->waiting_on_me.size(); i++)
+ ScheduleLoadFile(&record->settings, record->waiting_on_me[i]);
+ record->waiting_on_me.clear();
+
+ DecrementPendingLoads();
+}
+
+void LoaderImpl::DecrementPendingLoads() {
+ DCHECK(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);
+}
« no previous file with comments | « tools/gn/loader.h ('k') | tools/gn/loader_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698