| // 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/target_manager.h" |
| |
| #include <deque> |
| |
| #include "base/bind.h" |
| #include "base/strings/string_piece.h" |
| #include "tools/gn/build_settings.h" |
| #include "tools/gn/err.h" |
| #include "tools/gn/filesystem_utils.h" |
| #include "tools/gn/item_node.h" |
| #include "tools/gn/scheduler.h" |
| #include "tools/gn/toolchain_manager.h" |
| #include "tools/gn/value.h" |
| |
| TargetManager::TargetManager(const BuildSettings* build_settings) |
| : build_settings_(build_settings) { |
| } |
| |
| TargetManager::~TargetManager() { |
| } |
| |
| Target* TargetManager::GetTarget(const Label& label, |
| const LocationRange& specified_from_here, |
| Target* dep_from, |
| Err* err) { |
| DCHECK(!label.is_null()); |
| DCHECK(!label.toolchain_dir().value().empty()); |
| DCHECK(!label.toolchain_name().empty()); |
| |
| base::AutoLock lock(build_settings_->item_tree().lock()); |
| |
| ItemNode* target_node = |
| build_settings_->item_tree().GetExistingNodeLocked(label); |
| Target* target = NULL; |
| if (!target_node) { |
| // First time we've seen this, may need to load the file. |
| |
| // Compute the settings. The common case is that we have a dep_from and |
| // the toolchains match, so we can use the settings from there rather than |
| // querying the toolchain manager (which requires locking, etc.). |
| const Settings* settings; |
| if (dep_from && dep_from->label().ToolchainsEqual(label)) { |
| settings = dep_from->settings(); |
| } else { |
| settings = |
| build_settings_->toolchain_manager().GetSettingsForToolchainLocked( |
| specified_from_here, label.GetToolchainLabel(), err); |
| if (!settings) |
| return NULL; |
| } |
| |
| target = new Target(settings, label); |
| target_node = new ItemNode(target); |
| target_node->set_originally_referenced_from_here(specified_from_here); |
| build_settings_->item_tree().AddNodeLocked(target_node); |
| |
| // We're generating a node when there is no referencing one. |
| if (!dep_from) |
| target_node->set_generated_from_here(specified_from_here); |
| |
| // Only schedule loading the given target if somebody is depending on it |
| // (and we optimize by not re-asking it to run the current file). |
| // Otherwise, we're probably generating it right now. |
| if (dep_from && dep_from->label().dir() != label.dir()) { |
| if (!ScheduleInvocationLocked(specified_from_here, label, err)) |
| return NULL; |
| } |
| } else if ((target = target_node->item()->AsTarget())) { |
| // Previously saw this item as a target. |
| |
| // If we have no dep_from, we're generating it. |
| if (!dep_from) { |
| // In this case, it had better not already be generated. |
| if (target_node->state() != ItemNode::REFERENCED) { |
| *err = Err(specified_from_here, |
| "Duplicate target.", |
| "\"" + label.GetUserVisibleName(true) + |
| "\" being defined here."); |
| err->AppendSubErr(Err(target_node->generated_from_here(), |
| "Originally defined here.")); |
| return NULL; |
| } else { |
| target_node->set_generated_from_here(specified_from_here); |
| } |
| } |
| } else { |
| // Error, we previously saw this thing as a non-target. |
| *err = Err(specified_from_here, "Not previously a target.", |
| "The target being declared here was previously seen referenced as a\n" |
| "non-target (like a config)"); |
| err->AppendSubErr(Err(target_node->originally_referenced_from_here(), |
| "Originally referenced from here.")); |
| return NULL; |
| } |
| |
| // Keep a record of the guy asking us for this dependency. We know if |
| // somebody is adding a dependency, that guy it himself not resolved. |
| if (dep_from && target_node->state() != ItemNode::RESOLVED) { |
| build_settings_->item_tree().GetExistingNodeLocked( |
| dep_from->label())->AddDependency(target_node); |
| } |
| |
| return target; |
| } |
| |
| void TargetManager::TargetGenerationComplete(const Label& label) { |
| base::AutoLock lock(build_settings_->item_tree().lock()); |
| build_settings_->item_tree().MarkItemGeneratedLocked(label); |
| } |
| |
| void TargetManager::GetAllTargets( |
| std::vector<const Target*>* all_targets) const { |
| base::AutoLock lock(build_settings_->item_tree().lock()); |
| |
| std::vector<const Item*> all_items; |
| build_settings_->item_tree().GetAllItemsLocked(&all_items); |
| for (size_t i = 0; i < all_items.size(); i++) { |
| const Target* t = all_items[i]->AsTarget(); |
| if (t) |
| all_targets->push_back(t); |
| } |
| } |
| |
| bool TargetManager::ScheduleInvocationLocked( |
| const LocationRange& specified_from_here, |
| const Label& label, |
| Err* err) { |
| return build_settings_->toolchain_manager().ScheduleInvocationLocked( |
| specified_from_here, label.GetToolchainLabel(), label.dir(), err); |
| } |