diff options
Diffstat (limited to 'tools/gn/toolchain_manager.cc')
-rw-r--r-- | tools/gn/toolchain_manager.cc | 480 |
1 files changed, 0 insertions, 480 deletions
diff --git a/tools/gn/toolchain_manager.cc b/tools/gn/toolchain_manager.cc deleted file mode 100644 index 8a54163..0000000 --- a/tools/gn/toolchain_manager.cc +++ /dev/null @@ -1,480 +0,0 @@ -// 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/toolchain_manager.h" - -#include <set> - -#include "base/bind.h" -#include "tools/gn/err.h" -#include "tools/gn/item.h" -#include "tools/gn/item_node.h" -#include "tools/gn/item_tree.h" -#include "tools/gn/parse_tree.h" -#include "tools/gn/scheduler.h" -#include "tools/gn/scope.h" -#include "tools/gn/scope_per_file_provider.h" - -namespace { - -SourceFile DirToBuildFile(const SourceDir& dir) { - return SourceFile(dir.value() + "BUILD.gn"); -} - -void SetSystemVars(const Settings& settings, Scope* scope) { - // FIXME(brettw) port. - scope->SetValue("is_win", Value(NULL, 1), NULL); - scope->SetValue("is_linux", Value(NULL, 0), NULL); - scope->SetValue("is_posix", Value(NULL, 0), NULL); - scope->SetValue("is_mac", Value(NULL, 0), NULL); - scope->SetValue("is_android", Value(NULL, 0), NULL); - scope->SetValue("is_ios", Value(NULL, 0), NULL); - - // Set this value without the terminating slash because the code expects - // $root_output_dir/foo to work. - scope->SetValue(ScopePerFileProvider::kRootOutputDirName, - ScopePerFileProvider::GetRootOutputDir(&settings), - NULL); - scope->SetValue(ScopePerFileProvider::kRootGenDirName, - ScopePerFileProvider::GetRootGenDir(&settings), - NULL); -} - -} // namespace - -struct ToolchainManager::Info { - Info(const BuildSettings* build_settings, - const Label& toolchain_name, - const std::string& output_subdir_name) - : state(TOOLCHAIN_SETTINGS_NOT_LOADED), - toolchain(toolchain_name), - toolchain_set(false), - settings(build_settings, &toolchain, output_subdir_name), - item_node(NULL) { - } - - // MAkes sure that an ItemNode is created for the toolchain, which lets - // targets depend on the (potentially future) loading of the toolchain. - // - // We can't always do this at the beginning since when doing the default - // build config, we don't know the toolchain name yet. We also need to go - // through some effort to avoid doing this inside the toolchain manager's - // lock (to avoid holding two locks at once). - void EnsureItemNode() { - if (!item_node) { - ItemTree& tree = settings.build_settings()->item_tree(); - item_node = new ItemNode(&toolchain); - tree.AddNodeLocked(item_node); - } - } - - SettingsState state; - - Toolchain toolchain; - bool toolchain_set; - LocationRange toolchain_definition_location; - - // When the state is TOOLCHAIN_SETTINGS_LOADED, the settings should be - // considered read-only and can be read without locking. Otherwise, they - // should not be accessed at all except to load them (which can therefore - // also be done outside of the lock). This works as long as the state flag - // is only ever read or written inside the lock. - Settings settings; - - // While state == TOOLCHAIN_SETTINGS_LOADING, this will collect all - // scheduled invocations using this toolchain. They'll be issued once the - // settings file has been interpreted. - // - // The map maps the source file to "some" location it was invoked from (so - // we can give good error messages. It does NOT map to the root of the - // file to be invoked (the file still needs loading). This will be NULL - // for internally invoked files. - typedef std::map<SourceFile, LocationRange> ScheduledInvocationMap; - ScheduledInvocationMap scheduled_invocations; - - // Tracks all scheduled and executed invocations for this toolchain. This - // is used to avoid invoking a file more than once for a toolchain. - std::set<SourceFile> all_invocations; - - // Filled in by EnsureItemNode, see that for more. - ItemNode* item_node; -}; - -ToolchainManager::ToolchainManager(const BuildSettings* build_settings) - : build_settings_(build_settings) { -} - -ToolchainManager::~ToolchainManager() { - for (ToolchainMap::iterator i = toolchains_.begin(); - i != toolchains_.end(); ++i) - delete i->second; - toolchains_.clear(); -} - -void ToolchainManager::StartLoadingUnlocked(const SourceFile& build_file_name) { - // How the default build config works: Initially we don't have a toolchain - // name to call the settings for the default build config. So we create one - // with an empty toolchain name and execute the default build config file. - // When that's done, we'll go and fix up the name to the default build config - // that the script set. - base::AutoLock lock(GetLock()); - Err err; - Info* info = LoadNewToolchainLocked(LocationRange(), Label(), &err); - if (err.has_error()) - g_scheduler->FailWithError(err); - CHECK(info); - info->scheduled_invocations[build_file_name] = LocationRange(); - info->all_invocations.insert(build_file_name); - - g_scheduler->IncrementWorkCount(); - if (!g_scheduler->input_file_manager()->AsyncLoadFile( - LocationRange(), build_settings_, - build_settings_->build_config_file(), - base::Bind(&ToolchainManager::BackgroundLoadBuildConfig, - base::Unretained(this), info, true), - &err)) { - g_scheduler->FailWithError(err); - g_scheduler->DecrementWorkCount(); - } -} - -const Settings* ToolchainManager::GetSettingsForToolchainLocked( - const LocationRange& from_here, - const Label& toolchain_name, - Err* err) { - GetLock().AssertAcquired(); - ToolchainMap::iterator found = toolchains_.find(toolchain_name); - Info* info = NULL; - if (found == toolchains_.end()) { - info = LoadNewToolchainLocked(from_here, toolchain_name, err); - if (!info) - return NULL; - } else { - info = found->second; - } - info->EnsureItemNode(); - - return &info->settings; -} - -const Toolchain* ToolchainManager::GetToolchainDefinitionUnlocked( - const Label& toolchain_name) { - base::AutoLock lock(GetLock()); - ToolchainMap::iterator found = toolchains_.find(toolchain_name); - if (found == toolchains_.end() || !found->second->toolchain_set) - return NULL; - - // Since we don't allow defining a toolchain more than once, we know that - // once it's set it won't be mutated, so we can safely return this pointer - // for reading outside the lock. - return &found->second->toolchain; -} - -bool ToolchainManager::SetDefaultToolchainUnlocked( - const Label& default_toolchain, - const LocationRange& defined_here, - Err* err) { - base::AutoLock lock(GetLock()); - if (!default_toolchain_.is_null()) { - *err = Err(defined_here, "Default toolchain already set."); - err->AppendSubErr(Err(default_toolchain_defined_here_, - "Previously defined here.", - "You can only set this once.")); - return false; - } - - if (default_toolchain.is_null()) { - *err = Err(defined_here, "Bad default toolchain name.", - "You can't set the default toolchain name to nothing."); - return false; - } - if (!default_toolchain.toolchain_dir().is_null() || - !default_toolchain.toolchain_name().empty()) { - *err = Err(defined_here, "Toolchain name has toolchain.", - "You can't specify a toolchain (inside the parens) for a toolchain " - "name. I got:\n" + default_toolchain.GetUserVisibleName(true)); - return false; - } - - default_toolchain_ = default_toolchain; - default_toolchain_defined_here_ = defined_here; - return true; -} - -Label ToolchainManager::GetDefaultToolchainUnlocked() const { - base::AutoLock lock(GetLock()); - return default_toolchain_; -} - -bool ToolchainManager::SetToolchainDefinitionLocked( - const Toolchain& tc, - const LocationRange& defined_from, - Err* err) { - GetLock().AssertAcquired(); - - ToolchainMap::iterator found = toolchains_.find(tc.label()); - Info* info = NULL; - if (found == toolchains_.end()) { - // New toolchain. - info = LoadNewToolchainLocked(defined_from, tc.label(), err); - if (!info) - return false; - } else { - // It's important to preserve the exact Toolchain object in our tree since - // it will be in the ItemTree and targets may have dependencies on it. - info = found->second; - } - - // The labels should match or else we're setting the wrong one! - CHECK(info->toolchain.label() == tc.label()); - - info->toolchain = tc; - if (info->toolchain_set) { - *err = Err(defined_from, "Duplicate toolchain definition."); - err->AppendSubErr(Err( - info->toolchain_definition_location, - "Previously defined here.", - "A toolchain can only be defined once. One tricky way that this could\n" - "happen is if your definition is itself in a file that's interpreted\n" - "under different toolchains, which would result in multiple\n" - "definitions as the file is loaded multiple times. So be sure your\n" - "toolchain definitions are in files that either don't define any\n" - "targets (probably best) or at least don't contain targets executed\n" - "with more than one toolchain.")); - return false; - } - - info->EnsureItemNode(); - - info->toolchain_set = true; - info->toolchain_definition_location = defined_from; - return true; -} - -bool ToolchainManager::ScheduleInvocationLocked( - const LocationRange& specified_from, - const Label& toolchain_name, - const SourceDir& dir, - Err* err) { - GetLock().AssertAcquired(); - SourceFile build_file(DirToBuildFile(dir)); - - ToolchainMap::iterator found = toolchains_.find(toolchain_name); - Info* info = NULL; - if (found == toolchains_.end()) { - // New toolchain. - info = LoadNewToolchainLocked(specified_from, toolchain_name, err); - if (!info) - return false; - } else { - // Use existing one. - info = found->second; - if (info->all_invocations.find(build_file) != - info->all_invocations.end()) { - // We've already seen this source file for this toolchain, don't need - // to do anything. - return true; - } - } - - info->all_invocations.insert(build_file); - - // True if the settings load needs to be scheduled. - bool info_needs_settings_load = false; - - // True if the settings load has completed. - bool info_settings_loaded = false; - - switch (info->state) { - case TOOLCHAIN_SETTINGS_NOT_LOADED: - info_needs_settings_load = true; - info->scheduled_invocations[build_file] = specified_from; - break; - - case TOOLCHAIN_SETTINGS_LOADING: - info->scheduled_invocations[build_file] = specified_from; - break; - - case TOOLCHAIN_SETTINGS_LOADED: - info_settings_loaded = true; - break; - } - - if (info_needs_settings_load) { - // Load the settings file. - g_scheduler->IncrementWorkCount(); - if (!g_scheduler->input_file_manager()->AsyncLoadFile( - specified_from, build_settings_, - build_settings_->build_config_file(), - base::Bind(&ToolchainManager::BackgroundLoadBuildConfig, - base::Unretained(this), info, false), - err)) { - g_scheduler->DecrementWorkCount(); - return false; - } - } else if (info_settings_loaded) { - // Settings are ready to go, load the target file. - g_scheduler->IncrementWorkCount(); - if (!g_scheduler->input_file_manager()->AsyncLoadFile( - specified_from, build_settings_, build_file, - base::Bind(&ToolchainManager::BackgroundInvoke, - base::Unretained(this), info, build_file), - err)) { - g_scheduler->DecrementWorkCount(); - return false; - } - } - // Else we should have added the infocations to the scheduled_invocations - // from within the lock above. - return true; -} - -// static -std::string ToolchainManager::ToolchainToOutputSubdir( - const Label& toolchain_name) { - // 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_name.name(); -} - -ToolchainManager::Info* ToolchainManager::LoadNewToolchainLocked( - const LocationRange& specified_from, - const Label& toolchain_name, - Err* err) { - GetLock().AssertAcquired(); - Info* info = new Info(build_settings_, - toolchain_name, - ToolchainToOutputSubdir(toolchain_name)); - - toolchains_[toolchain_name] = info; - - // Invoke the file containing the toolchain definition so that it gets - // defined. The default one (label is empty) will be done spearately. - if (!toolchain_name.is_null()) { - // The default toolchain should be specified whenever we're requesting - // another one. This is how we know under what context we should execute - // the invoke for the toolchain file. - CHECK(!default_toolchain_.is_null()); - ScheduleInvocationLocked(specified_from, default_toolchain_, - toolchain_name.dir(), err); - } - return info; -} - -void ToolchainManager::FixupDefaultToolchainLocked() { - // Now that we've run the default build config, we should know the - // default toolchain name. Fix up our reference. - // See Start() for more. - GetLock().AssertAcquired(); - if (default_toolchain_.is_null()) { - g_scheduler->FailWithError(Err(Location(), - "Default toolchain not set.", - "Your build config file \"" + - build_settings_->build_config_file().value() + - "\"\ndid not call set_default_toolchain(). This is needed so " - "I know how to actually\ncompile your code.")); - return; - } - - ToolchainMap::iterator old_default = toolchains_.find(Label()); - CHECK(old_default != toolchains_.end()); - Info* info = old_default->second; - toolchains_[default_toolchain_] = info; - toolchains_.erase(old_default); - - // Toolchain should not have been loaded in the build config file. - CHECK(!info->toolchain_set); - - // We need to set the toolchain label now that we know it. There's no way - // to set the label, but we can assign the toolchain to a new one. Loading - // the build config can not change the toolchain, so we won't be overwriting - // anything useful. - info->toolchain = Toolchain(default_toolchain_); - info->EnsureItemNode(); - - // Schedule a load of the toolchain build file. - Err err; - ScheduleInvocationLocked(LocationRange(), default_toolchain_, - default_toolchain_.dir(), &err); - if (err.has_error()) - g_scheduler->FailWithError(err); -} - -void ToolchainManager::BackgroundLoadBuildConfig(Info* info, - bool is_default, - const ParseNode* root) { - // Danger: No early returns without decrementing the work count. - if (root && !g_scheduler->is_failed()) { - // Nobody should be accessing settings at this point other than us since we - // haven't marked it loaded, so we can do it outside the lock. - Scope* base_config = info->settings.base_config(); - SetSystemVars(info->settings, base_config); - base_config->SetProcessingBuildConfig(); - if (is_default) - base_config->SetProcessingDefaultBuildConfig(); - - const BlockNode* root_block = root->AsBlock(); - Err err; - root_block->ExecuteBlockInScope(base_config, &err); - - base_config->ClearProcessingBuildConfig(); - if (is_default) - base_config->ClearProcessingDefaultBuildConfig(); - - if (err.has_error()) { - g_scheduler->FailWithError(err); - } else { - // Base config processing succeeded. - Info::ScheduledInvocationMap schedule_these; - { - base::AutoLock lock(GetLock()); - schedule_these.swap(info->scheduled_invocations); - info->state = TOOLCHAIN_SETTINGS_LOADED; - if (is_default) - FixupDefaultToolchainLocked(); - } - - // Schedule build files waiting on this settings. There can be many so we - // want to load them in parallel on the pool. - for (Info::ScheduledInvocationMap::iterator i = schedule_these.begin(); - i != schedule_these.end() && !g_scheduler->is_failed(); ++i) { - // Note i->second may be NULL, so don't dereference. - g_scheduler->IncrementWorkCount(); - if (!g_scheduler->input_file_manager()->AsyncLoadFile( - i->second, build_settings_, i->first, - base::Bind(&ToolchainManager::BackgroundInvoke, - base::Unretained(this), info, i->first), - &err)) { - g_scheduler->FailWithError(err); - g_scheduler->DecrementWorkCount(); - break; - } - } - } - } - g_scheduler->DecrementWorkCount(); -} - -void ToolchainManager::BackgroundInvoke(const Info* info, - const SourceFile& file_name, - const ParseNode* root) { - if (root && !g_scheduler->is_failed()) { - if (g_scheduler->verbose_logging()) - g_scheduler->Log("Running", file_name.value()); - - Scope our_scope(info->settings.base_config()); - ScopePerFileProvider per_file_provider(&our_scope, file_name); - - Err err; - root->Execute(&our_scope, &err); - if (err.has_error()) - g_scheduler->FailWithError(err); - } - - g_scheduler->DecrementWorkCount(); -} - -base::Lock& ToolchainManager::GetLock() const { - return build_settings_->item_tree().lock(); -} |