summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/gn/BUILD.gn19
-rw-r--r--tools/gn/bin/linux/gn.sha12
-rw-r--r--tools/gn/bin/win/gn.exe.sha12
-rw-r--r--tools/gn/binary_target_generator.cc17
-rw-r--r--tools/gn/binary_target_generator.h2
-rw-r--r--tools/gn/build_settings.cc18
-rw-r--r--tools/gn/build_settings.h45
-rw-r--r--tools/gn/builder.cc471
-rw-r--r--tools/gn/builder.h135
-rw-r--r--tools/gn/builder_record.cc69
-rw-r--r--tools/gn/builder_record.h112
-rw-r--r--tools/gn/builder_unittest.cc226
-rw-r--r--tools/gn/command_desc.cc23
-rw-r--r--tools/gn/command_gen.cc30
-rw-r--r--tools/gn/command_gyp.cc89
-rw-r--r--tools/gn/command_refs.cc33
-rw-r--r--tools/gn/commands.cc21
-rw-r--r--tools/gn/config.cc58
-rw-r--r--tools/gn/config.h15
-rw-r--r--tools/gn/config_values_generator.cc11
-rw-r--r--tools/gn/config_values_generator.h6
-rw-r--r--tools/gn/copy_target_generator.cc11
-rw-r--r--tools/gn/copy_target_generator.h2
-rw-r--r--tools/gn/function_set_default_toolchain.cc13
-rw-r--r--tools/gn/function_toolchain.cc19
-rw-r--r--tools/gn/functions.cc22
-rw-r--r--tools/gn/functions_target.cc7
-rw-r--r--tools/gn/gn.gyp17
-rw-r--r--tools/gn/group_target_generator.cc11
-rw-r--r--tools/gn/group_target_generator.h2
-rw-r--r--tools/gn/gyp_binary_target_writer.cc26
-rw-r--r--tools/gn/gyp_target_writer.cc11
-rw-r--r--tools/gn/gyp_target_writer.h9
-rw-r--r--tools/gn/input_file_manager.cc3
-rw-r--r--tools/gn/item.cc3
-rw-r--r--tools/gn/item.h13
-rw-r--r--tools/gn/item_node.cc120
-rw-r--r--tools/gn/item_node.h159
-rw-r--r--tools/gn/item_tree.cc208
-rw-r--r--tools/gn/item_tree.h93
-rw-r--r--tools/gn/label_ptr.h3
-rw-r--r--tools/gn/loader.cc398
-rw-r--r--tools/gn/loader.h177
-rw-r--r--tools/gn/loader_unittest.cc191
-rw-r--r--tools/gn/ninja_target_writer.cc22
-rw-r--r--tools/gn/ninja_target_writer.h2
-rw-r--r--tools/gn/ninja_toolchain_writer.cc26
-rw-r--r--tools/gn/ninja_toolchain_writer.h12
-rw-r--r--tools/gn/ninja_writer.cc66
-rw-r--r--tools/gn/ninja_writer.h14
-rw-r--r--tools/gn/scheduler.cc19
-rw-r--r--tools/gn/scheduler.h7
-rw-r--r--tools/gn/scope.cc21
-rw-r--r--tools/gn/scope.h9
-rw-r--r--tools/gn/scope_per_file_provider.cc5
-rw-r--r--tools/gn/script_target_generator.cc14
-rw-r--r--tools/gn/script_target_generator.h3
-rw-r--r--tools/gn/secondary/base/BUILD.gn3
-rw-r--r--tools/gn/settings.cc4
-rw-r--r--tools/gn/settings.h14
-rw-r--r--tools/gn/setup.cc43
-rw-r--r--tools/gn/setup.h9
-rw-r--r--tools/gn/target.cc28
-rw-r--r--tools/gn/target.h10
-rw-r--r--tools/gn/target_generator.cc127
-rw-r--r--tools/gn/target_generator.h16
-rw-r--r--tools/gn/target_manager.cc129
-rw-r--r--tools/gn/target_manager.h69
-rw-r--r--tools/gn/target_manager_unittest.cc95
-rw-r--r--tools/gn/test_with_scope.cc2
-rw-r--r--tools/gn/toolchain_manager.cc593
-rw-r--r--tools/gn/toolchain_manager.h170
-rw-r--r--tools/gn/trace.cc19
-rw-r--r--tools/gn/trace.h4
74 files changed, 2220 insertions, 2257 deletions
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn
index c5cc1cb..43ca2d1 100644
--- a/tools/gn/BUILD.gn
+++ b/tools/gn/BUILD.gn
@@ -16,6 +16,10 @@ static_library("gn_lib") {
"binary_target_generator.h",
"build_settings.cc",
"build_settings.h",
+ "builder.cc",
+ "builder.h",
+ "builder_record.cc",
+ "builder_record.h",
"command_args.cc",
"command_desc.cc",
"command_gen.cc",
@@ -72,13 +76,11 @@ static_library("gn_lib") {
"input_file_manager.h",
"item.cc",
"item.h",
- "item_node.cc",
- "item_node.h",
- "item_tree.cc",
- "item_tree.h",
"label.cc",
"label.h",
"label_ptr.h",
+ "loader.cc",
+ "loader.h",
"location.cc",
"location.h",
"ninja_binary_target_writer.cc",
@@ -136,16 +138,12 @@ static_library("gn_lib") {
"target.h",
"target_generator.cc",
"target_generator.h",
- "target_manager.cc",
- "target_manager.h",
"token.cc",
"token.h",
"tokenizer.cc",
"tokenizer.h",
"toolchain.cc",
"toolchain.h",
- "toolchain_manager.cc",
- "toolchain_manager.h",
"trace.cc",
"trace.h",
"value.cc",
@@ -159,7 +157,6 @@ static_library("gn_lib") {
deps = [
"//base",
"//base/third_party/dynamic_annotations",
- "//build/util:last_change",
]
}
@@ -170,17 +167,20 @@ executable("gn") {
deps = [
":gn_lib",
+ "//build/util:last_change",
]
}
test("gn_unittests") {
sources = [
+ "builder_unittest.cc",
"escape_unittest.cc",
"file_template_unittest.cc",
"filesystem_utils_unittest.cc",
"function_rebase_path_unittest.cc",
"input_conversion_unittest.cc",
"label_unittest.cc",
+ "loader_unittest.cc",
"ninja_binary_target_writer_unittest.cc",
"ninja_copy_target_writer_unittest.cc",
"ninja_helper_unittest.cc",
@@ -192,7 +192,6 @@ test("gn_unittests") {
"source_dir_unittest.cc",
"string_utils_unittest.cc",
"target_generator_unittest.cc",
- "target_manager_unittest.cc",
"target_unittest.cc",
"test_with_scope.cc",
"test_with_scope.h",
diff --git a/tools/gn/bin/linux/gn.sha1 b/tools/gn/bin/linux/gn.sha1
index af60316..92e7e20 100644
--- a/tools/gn/bin/linux/gn.sha1
+++ b/tools/gn/bin/linux/gn.sha1
@@ -1 +1 @@
-9c71ff30a0781745029d018313783cc814a8c305 \ No newline at end of file
+bca8a009781cf114d357cd73323bddeca8ac33e3
diff --git a/tools/gn/bin/win/gn.exe.sha1 b/tools/gn/bin/win/gn.exe.sha1
index 492ffbc..19440ce 100644
--- a/tools/gn/bin/win/gn.exe.sha1
+++ b/tools/gn/bin/win/gn.exe.sha1
@@ -1 +1 @@
-afd819eef0c7a347b4021ac47b722e04e9ff3193 \ No newline at end of file
+d8b10289afc05f38d0e4581ed093069424d58634 \ No newline at end of file
diff --git a/tools/gn/binary_target_generator.cc b/tools/gn/binary_target_generator.cc
index b8d2d6e..3aec119 100644
--- a/tools/gn/binary_target_generator.cc
+++ b/tools/gn/binary_target_generator.cc
@@ -9,12 +9,13 @@
#include "tools/gn/scope.h"
#include "tools/gn/variables.h"
-BinaryTargetGenerator::BinaryTargetGenerator(Target* target,
- Scope* scope,
- const Token& function_token,
- Target::OutputType type,
- Err* err)
- : TargetGenerator(target, scope, function_token, err),
+BinaryTargetGenerator::BinaryTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Target::OutputType type,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err),
output_type_(type) {
}
@@ -46,12 +47,10 @@ void BinaryTargetGenerator::DoRun() {
// Config values (compiler flags, etc.) set directly on this target.
ConfigValuesGenerator gen(&target_->config_values(), scope_,
- function_token_, scope_->GetSourceDir(), err_);
+ scope_->GetSourceDir(), err_);
gen.Run();
if (err_->has_error())
return;
-
- SetToolchainDependency();
}
void BinaryTargetGenerator::FillOutputName() {
diff --git a/tools/gn/binary_target_generator.h b/tools/gn/binary_target_generator.h
index f0b42ea..d4db096 100644
--- a/tools/gn/binary_target_generator.h
+++ b/tools/gn/binary_target_generator.h
@@ -14,7 +14,7 @@ class BinaryTargetGenerator : public TargetGenerator {
public:
BinaryTargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Target::OutputType type,
Err* err);
virtual ~BinaryTargetGenerator();
diff --git a/tools/gn/build_settings.cc b/tools/gn/build_settings.cc
index 0a75569..7d1bcf0 100644
--- a/tools/gn/build_settings.cc
+++ b/tools/gn/build_settings.cc
@@ -7,27 +7,18 @@
#include "base/file_util.h"
#include "tools/gn/filesystem_utils.h"
-BuildSettings::BuildSettings()
- : using_external_generator_(false),
- item_tree_(),
- target_manager_(this),
- toolchain_manager_(this) {
+BuildSettings::BuildSettings() {
}
BuildSettings::BuildSettings(const BuildSettings& other)
: root_path_(other.root_path_),
root_path_utf8_(other.root_path_utf8_),
secondary_source_path_(other.secondary_source_path_),
- using_external_generator_(other.using_external_generator_),
python_path_(other.python_path_),
build_config_file_(other.build_config_file_),
build_dir_(other.build_dir_),
build_to_source_dir_string_(other.build_to_source_dir_string_),
- build_args_(other.build_args_),
- target_resolved_callback_(), // Don't copy.
- item_tree_(),
- target_manager_(this),
- toolchain_manager_(this) {
+ build_args_(other.build_args_) {
}
BuildSettings::~BuildSettings() {
@@ -66,3 +57,8 @@ base::FilePath BuildSettings::GetFullPathSecondary(
return dir.Resolve(secondary_source_path_);
}
+void BuildSettings::ItemDefined(scoped_ptr<Item> item) const {
+ DCHECK(item);
+ if (!item_defined_callback_.is_null())
+ item_defined_callback_.Run(item.Pass());
+}
diff --git a/tools/gn/build_settings.h b/tools/gn/build_settings.h
index fc175df..2e93296 100644
--- a/tools/gn/build_settings.h
+++ b/tools/gn/build_settings.h
@@ -10,21 +10,20 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
#include "tools/gn/args.h"
-#include "tools/gn/item_tree.h"
#include "tools/gn/scope.h"
#include "tools/gn/source_dir.h"
#include "tools/gn/source_file.h"
-#include "tools/gn/target_manager.h"
-#include "tools/gn/toolchain_manager.h"
+class Item;
class OutputFile;
// Settings for one build, which is one toplevel output directory. There
// may be multiple Settings objects that refer to this, one for each toolchain.
class BuildSettings {
public:
- typedef base::Callback<void(const Target*)> TargetResolvedCallback;
+ typedef base::Callback<void(scoped_ptr<Item>)> ItemDefinedCallback;
BuildSettings();
BuildSettings(const BuildSettings& other);
@@ -44,13 +43,6 @@ class BuildSettings {
}
void SetSecondarySourcePath(const SourceDir& d);
- // Set when we're running an external generator (e.g. GYP) and should
- // enable "external" flags on targets.
- bool using_external_generator() const { return using_external_generator_; }
- void set_using_external_generator(bool ueg) {
- using_external_generator_ = ueg;
- }
-
// Path of the python executable to run scripts with.
base::FilePath python_path() const { return python_path_; }
void set_python_path(const base::FilePath& p) { python_path_ = p; }
@@ -74,16 +66,6 @@ class BuildSettings {
Args& build_args() { return build_args_; }
const Args& build_args() const { return build_args_; }
- // These accessors do not return const objects since the resulting objects
- // are threadsafe. In this setting, we use constness primarily to ensure
- // that the Settings object is used in a threadsafe manner. Although this
- // violates the concept of logical constness, that's less important in our
- // application, and actually implementing this in a way that preserves
- // logical constness is cumbersome.
- ItemTree& item_tree() const { return item_tree_; }
- TargetManager& target_manager() const { return target_manager_; }
- ToolchainManager& toolchain_manager() const { return toolchain_manager_; }
-
// Returns the full absolute OS path cooresponding to the given file in the
// root source tree.
base::FilePath GetFullPath(const SourceFile& file) const;
@@ -95,22 +77,16 @@ class BuildSettings {
base::FilePath GetFullPathSecondary(const SourceFile& file) const;
base::FilePath GetFullPathSecondary(const SourceDir& dir) const;
- // This is the callback to execute when a target is marked resolved. If we
- // don't need to do anything, this will be null. When a target is resolved,
- // this callback should be posted to the scheduler pool so the work is
- // distributed properly.
- const TargetResolvedCallback& target_resolved_callback() const {
- return target_resolved_callback_;
- }
- void set_target_resolved_callback(const TargetResolvedCallback& cb) {
- target_resolved_callback_ = cb;
+ // Called when an item is defined from a background thread.
+ void ItemDefined(scoped_ptr<Item> item) const;
+ void set_item_defined_callback(ItemDefinedCallback cb) {
+ item_defined_callback_ = cb;
}
private:
base::FilePath root_path_;
std::string root_path_utf8_;
base::FilePath secondary_source_path_;
- bool using_external_generator_;
base::FilePath python_path_;
SourceFile build_config_file_;
@@ -118,12 +94,7 @@ class BuildSettings {
std::string build_to_source_dir_string_;
Args build_args_;
- TargetResolvedCallback target_resolved_callback_;
-
- // See getters above.
- mutable ItemTree item_tree_;
- mutable TargetManager target_manager_;
- mutable ToolchainManager toolchain_manager_;
+ ItemDefinedCallback item_defined_callback_;
BuildSettings& operator=(const BuildSettings& other); // Disallow.
};
diff --git a/tools/gn/builder.cc b/tools/gn/builder.cc
new file mode 100644
index 0000000..07a308b
--- /dev/null
+++ b/tools/gn/builder.cc
@@ -0,0 +1,471 @@
+// 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/builder.h"
+
+#include "tools/gn/config.h"
+#include "tools/gn/err.h"
+#include "tools/gn/loader.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/settings.h"
+#include "tools/gn/target.h"
+#include "tools/gn/trace.h"
+
+namespace {
+
+typedef BuilderRecord::BuilderRecordSet BuilderRecordSet;
+
+// Recursively looks in the tree for a given node, returning true if it
+// was found in the dependecy graph. This is used to see if a given node
+// participates in a cycle.
+//
+// If this returns true, the cycle will be in *path. This should point to an
+// empty vector for the first call. During computation, the path will contain
+// the full dependency path to the current node.
+//
+// Return false means no cycle was found.
+bool RecursiveFindCycle(const BuilderRecord* search_in,
+ std::vector<const BuilderRecord*>* path) {
+ path->push_back(search_in);
+
+ const BuilderRecord::BuilderRecordSet& unresolved =
+ search_in->unresolved_deps();
+ for (BuilderRecord::BuilderRecordSet::const_iterator i = unresolved.begin();
+ i != unresolved.end(); ++i) {
+ const BuilderRecord* cur = *i;
+
+ std::vector<const BuilderRecord*>::iterator found =
+ std::find(path->begin(), path->end(), cur);
+ if (found != path->end()) {
+ // This item is already in the set, we found the cycle. Everything before
+ // the first definition of cur is irrelevant to the cycle.
+ path->erase(path->begin(), found);
+ path->push_back(cur);
+ return true;
+ }
+
+ if (RecursiveFindCycle(cur, path))
+ return true; // Found cycle.
+ }
+ path->pop_back();
+ return false;
+}
+
+} // namespace
+
+Builder::Builder(Loader* loader) : loader_(loader) {
+}
+
+Builder::~Builder() {
+}
+
+void Builder::ItemDefined(scoped_ptr<Item> item) {
+ ScopedTrace trace(TraceItem::TRACE_DEFINE_TARGET, item->label());
+ trace.SetToolchain(item->settings()->toolchain_label());
+
+ BuilderRecord::ItemType type = BuilderRecord::TypeOfItem(item.get());
+
+ Err err;
+ BuilderRecord* record =
+ GetOrCreateRecordOfType(item->label(), item->defined_from(), type, &err);
+ if (!record) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ // Check that it's not been already defined.
+ if (record->item()) {
+ err = Err(item->defined_from(), "Duplicate definition.",
+ "The item\n " + item->label().GetUserVisibleName(false) +
+ "\nwas already defined.");
+ err.AppendSubErr(Err(record->item()->defined_from(),
+ "Previous definition:"));
+ g_scheduler->FailWithError(err);
+ return;
+ }
+
+ record->set_item(item.Pass());
+
+ // Do target-specific dependency setup. This will also schedule dependency
+ // loads for targets that are required.
+ switch (type) {
+ case BuilderRecord::ITEM_TARGET:
+ if (!TargetDefined(record, &err)) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+ break;
+ case BuilderRecord::ITEM_TOOLCHAIN:
+ loader_->ToolchainLoaded(record->item()->AsToolchain());
+ break;
+ default:
+ break;
+ }
+
+ if (record->can_resolve()) {
+ if (!ResolveItem(record, &err)) {
+ g_scheduler->FailWithError(err);
+ return;
+ }
+ }
+}
+
+const Item* Builder::GetItem(const Label& label) const {
+ const BuilderRecord* record = GetRecord(label);
+ if (!record)
+ return NULL;
+ return record->item();
+}
+
+const Toolchain* Builder::GetToolchain(const Label& label) const {
+ const BuilderRecord* record = GetRecord(label);
+ if (!record)
+ return NULL;
+ if (!record->item())
+ return NULL;
+ return record->item()->AsToolchain();
+}
+
+std::vector<const BuilderRecord*> Builder::GetAllRecords() const {
+ std::vector<const BuilderRecord*> result;
+ result.reserve(records_.size());
+ for (RecordMap::const_iterator i = records_.begin();
+ i != records_.end(); ++i)
+ result.push_back(i->second);
+ return result;
+}
+
+std::vector<const Target*> Builder::GetAllResolvedTargets() const {
+ std::vector<const Target*> result;
+ result.reserve(records_.size());
+ for (RecordMap::const_iterator i = records_.begin();
+ i != records_.end(); ++i) {
+ if (i->second->type() == BuilderRecord::ITEM_TARGET &&
+ i->second->should_generate() && i->second->item())
+ result.push_back(i->second->item()->AsTarget());
+ }
+ return result;
+}
+
+const BuilderRecord* Builder::GetRecord(const Label& label) const {
+ // Forward to the non-const version.
+ return const_cast<Builder*>(this)->GetRecord(label);
+}
+
+BuilderRecord* Builder::GetRecord(const Label& label) {
+ RecordMap::iterator found = records_.find(label);
+ if (found == records_.end())
+ return NULL;
+ return found->second;
+}
+
+bool Builder::CheckForBadItems(Err* err) const {
+ // Look for errors where we find a defined node with an item that refers to
+ // an undefined one with no item. There may be other nodes in turn depending
+ // on our defined one, but listing those isn't helpful: we want to find the
+ // broken link.
+ //
+ // This finds normal "missing dependency" errors but does not find circular
+ // dependencies because in this case all items in the cycle will be GENERATED
+ // but none will be resolved. If this happens, we'll check explicitly for
+ // that below.
+ std::vector<const BuilderRecord*> bad_records;
+ std::string depstring;
+ for (RecordMap::const_iterator i = records_.begin();
+ i != records_.end(); ++i) {
+ const BuilderRecord* src = i->second;
+ if (!src->should_generate())
+ continue; // Skip ungenerated nodes.
+
+ if (!src->resolved()) {
+ bad_records.push_back(src);
+
+ // Check dependencies.
+ for (BuilderRecord::BuilderRecordSet::const_iterator dest_iter =
+ src->unresolved_deps().begin();
+ dest_iter != src->unresolved_deps().end();
+ ++dest_iter) {
+ const BuilderRecord* dest = *dest_iter;
+ if (!dest->item()) {
+ depstring += src->label().GetUserVisibleName(true) +
+ "\n needs " + dest->label().GetUserVisibleName(true) + "\n";
+ }
+ }
+ }
+ }
+
+ if (!depstring.empty()) {
+ *err = Err(Location(), "Unresolved dependencies.", depstring);
+ return false;
+ }
+
+ if (!bad_records.empty()) {
+ // Our logic above found a bad node but didn't identify the problem. This
+ // normally means a circular dependency.
+ depstring = CheckForCircularDependencies(bad_records);
+ if (depstring.empty()) {
+ // Something's very wrong, just dump out the bad nodes.
+ depstring = "I have no idea what went wrong, but these are unresolved, "
+ "possible due to an\ninternal error:";
+ for (size_t i = 0; i < bad_records.size(); i++) {
+ depstring += "\n\"" +
+ bad_records[i]->label().GetUserVisibleName(false) + "\"";
+ }
+ *err = Err(Location(), "", depstring);
+ } else {
+ *err = Err(Location(), "Dependency cycle:", depstring);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool Builder::TargetDefined(BuilderRecord* record, Err* err) {
+ Target* target = record->item()->AsTarget();
+
+ if (!AddDeps(record, target->deps(), err) ||
+ !AddDeps(record, target->datadeps(), err) ||
+ !AddDeps(record, target->configs(), err) ||
+ !AddDeps(record, target->all_dependent_configs(), err) ||
+ !AddDeps(record, target->direct_dependent_configs(), err) ||
+ !AddToolchainDep(record, target, err))
+ return false;
+
+ // All targets in the default toolchain get generated by default. We also
+ // check if this target was previously marked as "required" and force setting
+ // the bit again so the target's dependencies (which we now know) get the
+ // required bit pushed to them.
+ if (record->should_generate() || target->settings()->is_default())
+ RecursiveSetShouldGenerate(record, true);
+
+ return true;
+}
+
+BuilderRecord* Builder::GetOrCreateRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err) {
+ BuilderRecord* record = GetRecord(label);
+ if (!record) {
+ // Not seen this record yet, create a new one.
+ record = new BuilderRecord(type, label);
+ records_[label] = record;
+ return record;
+ }
+
+ // Check types.
+ if (record->type() != type) {
+ *err = Err(request_from, "Item type does not match.",
+ "The item \"" + label.GetUserVisibleName(false) +
+ "\" was expected\nto be a " +
+ BuilderRecord::GetNameForType(type) +
+ " but was previously\n referenced as a " +
+ BuilderRecord::GetNameForType(record->type()));
+ err->AppendSubErr(Err(record->originally_referenced_from(),
+ "The previous reference was here."));
+ return NULL;
+ }
+
+ return record;
+}
+
+BuilderRecord* Builder::GetResolvedRecordOfType(const Label& label,
+ const ParseNode* origin,
+ BuilderRecord::ItemType type,
+ Err* err) {
+ BuilderRecord* record = GetRecord(label);
+ if (!record) {
+ *err = Err(origin, "Item not found",
+ "\"" + label.GetUserVisibleName(false) + "\" doesn't\n"
+ "refer to an existant thing.");
+ return NULL;
+ }
+
+ const Item* item = record->item();
+ if (!item) {
+ *err = Err(origin, "Item not resolved.",
+ "\"" + label.GetUserVisibleName(false) + "\" hasn't been resolved.\n");
+ return NULL;
+ }
+
+ if (!BuilderRecord::IsItemOfType(item, type)) {
+ *err = Err(origin,
+ std::string("This is not a ") + BuilderRecord::GetNameForType(type),
+ "\"" + label.GetUserVisibleName(false) + "\" refers to a " +
+ item->GetItemTypeName() + " instead of a " +
+ BuilderRecord::GetNameForType(type) + ".");
+ return NULL;
+ }
+ return record;
+}
+
+bool Builder::AddDeps(BuilderRecord* record,
+ const LabelConfigVector& configs,
+ Err* err) {
+ for (size_t i = 0; i < configs.size(); i++) {
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ configs[i].label, configs[i].origin, BuilderRecord::ITEM_CONFIG, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+ return true;
+}
+
+bool Builder::AddDeps(BuilderRecord* record,
+ const LabelTargetVector& targets,
+ Err* err) {
+ for (size_t i = 0; i < targets.size(); i++) {
+ BuilderRecord* dep_record = GetOrCreateRecordOfType(
+ targets[i].label, targets[i].origin, BuilderRecord::ITEM_TARGET, err);
+ if (!dep_record)
+ return false;
+ record->AddDep(dep_record);
+ }
+ return true;
+}
+
+bool Builder::AddToolchainDep(BuilderRecord* record,
+ const Target* target,
+ Err* err) {
+ BuilderRecord* toolchain_record = GetOrCreateRecordOfType(
+ target->settings()->toolchain_label(), target->defined_from(),
+ BuilderRecord::ITEM_TOOLCHAIN, err);
+ if (!toolchain_record)
+ return false;
+ record->AddDep(toolchain_record);
+
+ return true;
+}
+
+void Builder::RecursiveSetShouldGenerate(BuilderRecord* record,
+ bool force) {
+ if (!force && record->should_generate())
+ return; // Already set.
+ record->set_should_generate(true);
+
+ const BuilderRecordSet& deps = record->all_deps();
+ for (BuilderRecordSet::const_iterator i = deps.begin();
+ i != deps.end(); i++) {
+ BuilderRecord* cur = *i;
+ if (!cur->should_generate()) {
+ ScheduleItemLoadIfNecessary(cur);
+ RecursiveSetShouldGenerate(cur, false);
+ }
+ }
+}
+
+void Builder::ScheduleItemLoadIfNecessary(BuilderRecord* record) {
+ loader_->Load(record->label());
+}
+
+bool Builder::ResolveItem(BuilderRecord* record, Err* err) {
+ DCHECK(record->can_resolve() && !record->resolved());
+
+ if (record->type() == BuilderRecord::ITEM_TARGET) {
+ Target* target = record->item()->AsTarget();
+ if (!ResolveDeps(&target->deps(), err) ||
+ !ResolveDeps(&target->datadeps(), err) ||
+ !ResolveConfigs(&target->configs(), err) ||
+ !ResolveConfigs(&target->all_dependent_configs(), err) ||
+ !ResolveConfigs(&target->direct_dependent_configs(), err) ||
+ !ResolveForwardDependentConfigs(target, err))
+ return false;
+ }
+
+ record->set_resolved(true);
+ record->item()->OnResolved();
+ if (!resolved_callback_.is_null())
+ resolved_callback_.Run(record->item());
+
+ // Recursively update everybody waiting on this item to be resolved.
+ BuilderRecordSet& waiting_set = record->waiting_on_resolution();
+ for (BuilderRecordSet::iterator i = waiting_set.begin();
+ i != waiting_set.end(); ++i) {
+ BuilderRecord* waiting = *i;
+ DCHECK(waiting->unresolved_deps().find(record) !=
+ waiting->unresolved_deps().end());
+ waiting->unresolved_deps().erase(record);
+
+ if (waiting->can_resolve()) {
+ if (!ResolveItem(waiting, err))
+ return false;
+ }
+ }
+ waiting_set.clear();
+ return true;
+}
+
+bool Builder::ResolveDeps(LabelTargetVector* deps, Err* err) {
+ for (size_t i = 0; i < deps->size(); i++) {
+ LabelTargetPair& cur = (*deps)[i];
+ DCHECK(!cur.ptr);
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ cur.label, cur.origin, BuilderRecord::ITEM_TARGET, err);
+ if (!record)
+ return false;
+ cur.ptr = record->item()->AsTarget();
+ }
+ return true;
+}
+
+bool Builder::ResolveConfigs(LabelConfigVector* configs, Err* err) {
+ for (size_t i = 0; i < configs->size(); i++) {
+ LabelConfigPair& cur = (*configs)[i];
+ DCHECK(!cur.ptr);
+
+ BuilderRecord* record = GetResolvedRecordOfType(
+ cur.label, cur.origin, BuilderRecord::ITEM_CONFIG, err);
+ if (!record)
+ return false;
+ cur.ptr = record->item()->AsConfig();
+ }
+ return true;
+}
+
+// "Forward dependent configs" should refer to targets in the deps that should
+// have their configs forwarded.
+bool Builder::ResolveForwardDependentConfigs(Target* target, Err* err) {
+ const LabelTargetVector& deps = target->deps();
+ LabelTargetVector& configs = target->forward_dependent_configs();
+
+ // Assume that the lists are small so that brute-force n^2 is appropriate.
+ for (size_t config_i = 0; config_i < configs.size(); config_i++) {
+ for (size_t dep_i = 0; dep_i < deps.size(); dep_i++) {
+ if (configs[config_i].label == deps[dep_i].label) {
+ DCHECK(deps[dep_i].ptr); // Should already be resolved.
+ configs[config_i].ptr = deps[dep_i].ptr;
+ break;
+ }
+ }
+ if (!configs[config_i].ptr) {
+ *err = Err(target->defined_from(),
+ "Target in forward_dependent_configs_from was not listed in the deps",
+ "The target \"" + configs[config_i].label.GetUserVisibleName(false) +
+ "\"\n was not present in the deps. This thing is used to forward\n"
+ "configs from direct dependents.");
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string Builder::CheckForCircularDependencies(
+ const std::vector<const BuilderRecord*>& bad_records) const {
+ std::vector<const BuilderRecord*> cycle;
+ if (!RecursiveFindCycle(bad_records[0], &cycle))
+ return std::string(); // Didn't find a cycle, something else is wrong.
+
+ // Walk backwards since the dependency arrows point in the reverse direction.
+ std::string ret;
+ for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) {
+ ret += " " + cycle[i]->label().GetUserVisibleName(false);
+ if (i != 0)
+ ret += " ->\n";
+ }
+
+ return ret;
+}
diff --git a/tools/gn/builder.h b/tools/gn/builder.h
new file mode 100644
index 0000000..994c8e1
--- /dev/null
+++ b/tools/gn/builder.h
@@ -0,0 +1,135 @@
+// 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.
+
+#ifndef TOOLS_GN_BUILDER_H_
+#define TOOLS_GN_BUILDER_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "tools/gn/builder_record.h"
+#include "tools/gn/label.h"
+#include "tools/gn/label_ptr.h"
+
+class Config;
+class Err;
+class Loader;
+class ParseNode;
+
+class Builder : public base::RefCountedThreadSafe<Builder> {
+ public:
+ typedef base::Callback<void(const Item*)> ResolvedCallback;
+
+ Builder(Loader* loader);
+
+ // The resolved callback is called whenever a target has been resolved. This
+ // will be executed only on the main thread.
+ void set_resolved_callback(const ResolvedCallback& cb) {
+ resolved_callback_ = cb;
+ }
+
+ Loader* loader() const { return loader_; }
+
+ void ItemDefined(scoped_ptr<Item> item);
+
+ // Returns NULL if there is not a thing with the corresponding label.
+ const Item* GetItem(const Label& label) const;
+ const Toolchain* GetToolchain(const Label& label) const;
+
+ std::vector<const BuilderRecord*> GetAllRecords() const;
+
+ // Returns targets which should be generated and which are defined.
+ std::vector<const Target*> GetAllResolvedTargets() const;
+
+ // Returns the record for the given label, or NULL if it doesn't exist.
+ // Mostly used for unit tests.
+ const BuilderRecord* GetRecord(const Label& label) const;
+ BuilderRecord* GetRecord(const Label& label);
+
+ // If there are any undefined references, returns false and sets the error.
+ bool CheckForBadItems(Err* err) const;
+
+ private:
+ friend class base::RefCountedThreadSafe<Builder>;
+
+ virtual ~Builder();
+
+ bool TargetDefined(BuilderRecord* record, Err* err);
+
+ // Returns the record associated with the given label. This function checks
+ // that if we already have references for it, the type matches. If no record
+ // exists yet, a new one will be created.
+ //
+ // If any of the conditions fail, the return value will be null and the error
+ // will be set. request_from is used as the source of the error.
+ BuilderRecord* GetOrCreateRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err);
+
+ // Returns the record associated with the given label. This function checks
+ // that it's already been resolved to the correct type.
+ //
+ // If any of the conditions fail, the return value will be null and the error
+ // will be set. request_from is used as the source of the error.
+ BuilderRecord* GetResolvedRecordOfType(const Label& label,
+ const ParseNode* request_from,
+ BuilderRecord::ItemType type,
+ Err* err);
+
+ bool AddDeps(BuilderRecord* record,
+ const LabelConfigVector& configs,
+ Err* err);
+ bool AddDeps(BuilderRecord* record,
+ const LabelTargetVector& targets,
+ Err* err);
+ bool AddToolchainDep(BuilderRecord* record,
+ const Target* target,
+ Err* err);
+
+ // Given a target, sets the "should generate" bit and pushes it through the
+ // dependency tree. Any time the bit it set, we ensure that the given item is
+ // scheduled to be loaded.
+ //
+ // If the force flag is set, we'll ignore the current state of the record's
+ // should_generate flag, and set it on the dependents every time. This is
+ // used when defining a target: the "should generate" may have been set
+ // before the item was defined (if it is required by something that is
+ // required). In this case, we need to re-push the "should generate" flag
+ // to the item's dependencies.
+ void RecursiveSetShouldGenerate(BuilderRecord* record, bool force);
+
+ void ScheduleItemLoadIfNecessary(BuilderRecord* record);
+
+ // This takes a BuilderRecord with resolved depdencies, and fills in the
+ // target's Label*Vectors with the resolved pointers.
+ bool ResolveItem(BuilderRecord* record, Err* err);
+
+ // Fills in the pointers in the given vector based on the labels. We assume
+ // that everything should be resolved by this point, so will return an error
+ // if anything isn't found or if the type doesn't match.
+ bool ResolveDeps(LabelTargetVector* deps, Err* err);
+ bool ResolveConfigs(LabelConfigVector* configs, Err* err);
+ bool ResolveForwardDependentConfigs(Target* target, Err* err);
+
+ // Given a list of unresolved records, tries to find any circular
+ // dependencies and returns the string describing the problem. If no circular
+ // deps were found, returns the empty string.
+ std::string CheckForCircularDependencies(
+ const std::vector<const BuilderRecord*>& bad_records) const;
+
+ // Non owning pointer.
+ Loader* loader_;
+
+ // Owning pointers.
+ typedef base::hash_map<Label, BuilderRecord*> RecordMap;
+ RecordMap records_;
+
+ ResolvedCallback resolved_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(Builder);
+};
+
+#endif // TOOLS_GN_BUILDER_H_
diff --git a/tools/gn/builder_record.cc b/tools/gn/builder_record.cc
new file mode 100644
index 0000000..066b1ea
--- /dev/null
+++ b/tools/gn/builder_record.cc
@@ -0,0 +1,69 @@
+// 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/builder_record.h"
+
+#include "tools/gn/item.h"
+
+BuilderRecord::BuilderRecord(ItemType type, const Label& label)
+ : type_(type),
+ label_(label),
+ originally_referenced_from_(NULL),
+ should_generate_(false),
+ resolved_(false) {
+}
+
+BuilderRecord::~BuilderRecord() {
+}
+
+// static
+const char* BuilderRecord::GetNameForType(ItemType type) {
+ switch (type) {
+ case ITEM_TARGET:
+ return "target";
+ case ITEM_CONFIG:
+ return "config";
+ case ITEM_TOOLCHAIN:
+ return "toolchain";
+ case ITEM_UNKNOWN:
+ default:
+ return "unknown";
+ }
+}
+
+// static
+bool BuilderRecord::IsItemOfType(const Item* item, ItemType type) {
+ switch (type) {
+ case ITEM_TARGET:
+ return !!item->AsTarget();
+ case ITEM_CONFIG:
+ return !!item->AsConfig();
+ case ITEM_TOOLCHAIN:
+ return !!item->AsToolchain();
+ case ITEM_UNKNOWN:
+ default:
+ return false;
+ }
+}
+
+// static
+BuilderRecord::ItemType BuilderRecord::TypeOfItem(const Item* item) {
+ if (item->AsTarget())
+ return ITEM_TARGET;
+ if (item->AsConfig())
+ return ITEM_CONFIG;
+ if (item->AsToolchain())
+ return ITEM_TOOLCHAIN;
+
+ NOTREACHED();
+ return ITEM_UNKNOWN;
+}
+
+void BuilderRecord::AddDep(BuilderRecord* record) {
+ all_deps_.insert(record);
+ if (!record->resolved()) {
+ unresolved_deps_.insert(record);
+ record->waiting_on_resolution_.insert(this);
+ }
+}
diff --git a/tools/gn/builder_record.h b/tools/gn/builder_record.h
new file mode 100644
index 0000000..6e7e9bb
--- /dev/null
+++ b/tools/gn/builder_record.h
@@ -0,0 +1,112 @@
+// 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.
+
+#ifndef TOOLS_GN_BUILDER_RECORD_H_
+#define TOOLS_GN_BUILDER_RECORD_H_
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "tools/gn/item.h"
+#include "tools/gn/location.h"
+
+class ParseNode;
+
+// This class is used by the builder to manage the loading of the dependency
+// tree. It holds a reference to an item and links to other records that the
+// item depends on, both resolved ones, and unresolved ones.
+//
+// If a target depends on another one that hasn't been defined yet, we'll make
+// a placeholder BuilderRecord with no item, and try to load the buildfile
+// associated with the new item. The item will get filled in when we encounter
+// the declaration for the item (or when we're done and realize there are
+// undefined items).
+//
+// You can also have null item pointers when the target is not required for
+// the current build (should_generate is false).
+class BuilderRecord {
+ public:
+ typedef std::set<BuilderRecord*> BuilderRecordSet;
+
+ enum ItemType {
+ ITEM_UNKNOWN,
+ ITEM_TARGET,
+ ITEM_CONFIG,
+ ITEM_TOOLCHAIN
+ };
+
+ //BuilderRecord();
+ BuilderRecord(ItemType type, const Label& label);
+ ~BuilderRecord();
+
+ ItemType type() const { return type_; }
+ const Label& label() const { return label_; }
+
+ // Returns a user-ready name for the given type. e.g. "target".
+ static const char* GetNameForType(ItemType type);
+
+ // Returns true if the given item is of the given type.
+ static bool IsItemOfType(const Item* item, ItemType type);
+
+ // Returns the type enum for the given item.
+ static ItemType TypeOfItem(const Item* item);
+
+ Item* item() { return item_.get(); }
+ const Item* item() const { return item_.get(); }
+ void set_item(scoped_ptr<Item> item) { item_ = item.Pass(); }
+
+ // Indicates from where this item was originally referenced from that caused
+ // it to be loaded. For targets for which we encountered the declaration
+ // before a reference, this will be the empty range.
+ const ParseNode* originally_referenced_from() const {
+ return originally_referenced_from_;
+ }
+ void set_originally_referenced_from(const ParseNode* pn) {
+ originally_referenced_from_ = pn;
+ }
+
+ bool should_generate() const { return should_generate_; }
+ void set_should_generate(bool sg) { should_generate_ = sg; }
+
+ bool resolved() const { return resolved_; }
+ void set_resolved(bool r) { resolved_ = r; }
+
+ bool can_resolve() const {
+ return item_ && unresolved_deps_.empty();
+ }
+
+ // All records this one is depending on.
+ BuilderRecordSet& all_deps() { return all_deps_; }
+ const BuilderRecordSet& all_deps() const { return all_deps_; }
+
+ // Unresolved records this one is depending on. A subset of all... above.
+ BuilderRecordSet& unresolved_deps() { return unresolved_deps_; }
+ const BuilderRecordSet& unresolved_deps() const { return unresolved_deps_; }
+
+ // Records that are waiting on this one to be resolved. This is the other
+ // end of the "unresolved deps" arrow.
+ BuilderRecordSet& waiting_on_resolution() { return waiting_on_resolution_; }
+ const BuilderRecordSet& waiting_on_resolution() const {
+ return waiting_on_resolution_;
+ }
+
+ void AddDep(BuilderRecord* record);
+
+ private:
+ ItemType type_;
+ Label label_;
+ scoped_ptr<Item> item_;
+ const ParseNode* originally_referenced_from_;
+ bool should_generate_;
+ bool resolved_;
+
+ BuilderRecordSet all_deps_;
+ BuilderRecordSet unresolved_deps_;
+ BuilderRecordSet waiting_on_resolution_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuilderRecord);
+};
+
+#endif // TOOLS_GN_BUILDER_RECORD_H_
diff --git a/tools/gn/builder_unittest.cc b/tools/gn/builder_unittest.cc
new file mode 100644
index 0000000..538d5f5
--- /dev/null
+++ b/tools/gn/builder_unittest.cc
@@ -0,0 +1,226 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/loader.h"
+#include "tools/gn/target.h"
+#include "tools/gn/test_with_scope.h"
+#include "tools/gn/toolchain.h"
+
+namespace {
+
+class MockLoader : public Loader {
+ public:
+ MockLoader() {
+ }
+
+ // Loader implementation:
+ virtual void Load(const SourceFile& file,
+ const Label& toolchain_name) OVERRIDE {
+ files_.push_back(file);
+ }
+ virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE {
+ }
+ virtual Label GetDefaultToolchain() const OVERRIDE {
+ return Label();
+ }
+ virtual const Settings* GetToolchainSettings(const Label& label) OVERRIDE {
+ return NULL;
+ }
+
+ bool HasLoadedNone() const {
+ return files_.empty();
+ }
+
+ // Returns true if one load has been requested and it matches the given
+ // file. This will clear the records so it will be empty for the next call.
+ bool HasLoadedOne(const SourceFile& f) {
+ if (files_.size() != 1u) {
+ files_.clear();
+ return false;
+ }
+
+ bool match = (files_[0] == f);
+ files_.clear();
+ return match;
+ }
+
+ // Like HasLoadedOne above. Accepts any ordering.
+ bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
+ if (files_.size() != 2u) {
+ files_.clear();
+ return false;
+ }
+
+ bool match = (
+ (files_[0] == a && files_[1] == b) ||
+ (files_[0] == b && files_[0] == a));
+ files_.clear();
+ return match;
+ }
+
+ private:
+ virtual ~MockLoader() {}
+
+ std::vector<SourceFile> files_;
+};
+
+class BuilderTest : public testing::Test {
+ public:
+ BuilderTest()
+ : loader_(new MockLoader),
+ builder_(new Builder(loader_.get())),
+ settings_(&build_settings_, std::string()),
+ scope_(&settings_) {
+ build_settings_.SetBuildDir(SourceDir("//out/"));
+ settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
+ settings_.set_default_toolchain_label(settings_.toolchain_label());
+ }
+
+ Toolchain* DefineToolchain() {
+ Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
+ builder_->ItemDefined(scoped_ptr<Item>(tc));
+ return tc;
+ }
+
+ protected:
+ scoped_refptr<MockLoader> loader_;
+ scoped_refptr<Builder> builder_;
+ BuildSettings build_settings_;
+ Settings settings_;
+ Scope scope_;
+};
+
+} // namespace
+
+TEST_F(BuilderTest, BasicDeps) {
+ SourceDir toolchain_dir = settings_.toolchain_label().dir();
+ std::string toolchain_name = settings_.toolchain_label().name();
+
+ DefineToolchain();
+ BuilderRecord* toolchain_record =
+ builder_->GetRecord(settings_.toolchain_label());
+ ASSERT_TRUE(toolchain_record);
+ EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
+
+ // Construct a dependency chain: A -> B -> C. Define A first with a
+ // forward-reference to B, then C, then B to test the different orders that
+ // the dependencies are hooked up.
+ Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
+ Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
+ Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
+
+ // The builder will take ownership of the pointers.
+ Target* a = new Target(&settings_, a_label);
+ a->deps().push_back(LabelTargetPair(b_label));
+ a->set_output_type(Target::EXECUTABLE);
+ builder_->ItemDefined(scoped_ptr<Item>(a));
+
+ // Should have requested that B and the toolchain is loaded.
+ EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
+ SourceFile("//b/BUILD.gn")));
+
+ // A should be unresolved with an item
+ BuilderRecord* a_record = builder_->GetRecord(a_label);
+ EXPECT_TRUE(a_record->item());
+ EXPECT_FALSE(a_record->resolved());
+ EXPECT_FALSE(a_record->can_resolve());
+
+ // B should be unresolved, have no item, and no deps.
+ BuilderRecord* b_record = builder_->GetRecord(b_label);
+ EXPECT_FALSE(b_record->item());
+ EXPECT_FALSE(b_record->resolved());
+ EXPECT_FALSE(b_record->can_resolve());
+ EXPECT_TRUE(b_record->all_deps().empty());
+
+ // A should have two deps: B and the toolchain. Only B should be unresolved.
+ EXPECT_EQ(2u, a_record->all_deps().size());
+ EXPECT_EQ(1u, a_record->unresolved_deps().size());
+ EXPECT_NE(a_record->all_deps().end(),
+ a_record->all_deps().find(toolchain_record));
+ EXPECT_NE(a_record->all_deps().end(),
+ a_record->all_deps().find(b_record));
+ EXPECT_NE(a_record->unresolved_deps().end(),
+ a_record->unresolved_deps().find(b_record));
+
+ // B should be marked as having A waiting on it.
+ EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
+ EXPECT_NE(b_record->waiting_on_resolution().end(),
+ b_record->waiting_on_resolution().find(a_record));
+
+ // Add the C target.
+ Target* c = new Target(&settings_, c_label);
+ c->set_output_type(Target::STATIC_LIBRARY);
+ builder_->ItemDefined(scoped_ptr<Item>(c));
+
+ // C only depends on the already-loaded toolchain so we shouldn't have
+ // requested anything else.
+ EXPECT_TRUE(loader_->HasLoadedNone());
+
+ // Add the B target.
+ Target* b = new Target(&settings_, b_label);
+ a->deps().push_back(LabelTargetPair(c_label));
+ b->set_output_type(Target::SHARED_LIBRARY);
+ builder_->ItemDefined(scoped_ptr<Item>(b));
+
+ // B depends only on the already-loaded C and toolchain so we shouldn't have
+ // requested anything else.
+ EXPECT_TRUE(loader_->HasLoadedNone());
+
+ // All targets should now be resolved.
+ BuilderRecord* c_record = builder_->GetRecord(c_label);
+ EXPECT_TRUE(a_record->resolved());
+ EXPECT_TRUE(b_record->resolved());
+ EXPECT_TRUE(c_record->resolved());
+
+ EXPECT_TRUE(a_record->unresolved_deps().empty());
+ EXPECT_TRUE(b_record->unresolved_deps().empty());
+ EXPECT_TRUE(c_record->unresolved_deps().empty());
+
+ EXPECT_TRUE(a_record->waiting_on_resolution().empty());
+ EXPECT_TRUE(b_record->waiting_on_resolution().empty());
+ EXPECT_TRUE(c_record->waiting_on_resolution().empty());
+}
+
+// Tests that the should generate bit is set and propogated properly.
+TEST_F(BuilderTest, ShouldGenerate) {
+ DefineToolchain();
+
+ // Define a secondary toolchain.
+ Settings settings2(&build_settings_, "secondary");
+ Label toolchain_label2(SourceDir("//tc/"), "secondary");
+ settings2.set_toolchain_label(toolchain_label2);
+ Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
+ builder_->ItemDefined(scoped_ptr<Item>(tc2));
+
+ // Construct a dependency chain: A -> B. A is in the default toolchain, B
+ // is not.
+ Label a_label(SourceDir("//foo/"), "a",
+ settings_.toolchain_label().dir(), "a");
+ Label b_label(SourceDir("//foo/"), "b",
+ toolchain_label2.dir(), toolchain_label2.name());
+
+ // First define B.
+ Target* b = new Target(&settings2, b_label);
+ b->set_output_type(Target::EXECUTABLE);
+ builder_->ItemDefined(scoped_ptr<Item>(b));
+
+ // B should not be marked generated by default.
+ BuilderRecord* b_record = builder_->GetRecord(b_label);
+ EXPECT_FALSE(b_record->should_generate());
+
+ // Define A with a dependency on B.
+ Target* a = new Target(&settings_, a_label);
+ a->deps().push_back(LabelTargetPair(b_label));
+ a->set_output_type(Target::EXECUTABLE);
+ builder_->ItemDefined(scoped_ptr<Item>(a));
+
+ // A should have the generate bit set since it's in the default toolchain.
+ BuilderRecord* a_record = builder_->GetRecord(a_label);
+ EXPECT_TRUE(a_record->should_generate());
+
+ // It should have gotten pushed to B.
+ EXPECT_TRUE(b_record->should_generate());
+}
diff --git a/tools/gn/command_desc.cc b/tools/gn/command_desc.cc
index 415666c..f680da3 100644
--- a/tools/gn/command_desc.cc
+++ b/tools/gn/command_desc.cc
@@ -10,8 +10,8 @@
#include "tools/gn/commands.h"
#include "tools/gn/config.h"
#include "tools/gn/config_values_extractors.h"
+#include "tools/gn/filesystem_utils.h"
#include "tools/gn/item.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/label.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
@@ -21,6 +21,23 @@ namespace commands {
namespace {
+// Prints the given directory in a nice way for the user to view.
+std::string FormatSourceDir(const SourceDir& dir) {
+#if defined(OS_WIN)
+ // On Windows we fix up system absolute paths to look like native ones.
+ // Internally, they'll look like "/C:\foo\bar/"
+ if (dir.is_system_absolute()) {
+ std::string buf = dir.value();
+ if (buf.size() > 3 && buf[2] == ':') {
+ buf.erase(buf.begin()); // Erase beginning slash.
+ ConvertPathToSystem(&buf); // Convert to backslashes.
+ return buf;
+ }
+ }
+#endif
+ return dir.value();
+}
+
void RecursiveCollectChildDeps(const Target* target, std::set<Label>* result);
void RecursiveCollectDeps(const Target* target, std::set<Label>* result) {
@@ -114,7 +131,7 @@ void PrintLibDirs(const Target* target, bool display_header) {
OutputString("\nlib_dirs\n");
for (size_t i = 0; i < lib_dirs.size(); i++)
- OutputString(" " + lib_dirs[i].value() + "\n");
+ OutputString(" " + FormatSourceDir(lib_dirs[i]) + "\n");
}
void PrintLibs(const Target* target, bool display_header) {
@@ -176,7 +193,7 @@ template<> struct DescValueWriter<SourceFile> {
};
template<> struct DescValueWriter<SourceDir> {
void operator()(const SourceDir& dir, std::ostream& out) const {
- out << " " << dir.value() << "\n";
+ out << " " << FormatSourceDir(dir) << "\n";
}
};
diff --git a/tools/gn/command_gen.cc b/tools/gn/command_gen.cc
index 9f6d38f..cde677a 100644
--- a/tools/gn/command_gen.cc
+++ b/tools/gn/command_gen.cc
@@ -22,10 +22,26 @@ namespace {
// Suppress output on success.
const char kSwitchQuiet[] = "q";
-void TargetResolvedCallback(base::subtle::Atomic32* write_counter,
- const Target* target) {
+void BackgroundDoWrite(const Target* target, const Toolchain* toolchain) {
+ NinjaTargetWriter::RunAndWriteFile(target, toolchain);
+ g_scheduler->DecrementWorkCount();
+}
+
+// Called on the main thread.
+void ItemResolvedCallback(base::subtle::Atomic32* write_counter,
+ scoped_refptr<Builder> builder,
+ const Item* item) {
base::subtle::NoBarrier_AtomicIncrement(write_counter, 1);
- NinjaTargetWriter::RunAndWriteFile(target);
+
+ const Target* target = item->AsTarget();
+ if (target) {
+ const Toolchain* toolchain =
+ builder->GetToolchain(target->settings()->toolchain_label());
+ DCHECK(toolchain);
+ g_scheduler->IncrementWorkCount();
+ g_scheduler->ScheduleWork(
+ base::Bind(&BackgroundDoWrite, target, toolchain));
+ }
}
} // namespace
@@ -51,15 +67,17 @@ int RunGen(const std::vector<std::string>& args) {
// Cause the load to also generate the ninja files for each target. We wrap
// the writing to maintain a counter.
base::subtle::Atomic32 write_counter = 0;
- setup->build_settings().set_target_resolved_callback(
- base::Bind(&TargetResolvedCallback, &write_counter));
+ setup->builder()->set_resolved_callback(
+ base::Bind(&ItemResolvedCallback, &write_counter,
+ scoped_refptr<Builder>(setup->builder())));
// Do the actual load. This will also write out the target ninja files.
if (!setup->Run())
return 1;
// Write the root ninja files.
- if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings()))
+ if (!NinjaWriter::RunAndWriteFiles(&setup->build_settings(),
+ setup->builder()))
return 1;
base::TimeTicks end_time = base::TimeTicks::Now();
diff --git a/tools/gn/command_gyp.cc b/tools/gn/command_gyp.cc
index 0aff1a9..06e5bde 100644
--- a/tools/gn/command_gyp.cc
+++ b/tools/gn/command_gyp.cc
@@ -16,7 +16,6 @@
#include "tools/gn/err.h"
#include "tools/gn/gyp_helper.h"
#include "tools/gn/gyp_target_writer.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/location.h"
#include "tools/gn/setup.h"
#include "tools/gn/source_file.h"
@@ -32,17 +31,31 @@ typedef std::map<Label, TargetGroup> CorrelatedTargetsMap;
typedef std::map<SourceFile, std::vector<TargetGroup> > GroupedTargetsMap;
typedef std::map<std::string, std::string> StringStringMap;
+std::vector<const BuilderRecord*> GetAllResolvedTargetRecords(
+ const Builder* builder) {
+ std::vector<const BuilderRecord*> all = builder->GetAllRecords();
+ std::vector<const BuilderRecord*> result;
+ result.reserve(all.size());
+ for (size_t i = 0; i < all.size(); i++) {
+ if (all[i]->type() == BuilderRecord::ITEM_TARGET &&
+ all[i]->should_generate() &&
+ all[i]->item())
+ result.push_back(all[i]);
+ }
+ return result;
+}
+
// Groups targets sharing the same label between debug and release.
-void CorrelateTargets(const std::vector<const Target*>& debug_targets,
- const std::vector<const Target*>& release_targets,
+void CorrelateTargets(const std::vector<const BuilderRecord*>& debug_targets,
+ const std::vector<const BuilderRecord*>& release_targets,
CorrelatedTargetsMap* correlated) {
for (size_t i = 0; i < debug_targets.size(); i++) {
- const Target* target = debug_targets[i];
- (*correlated)[target->label()].debug = target;
+ const BuilderRecord* record = debug_targets[i];
+ (*correlated)[record->label()].debug = record;
}
for (size_t i = 0; i < release_targets.size(); i++) {
- const Target* target = release_targets[i];
- (*correlated)[target->label()].release = target;
+ const BuilderRecord* record = release_targets[i];
+ (*correlated)[record->label()].release = record;
}
}
@@ -51,18 +64,21 @@ void CorrelateTargets(const std::vector<const Target*>& debug_targets,
bool EnsureTargetsMatch(const TargetGroup& group, Err* err) {
// Check that both debug and release made this target.
if (!group.debug || !group.release) {
- const Target* non_null_one = group.debug ? group.debug : group.release;
+ const BuilderRecord* non_null_one =
+ group.debug ? group.debug : group.release;
*err = Err(Location(), "The debug and release builds did not both generate "
"a target with the name\n" +
non_null_one->label().GetUserVisibleName(true));
return false;
}
+ const Target* debug_target = group.debug->item()->AsTarget();
+ const Target* release_target = group.release->item()->AsTarget();
+
// Check the flags that determine if and where we write the GYP file.
- if (group.debug->item_node()->should_generate() !=
- group.release->item_node()->should_generate() ||
- group.debug->external() != group.release->external() ||
- group.debug->gyp_file() != group.release->gyp_file()) {
+ if (group.debug->should_generate() != group.release->should_generate() ||
+ debug_target->external() != release_target->external() ||
+ debug_target->gyp_file() != release_target->gyp_file()) {
*err = Err(Location(), "The metadata for the target\n" +
group.debug->label().GetUserVisibleName(true) +
"\ndoesn't match between the debug and release builds.");
@@ -70,41 +86,40 @@ bool EnsureTargetsMatch(const TargetGroup& group, Err* err) {
}
// Check that the sources match.
- if (group.debug->sources().size() != group.release->sources().size()) {
+ if (debug_target->sources().size() != release_target->sources().size()) {
*err = Err(Location(), "The source file count for the target\n" +
group.debug->label().GetUserVisibleName(true) +
"\ndoesn't have the same number of files between the debug and "
"release builds.");
return false;
}
- for (size_t i = 0; i < group.debug->sources().size(); i++) {
- if (group.debug->sources()[i] != group.release->sources()[i]) {
+ for (size_t i = 0; i < debug_target->sources().size(); i++) {
+ if (debug_target->sources()[i] != release_target->sources()[i]) {
*err = Err(Location(), "The debug and release version of the target \n" +
group.debug->label().GetUserVisibleName(true) +
"\ndon't agree on the file\n" +
- group.debug->sources()[i].value());
+ debug_target->sources()[i].value());
return false;
}
}
// Check that the deps match.
- if (group.debug->deps().size() != group.release->deps().size()) {
+ if (debug_target->deps().size() != release_target->deps().size()) {
*err = Err(Location(), "The source file count for the target\n" +
group.debug->label().GetUserVisibleName(true) +
"\ndoesn't have the same number of deps between the debug and "
"release builds.");
return false;
}
- for (size_t i = 0; i < group.debug->deps().size(); i++) {
- if (group.debug->deps()[i].label != group.release->deps()[i].label) {
+ for (size_t i = 0; i < debug_target->deps().size(); i++) {
+ if (debug_target->deps()[i].label != release_target->deps()[i].label) {
*err = Err(Location(), "The debug and release version of the target \n" +
group.debug->label().GetUserVisibleName(true) +
"\ndon't agree on the dep\n" +
- group.debug->deps()[i].label.GetUserVisibleName(true));
+ debug_target->deps()[i].label.GetUserVisibleName(true));
return false;
}
}
-
return true;
}
@@ -212,16 +227,15 @@ Scope::KeyValueMap GetArgsFromGypDefines() {
return result;
}
-// Returns the number of targets, number of GYP files.
-std::pair<int, int> WriteGypFiles(
- const BuildSettings& debug_settings,
- const BuildSettings& release_settings,
- Err* err) {
+// Returns the (number of targets, number of GYP files).
+std::pair<int, int> WriteGypFiles(CommonSetup* debug_setup,
+ CommonSetup* release_setup,
+ Err* err) {
// Group all targets by output GYP file name.
- std::vector<const Target*> debug_targets;
- std::vector<const Target*> release_targets;
- debug_settings.target_manager().GetAllTargets(&debug_targets);
- release_settings.target_manager().GetAllTargets(&release_targets);
+ std::vector<const BuilderRecord*> debug_targets =
+ GetAllResolvedTargetRecords(debug_setup->builder());
+ std::vector<const BuilderRecord*> release_targets =
+ GetAllResolvedTargetRecords(release_setup->builder());
// Match up the debug and release version of each target by label.
CorrelatedTargetsMap correlated;
@@ -233,19 +247,20 @@ std::pair<int, int> WriteGypFiles(
for (CorrelatedTargetsMap::iterator i = correlated.begin();
i != correlated.end(); ++i) {
const TargetGroup& group = i->second;
- if (!group.debug->item_node()->should_generate())
+ if (!group.debug->should_generate())
continue; // Skip non-generated ones.
- if (group.debug->external())
+ if (group.debug->item()->AsTarget()->external())
continue; // Skip external ones.
- if (group.debug->gyp_file().is_null())
+ if (group.debug->item()->AsTarget()->gyp_file().is_null())
continue; // Skip ones without GYP files.
if (!EnsureTargetsMatch(group, err))
return std::make_pair(0, 0);
target_count++;
- grouped_targets[helper.GetGypFileForTarget(group.debug, err)].push_back(
- group);
+ grouped_targets[
+ helper.GetGypFileForTarget(group.debug->item()->AsTarget(), err)]
+ .push_back(group);
if (err->has_error())
return std::make_pair(0, 0);
}
@@ -358,9 +373,7 @@ int RunGyp(const std::vector<std::string>& args) {
return 1;
Err err;
- std::pair<int, int> counts = WriteGypFiles(setup_debug->build_settings(),
- setup_release->build_settings(),
- &err);
+ std::pair<int, int> counts = WriteGypFiles(setup_debug, setup_release, &err);
if (err.has_error()) {
err.PrintToStdout();
return 1;
diff --git a/tools/gn/command_refs.cc b/tools/gn/command_refs.cc
index d49f536..fde617c 100644
--- a/tools/gn/command_refs.cc
+++ b/tools/gn/command_refs.cc
@@ -9,7 +9,6 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/input_file.h"
#include "tools/gn/item.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/pattern.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
@@ -19,12 +18,12 @@ namespace commands {
namespace {
-// Returns the file path generating this item node.
-base::FilePath FilePathForItemNode(const ItemNode& node) {
- const InputFile* file = node.generated_from_here().begin().file();
- if (!file)
+// Returns the file path generating this record.
+base::FilePath FilePathForRecord(const BuilderRecord* record) {
+ if (!record->item())
return base::FilePath(FILE_PATH_LITERAL("=UNRESOLVED DEPENDENCY="));
- return file->physical_name();
+ return record->item()->defined_from()->GetRange().begin().file()
+ ->physical_name();
}
} // namespace
@@ -34,6 +33,7 @@ const char kRefs_HelpShort[] =
"refs: Find stuff referencing a target, directory, or config.";
const char kRefs_Help[] =
"gn refs <label_pattern> [--files]\n"
+ "\n"
" Finds code referencing a given label. The label can be a\n"
" target or config name. Unlike most other commands, unresolved\n"
" dependencies will be tolerated. This allows you to use this command\n"
@@ -88,33 +88,30 @@ int RunRefs(const std::vector<std::string>& args) {
if (!setup->DoSetup() || !setup->Run())
return 1;
- const ItemTree& item_tree = setup->build_settings().item_tree();
- base::AutoLock lock(item_tree.lock());
-
- std::vector<const ItemNode*> nodes;
- item_tree.GetAllItemNodesLocked(&nodes);
+ std::vector<const BuilderRecord*> records = setup->builder()->GetAllRecords();
const CommandLine* cmdline = CommandLine::ForCurrentProcess();
bool file_output = cmdline->HasSwitch("files");
std::set<std::string> unique_output;
- for (size_t node_index = 0; node_index < nodes.size(); node_index++) {
- const ItemNode& node = *nodes[node_index];
- const ItemNode::ItemNodeMap& deps = node.direct_dependencies();
- for (ItemNode::ItemNodeMap::const_iterator d = deps.begin();
+ for (size_t record_index = 0; record_index < records.size(); record_index++) {
+ const BuilderRecord* record = records[record_index];
+ const BuilderRecord::BuilderRecordSet& deps = record->all_deps();
+ for (BuilderRecord::BuilderRecordSet::const_iterator d = deps.begin();
d != deps.end(); ++d) {
- std::string label = d->first->item()->label().GetUserVisibleName(false);
+ std::string label = (*d)->label().GetUserVisibleName(false);
if (pattern.MatchesString(label)) {
// Got a match.
if (file_output) {
- unique_output.insert(FilePathToUTF8(FilePathForItemNode(node)));
+ unique_output.insert(FilePathToUTF8(FilePathForRecord(record)));
break; // Found a match for this target's file, don't need more.
} else {
// We can get dupes when there are differnet toolchains involved,
// so we want to send all output through the de-duper.
unique_output.insert(
- node.item()->label().GetUserVisibleName(false) + " -> " + label);
+ record->item()->label().GetUserVisibleName(false) + " -> " +
+ label);
}
}
}
diff --git a/tools/gn/commands.cc b/tools/gn/commands.cc
index e600872..d40beab 100644
--- a/tools/gn/commands.cc
+++ b/tools/gn/commands.cc
@@ -4,7 +4,6 @@
#include "tools/gn/commands.h"
#include "tools/gn/item.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/label.h"
#include "tools/gn/setup.h"
#include "tools/gn/standard_out.h"
@@ -52,7 +51,7 @@ const Target* GetTargetForDesc(const std::vector<std::string>& args) {
if (!setup->DoSetup())
return NULL;
- // FIXME(brettw): set the output dir to be a sandbox one to avoid polluting
+ // TODO(brettw): set the output dir to be a sandbox one to avoid polluting
// the real output dir with files written by the build scripts.
// Do the actual load. This will also write out the target ninja files.
@@ -62,8 +61,7 @@ const Target* GetTargetForDesc(const std::vector<std::string>& args) {
// Need to resolve the label after we know the default toolchain.
// TODO(brettw) find the current directory and resolve the input label
// relative to that.
- Label default_toolchain = setup->build_settings().toolchain_manager()
- .GetDefaultToolchainUnlocked();
+ Label default_toolchain = setup->loader()->default_toolchain_label();
Value arg_value(NULL, args[0]);
Err err;
Label label =
@@ -73,19 +71,14 @@ const Target* GetTargetForDesc(const std::vector<std::string>& args) {
return NULL;
}
- ItemNode* node;
- {
- base::AutoLock lock(setup->build_settings().item_tree().lock());
- node = setup->build_settings().item_tree().GetExistingNodeLocked(label);
- }
- if (!node) {
- Err(Location(), "",
- "I don't know about this \"" + label.GetUserVisibleName(false) +
- "\"").PrintToStdout();
+ const Item* item = setup->builder()->GetItem(label);
+ if (!item) {
+ Err(Location(), "Label not found.",
+ label.GetUserVisibleName(false) + " not found.").PrintToStdout();
return NULL;
}
- const Target* target = node->item()->AsTarget();
+ const Target* target = item->AsTarget();
if (!target) {
Err(Location(), "Not a target.",
"The \"" + label.GetUserVisibleName(false) + "\" thing\n"
diff --git a/tools/gn/config.cc b/tools/gn/config.cc
index 5b89e2b..dce951f 100644
--- a/tools/gn/config.cc
+++ b/tools/gn/config.cc
@@ -6,8 +6,6 @@
#include "tools/gn/err.h"
#include "tools/gn/input_file_manager.h"
-#include "tools/gn/item_node.h"
-#include "tools/gn/item_tree.h"
#include "tools/gn/scheduler.h"
Config::Config(const Settings* settings, const Label& label)
@@ -24,59 +22,3 @@ Config* Config::AsConfig() {
const Config* Config::AsConfig() const {
return this;
}
-
-// static
-Config* Config::GetConfig(const Settings* settings,
- const LocationRange& specified_from_here,
- const Label& label,
- Item* dep_from,
- Err* err) {
- DCHECK(!label.is_null());
-
- ItemTree* tree = &settings->build_settings()->item_tree();
- base::AutoLock lock(tree->lock());
-
- ItemNode* node = tree->GetExistingNodeLocked(label);
- Config* config = NULL;
- if (!node) {
- config = new Config(settings, label);
- node = new ItemNode(config);
- node->set_originally_referenced_from_here(specified_from_here);
- tree->AddNodeLocked(node);
-
- // 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()) {
- settings->build_settings()->toolchain_manager().ScheduleInvocationLocked(
- specified_from_here, label.GetToolchainLabel(), label.dir(),
- err);
- }
- } else if ((config = node->item()->AsConfig())) {
- // Previously saw this item as a config.
-
- // If we have no dep_from, we're generating it. In this case, it had better
- // not already be generated.
- if (!dep_from && node->state() != ItemNode::REFERENCED) {
- *err = Err(specified_from_here, "Duplicate config definition.",
- "You already told me about a config with this name.");
- return NULL;
- }
- } else {
- // Previously saw this thing as a non-config.
- *err = Err(specified_from_here,
- "Config name already used.",
- "Previously you specified a " +
- node->item()->GetItemTypeName() + " with this name instead.");
- 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) {
- if (!dep_from->item_node()->AddDependency(
- settings->build_settings(), specified_from_here, node, err))
- return NULL;
- }
- return config;
-}
diff --git a/tools/gn/config.h b/tools/gn/config.h
index e5a1df6..f08b859 100644
--- a/tools/gn/config.h
+++ b/tools/gn/config.h
@@ -9,11 +9,6 @@
#include "tools/gn/config_values.h"
#include "tools/gn/item.h"
-class Err;
-class ItemTree;
-class LocationRange;
-class Settings;
-
// Represents a named config in the dependency graph.
class Config : public Item {
public:
@@ -26,16 +21,6 @@ class Config : public Item {
ConfigValues& config_values() { return config_values_; }
const ConfigValues& config_values() const { return config_values_; }
- // Gets or creates a config.
- //
- // This is like the TargetManager is for Targets, but Configs are so much
- // simpler that this one function is all we need.
- static Config* GetConfig(const Settings* settings,
- const LocationRange& specified_from_here,
- const Label& label,
- Item* dep_from,
- Err* err);
-
private:
ConfigValues config_values_;
diff --git a/tools/gn/config_values_generator.cc b/tools/gn/config_values_generator.cc
index 6f7fb22..f66ddfd 100644
--- a/tools/gn/config_values_generator.cc
+++ b/tools/gn/config_values_generator.cc
@@ -47,14 +47,13 @@ void GetDirList(
} // namespace
-ConfigValuesGenerator::ConfigValuesGenerator(ConfigValues* dest_values,
- Scope* scope,
- const Token& function_token,
- const SourceDir& input_dir,
- Err* err)
+ConfigValuesGenerator::ConfigValuesGenerator(
+ ConfigValues* dest_values,
+ Scope* scope,
+ const SourceDir& input_dir,
+ Err* err)
: config_values_(dest_values),
scope_(scope),
- function_token_(function_token),
input_dir_(input_dir),
err_(err) {
}
diff --git a/tools/gn/config_values_generator.h b/tools/gn/config_values_generator.h
index d58018e..eb8a2e6 100644
--- a/tools/gn/config_values_generator.h
+++ b/tools/gn/config_values_generator.h
@@ -16,11 +16,14 @@ class Err;
class Scope;
class Token;
+// This class fills in the config values from a given scope. It's shared
+// between the "config" function call and all the different binary target types
+// (shared library, static library, etc.) since all of these support the
+// various flags stored in the ConfigValues class.
class ConfigValuesGenerator {
public:
ConfigValuesGenerator(ConfigValues* dest_values,
Scope* scope,
- const Token& function_token,
const SourceDir& input_dir,
Err* err);
~ConfigValuesGenerator();
@@ -31,7 +34,6 @@ class ConfigValuesGenerator {
private:
ConfigValues* config_values_;
Scope* scope_;
- const Token& function_token_;
const SourceDir input_dir_;
Err* err_;
diff --git a/tools/gn/copy_target_generator.cc b/tools/gn/copy_target_generator.cc
index f02d4a7..b1b9076 100644
--- a/tools/gn/copy_target_generator.cc
+++ b/tools/gn/copy_target_generator.cc
@@ -6,14 +6,15 @@
#include "tools/gn/build_settings.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parse_tree.h"
#include "tools/gn/scope.h"
#include "tools/gn/value.h"
CopyTargetGenerator::CopyTargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err)
- : TargetGenerator(target, scope, function_token, err) {
+ : TargetGenerator(target, scope, function_call, err) {
}
CopyTargetGenerator::~CopyTargetGenerator() {
@@ -33,18 +34,16 @@ void CopyTargetGenerator::DoRun() {
return;
if (target_->sources().empty()) {
- *err_ = Err(function_token_, "Empty sources for copy command.",
+ *err_ = Err(function_call_, "Empty sources for copy command.",
"You have to specify at least one file to copy in the \"sources\".");
return;
}
if (target_->script_values().outputs().size() != 1) {
- *err_ = Err(function_token_, "Copy command must have exactly one output.",
+ *err_ = Err(function_call_, "Copy command must have exactly one output.",
"You must specify exactly one value in the \"outputs\" array for the "
"destination of the copy\n(see \"gn help copy\"). If there are "
"multiple sources to copy, use source expansion\n(see \"gn help "
"source_expansion\").");
return;
}
-
- SetToolchainDependency();
}
diff --git a/tools/gn/copy_target_generator.h b/tools/gn/copy_target_generator.h
index b6f7e14..2361bff 100644
--- a/tools/gn/copy_target_generator.h
+++ b/tools/gn/copy_target_generator.h
@@ -13,7 +13,7 @@ class CopyTargetGenerator : public TargetGenerator {
public:
CopyTargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err);
virtual ~CopyTargetGenerator();
diff --git a/tools/gn/function_set_default_toolchain.cc b/tools/gn/function_set_default_toolchain.cc
index c2b20a5..78f313f 100644
--- a/tools/gn/function_set_default_toolchain.cc
+++ b/tools/gn/function_set_default_toolchain.cc
@@ -4,10 +4,10 @@
#include "tools/gn/build_settings.h"
#include "tools/gn/functions.h"
+#include "tools/gn/loader.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/scope.h"
#include "tools/gn/settings.h"
-#include "tools/gn/toolchain_manager.h"
namespace functions {
@@ -52,8 +52,11 @@ Value RunSetDefaultToolchain(Scope* scope,
return Value();
}
- // Ignore non-default-build-config invocations.
- if (!scope->IsProcessingDefaultBuildConfig())
+ // When the loader is expecting the default toolchain to be set, it will set
+ // this key on the scope to point to the destination.
+ Label* default_toolchain_dest = static_cast<Label*>(
+ scope->GetProperty(Loader::kDefaultToolchainKey, NULL));
+ if (!default_toolchain_dest)
return Value();
const SourceDir& current_dir = scope->GetSourceDir();
@@ -66,9 +69,7 @@ Value RunSetDefaultToolchain(Scope* scope,
if (toolchain_label.is_null())
return Value();
- ToolchainManager& mgr =
- scope->settings()->build_settings()->toolchain_manager();
- mgr.SetDefaultToolchainUnlocked(toolchain_label, function->GetRange(), err);
+ *default_toolchain_dest = toolchain_label;
return Value();
}
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index dde0306..cbd8826 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -98,14 +98,15 @@ Value RunToolchain(Scope* scope,
const SourceDir& input_dir = scope->GetSourceDir();
Label label(input_dir, args[0].string_value());
if (g_scheduler->verbose_logging())
- g_scheduler->Log("Generating toolchain", label.GetUserVisibleName(false));
+ g_scheduler->Log("Definining toolchain", label.GetUserVisibleName(false));
// This object will actually be copied into the one owned by the toolchain
// manager, but that has to be done in the lock.
- Toolchain toolchain(scope->settings(), label);
+ scoped_ptr<Toolchain> toolchain(new Toolchain(scope->settings(), label));
+ toolchain->set_defined_from(function);
Scope block_scope(scope);
- block_scope.SetProperty(&kToolchainPropertyKey, &toolchain);
+ block_scope.SetProperty(&kToolchainPropertyKey, toolchain.get());
block->ExecuteBlockInScope(&block_scope, err);
block_scope.SetProperty(&kToolchainPropertyKey, NULL);
if (err->has_error())
@@ -113,17 +114,7 @@ Value RunToolchain(Scope* scope,
if (!block_scope.CheckForUnusedVars(err))
return Value();
- const BuildSettings* build_settings = scope->settings()->build_settings();
- {
- // Save the toolchain definition in the toolchain manager and mark the
- // corresponding item in the dependency tree resolved so that targets
- // that depend on this toolchain know it's ready.
- base::AutoLock lock(build_settings->item_tree().lock());
- build_settings->toolchain_manager().SetToolchainDefinitionLocked(
- toolchain, function->GetRange(), err);
- build_settings->item_tree().MarkItemDefinedLocked(build_settings, label,
- err);
- }
+ scope->settings()->build_settings()->ItemDefined(toolchain.PassAs<Item>());
return Value();
}
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 0e27297..96bc915 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -11,12 +11,10 @@
#include "tools/gn/config_values_generator.h"
#include "tools/gn/err.h"
#include "tools/gn/input_file.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/settings.h"
-#include "tools/gn/target_manager.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
@@ -261,29 +259,21 @@ Value RunConfig(const FunctionCallNode* function,
Label label(MakeLabelForScope(scope, function, args[0].string_value()));
if (g_scheduler->verbose_logging())
- g_scheduler->Log("Generating config", label.GetUserVisibleName(true));
+ g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
- // Create the empty config object.
- ItemTree* tree = &scope->settings()->build_settings()->item_tree();
- Config* config = Config::GetConfig(scope->settings(), function->GetRange(),
- label, NULL, err);
- if (err->has_error())
- return Value();
+ // Create the new config.
+ scoped_ptr<Config> config(new Config(scope->settings(), label));
+ config->set_defined_from(function);
// Fill it.
const SourceDir& input_dir = scope->GetSourceDir();
- ConfigValuesGenerator gen(&config->config_values(), scope,
- function->function(), input_dir, err);
+ ConfigValuesGenerator gen(&config->config_values(), scope, input_dir, err);
gen.Run();
if (err->has_error())
return Value();
// Mark as complete.
- {
- base::AutoLock lock(tree->lock());
- tree->MarkItemDefinedLocked(scope->settings()->build_settings(), label,
- err);
- }
+ scope->settings()->build_settings()->ItemDefined(config.PassAs<Item>());
return Value();
}
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index fc968e5..ceb0fbb 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -41,7 +41,7 @@ Value ExecuteGenericTarget(const char* target_type,
if (err->has_error())
return Value();
- TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+ TargetGenerator::GenerateTarget(&block_scope, function, args,
target_type, err);
if (err->has_error())
return Value();
@@ -109,7 +109,7 @@ Value RunComponent(Scope* scope,
if (err->has_error())
return Value();
- TargetGenerator::GenerateTarget(&block_scope, function->function(), args,
+ TargetGenerator::GenerateTarget(&block_scope, function, args,
component_mode, err);
return Value();
}
@@ -163,8 +163,7 @@ Value RunCopy(const FunctionCallNode* function,
if (!EnsureNotProcessingImport(function, scope, err) ||
!EnsureNotProcessingBuildConfig(function, scope, err))
return Value();
- TargetGenerator::GenerateTarget(scope, function->function(), args,
- functions::kCopy, err);
+ TargetGenerator::GenerateTarget(scope, function, args, functions::kCopy, err);
return Value();
}
diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp
index a4f4c34..3a04387 100644
--- a/tools/gn/gn.gyp
+++ b/tools/gn/gn.gyp
@@ -16,6 +16,10 @@
'binary_target_generator.h',
'build_settings.cc',
'build_settings.h',
+ 'builder.cc',
+ 'builder.h',
+ 'builder_record.cc',
+ 'builder_record.h',
'command_args.cc',
'command_desc.cc',
'command_gen.cc',
@@ -72,13 +76,11 @@
'input_file_manager.h',
'item.cc',
'item.h',
- 'item_node.cc',
- 'item_node.h',
- 'item_tree.cc',
- 'item_tree.h',
'label.cc',
'label.h',
'label_ptr.h',
+ 'loader.cc',
+ 'loader.h',
'location.cc',
'location.h',
'ninja_binary_target_writer.cc',
@@ -136,16 +138,12 @@
'target.h',
'target_generator.cc',
'target_generator.h',
- 'target_manager.cc',
- 'target_manager.h',
'token.cc',
'token.h',
'tokenizer.cc',
'tokenizer.h',
'toolchain.cc',
'toolchain.h',
- 'toolchain_manager.cc',
- 'toolchain_manager.h',
'trace.cc',
'trace.h',
'value.cc',
@@ -172,12 +170,14 @@
'target_name': 'gn_unittests',
'type': '<(gtest_target_type)',
'sources': [
+ 'builder_unittest.cc',
'escape_unittest.cc',
'file_template_unittest.cc',
'filesystem_utils_unittest.cc',
'function_rebase_path_unittest.cc',
'input_conversion_unittest.cc',
'label_unittest.cc',
+ 'loader_unittest.cc',
'ninja_binary_target_writer_unittest.cc',
'ninja_helper_unittest.cc',
'ninja_copy_target_writer_unittest.cc',
@@ -189,7 +189,6 @@
'source_dir_unittest.cc',
'string_utils_unittest.cc',
'target_generator_unittest.cc',
- 'target_manager_unittest.cc',
'target_unittest.cc',
'test_with_scope.cc',
'test_with_scope.h',
diff --git a/tools/gn/group_target_generator.cc b/tools/gn/group_target_generator.cc
index 30238e7..d52eb90c 100644
--- a/tools/gn/group_target_generator.cc
+++ b/tools/gn/group_target_generator.cc
@@ -4,11 +4,12 @@
#include "tools/gn/group_target_generator.h"
-GroupTargetGenerator::GroupTargetGenerator(Target* target,
- Scope* scope,
- const Token& function_token,
- Err* err)
- : TargetGenerator(target, scope, function_token, err) {
+GroupTargetGenerator::GroupTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {
}
GroupTargetGenerator::~GroupTargetGenerator() {
diff --git a/tools/gn/group_target_generator.h b/tools/gn/group_target_generator.h
index a88ec03..4739949 100644
--- a/tools/gn/group_target_generator.h
+++ b/tools/gn/group_target_generator.h
@@ -13,7 +13,7 @@ class GroupTargetGenerator : public TargetGenerator {
public:
GroupTargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err);
virtual ~GroupTargetGenerator();
diff --git a/tools/gn/gyp_binary_target_writer.cc b/tools/gn/gyp_binary_target_writer.cc
index f668fd3..140ea15 100644
--- a/tools/gn/gyp_binary_target_writer.cc
+++ b/tools/gn/gyp_binary_target_writer.cc
@@ -7,6 +7,7 @@
#include <set>
#include "base/logging.h"
+#include "tools/gn/builder_record.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/err.h"
#include "tools/gn/escape.h"
@@ -118,7 +119,7 @@ GypBinaryTargetWriter::Flags::~Flags() {}
GypBinaryTargetWriter::GypBinaryTargetWriter(const TargetGroup& group,
std::ostream& out)
- : GypTargetWriter(group.debug, out),
+ : GypTargetWriter(group.debug->item()->AsTarget(), out),
group_(group) {
}
@@ -193,12 +194,12 @@ void GypBinaryTargetWriter::WriteVCConfiguration(int indent) {
Indent(indent) << "'configurations': {\n";
Indent(indent + kExtraIndent) << "'Debug': {\n";
- Flags debug_flags(FlagsFromTarget(group_.debug));
+ Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget()));
WriteVCFlags(debug_flags, indent + kExtraIndent * 2);
Indent(indent + kExtraIndent) << "},\n";
Indent(indent + kExtraIndent) << "'Release': {\n";
- Flags release_flags(FlagsFromTarget(group_.release));
+ Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget()));
WriteVCFlags(release_flags, indent + kExtraIndent * 2);
Indent(indent + kExtraIndent) << "},\n";
@@ -221,15 +222,18 @@ void GypBinaryTargetWriter::WriteLinuxConfiguration(int indent) {
Indent(indent + kExtraIndent) << "['_toolset == \"host\"', {\n";
Indent(indent + kExtraIndent * 2) << "'configurations': {\n";
Indent(indent + kExtraIndent * 3) << "'Debug': {\n";
- WriteLinuxFlagsForTarget(group_.host_debug, indent + kExtraIndent * 4);
+ WriteLinuxFlagsForTarget(group_.host_debug->item()->AsTarget(),
+ indent + kExtraIndent * 4);
Indent(indent + kExtraIndent * 3) << "},\n";
Indent(indent + kExtraIndent * 3) << "'Release': {\n";
- WriteLinuxFlagsForTarget(group_.host_release, indent + kExtraIndent * 4);
+ WriteLinuxFlagsForTarget(group_.host_release->item()->AsTarget(),
+ indent + kExtraIndent * 4);
Indent(indent + kExtraIndent * 3) << "},\n";
Indent(indent + kExtraIndent * 2) << "}\n";
// The sources are per-toolset but shared between debug & release.
- WriteSources(group_.host_debug, indent + kExtraIndent * 2);
+ WriteSources(group_.host_debug->item()->AsTarget(),
+ indent + kExtraIndent * 2);
Indent(indent + kExtraIndent) << "],\n";
}
@@ -238,10 +242,12 @@ void GypBinaryTargetWriter::WriteLinuxConfiguration(int indent) {
Indent(indent + kExtraIndent) << "['_toolset == \"target\"', {\n";
Indent(indent + kExtraIndent * 2) << "'configurations': {\n";
Indent(indent + kExtraIndent * 3) << "'Debug': {\n";
- WriteLinuxFlagsForTarget(group_.debug, indent + kExtraIndent * 4);
+ WriteLinuxFlagsForTarget(group_.debug->item()->AsTarget(),
+ indent + kExtraIndent * 4);
Indent(indent + kExtraIndent * 3) << "},\n";
Indent(indent + kExtraIndent * 3) << "'Release': {\n";
- WriteLinuxFlagsForTarget(group_.release, indent + kExtraIndent * 4);
+ WriteLinuxFlagsForTarget(group_.release->item()->AsTarget(),
+ indent + kExtraIndent * 4);
Indent(indent + kExtraIndent * 3) << "},\n";
Indent(indent + kExtraIndent * 2) << "},\n";
@@ -258,12 +264,12 @@ void GypBinaryTargetWriter::WriteMacConfiguration(int indent) {
Indent(indent) << "'configurations': {\n";
Indent(indent + kExtraIndent) << "'Debug': {\n";
- Flags debug_flags(FlagsFromTarget(group_.debug));
+ Flags debug_flags(FlagsFromTarget(group_.debug->item()->AsTarget()));
WriteMacFlags(debug_flags, indent + kExtraIndent * 2);
Indent(indent + kExtraIndent) << "},\n";
Indent(indent + kExtraIndent) << "'Release': {\n";
- Flags release_flags(FlagsFromTarget(group_.release));
+ Flags release_flags(FlagsFromTarget(group_.release->item()->AsTarget()));
WriteMacFlags(release_flags, indent + kExtraIndent * 2);
Indent(indent + kExtraIndent) << "},\n";
diff --git a/tools/gn/gyp_target_writer.cc b/tools/gn/gyp_target_writer.cc
index 8a55bdc..29cec2c 100644
--- a/tools/gn/gyp_target_writer.cc
+++ b/tools/gn/gyp_target_writer.cc
@@ -9,6 +9,7 @@
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "tools/gn/build_settings.h"
+#include "tools/gn/builder_record.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/gyp_binary_target_writer.h"
#include "tools/gn/scheduler.h"
@@ -30,15 +31,16 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file,
Err* err) {
if (targets.empty())
return;
- const BuildSettings* debug_build_settings =
- targets[0].debug->settings()->build_settings();
+ const Settings* debug_settings =
+ targets[0].debug->item()->AsTarget()->settings();
+ const BuildSettings* debug_build_settings = debug_settings->build_settings();
std::stringstream file;
file << "# Generated by GN. Do not edit.\n\n";
file << "{\n";
file << " 'skip_includes': 1,\n";
- if (targets[0].debug->settings()->IsMac()) {
+ if (debug_settings->IsMac()) {
// Global settings for make/ninja. This must match common.gypi :(
file << " 'make_global_settings': [\n";
file << " ['CC', 'third_party/llvm-build/Release+Asserts/bin/clang'],\n";
@@ -53,7 +55,8 @@ void GypTargetWriter::WriteFile(const SourceFile& gyp_file,
file << " 'targets': [\n";
for (size_t i = 0; i < targets.size(); i++) {
- switch (targets[i].debug->output_type()) {
+ const Target* cur = targets[i].debug->item()->AsTarget();
+ switch (cur->output_type()) {
case Target::COPY_FILES:
case Target::CUSTOM:
case Target::GROUP:
diff --git a/tools/gn/gyp_target_writer.h b/tools/gn/gyp_target_writer.h
index 27346b0..597a609 100644
--- a/tools/gn/gyp_target_writer.h
+++ b/tools/gn/gyp_target_writer.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "tools/gn/gyp_helper.h"
+class BuilderRecord;
class Err;
class Settings;
class SourceFile;
@@ -25,10 +26,10 @@ class GypTargetWriter {
host_debug(NULL),
host_release(NULL) {
}
- const Target* debug;
- const Target* release;
- const Target* host_debug;
- const Target* host_release;
+ const BuilderRecord* debug;
+ const BuilderRecord* release;
+ const BuilderRecord* host_debug;
+ const BuilderRecord* host_release;
};
GypTargetWriter(const Target* target, std::ostream& out);
diff --git a/tools/gn/input_file_manager.cc b/tools/gn/input_file_manager.cc
index cc41039..0c4bf280 100644
--- a/tools/gn/input_file_manager.cc
+++ b/tools/gn/input_file_manager.cc
@@ -152,6 +152,9 @@ const ParseNode* InputFileManager::SyncLoadFile(
base::AutoUnlock unlock(lock_);
data->completion_event->Wait();
}
+ // If there were multiple waiters on the same event, we now need to wake
+ // up the next one.
+ data->completion_event->Signal();
}
}
diff --git a/tools/gn/item.cc b/tools/gn/item.cc
index fa16975..f2fe9fe 100644
--- a/tools/gn/item.cc
+++ b/tools/gn/item.cc
@@ -8,7 +8,8 @@
Item::Item(const Settings* settings, const Label& label)
: settings_(settings),
- label_(label) {
+ label_(label),
+ defined_from_(NULL) {
}
Item::~Item() {
diff --git a/tools/gn/item.h b/tools/gn/item.h
index 69115bd..4d9f25b 100644
--- a/tools/gn/item.h
+++ b/tools/gn/item.h
@@ -10,7 +10,7 @@
#include "tools/gn/label.h"
class Config;
-class ItemNode;
+class ParseNode;
class Settings;
class Target;
class Toolchain;
@@ -28,12 +28,8 @@ class Item {
// accessed from any thread with no locking once the item is constructed.
const Label& label() const { return label_; }
- // The Item and the ItemNode make a pair. This will be set when the ItemNode
- // is constructed that owns this Item. The ItemNode should only be
- // dereferenced with the ItemTree lock held.
- ItemNode* item_node() { return item_node_; }
- const ItemNode* item_node() const { return item_node_; }
- void set_item_node(ItemNode* in) { item_node_ = in; }
+ const ParseNode* defined_from() const { return defined_from_; }
+ void set_defined_from(const ParseNode* df) { defined_from_ = df; }
// Manual RTTI.
virtual Config* AsConfig();
@@ -54,8 +50,7 @@ class Item {
private:
const Settings* settings_;
Label label_;
-
- ItemNode* item_node_;
+ const ParseNode* defined_from_;
};
#endif // TOOLS_GN_ITEM_H_
diff --git a/tools/gn/item_node.cc b/tools/gn/item_node.cc
deleted file mode 100644
index 2143b06..0000000
--- a/tools/gn/item_node.cc
+++ /dev/null
@@ -1,120 +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/item_node.h"
-
-#include <algorithm>
-
-#include "base/callback.h"
-#include "base/logging.h"
-#include "tools/gn/build_settings.h"
-#include "tools/gn/item.h"
-
-ItemNode::ItemNode(Item* i)
- : state_(REFERENCED),
- item_(i),
- should_generate_(false) {
- item_->set_item_node(this);
-}
-
-ItemNode::~ItemNode() {
-}
-
-bool ItemNode::SetShouldGenerate(const BuildSettings* build_settings,
- Err* err) {
- if (should_generate_)
- return true;
- should_generate_ = true;
-
- if (state_ == DEFINED) {
- if (!ScheduleDepsLoad(build_settings, err))
- return false;
- } else if (state_ == RESOLVED) {
- // The item may have been resolved even if we didn't set the generate bit
- // if all of its deps were loaded some other way. In this case, we need
- // to run the closure which we skipped when it became resolved.
- if (!resolved_closure_.is_null())
- resolved_closure_.Run();
- }
-
- // Pass the generate bit to all deps.
- for (ItemNodeMap::iterator i = direct_dependencies_.begin();
- i != direct_dependencies_.end(); ++i) {
- if (!i->first->SetShouldGenerate(build_settings, err))
- return false;
- }
- return true;
-}
-
-bool ItemNode::AddDependency(const BuildSettings* build_settings,
- const LocationRange& specified_from_here,
- ItemNode* node,
- Err* err) {
- // Can't add more deps once it's been defined.
- DCHECK(state_ == REFERENCED);
-
- if (direct_dependencies_.find(node) != direct_dependencies_.end())
- return true; // Already have this dep.
-
- direct_dependencies_[node] = specified_from_here;
-
- if (node->state() != RESOLVED) {
- // Wire up the pending resolution info.
- unresolved_dependencies_[node] = specified_from_here;
- node->waiting_on_resolution_[this] = specified_from_here;
- }
-
- if (should_generate_) {
- if (!node->SetShouldGenerate(build_settings, err))
- return false;
- }
- return true;
-}
-
-void ItemNode::MarkDirectDependencyResolved(ItemNode* node) {
- DCHECK(unresolved_dependencies_.find(node) != unresolved_dependencies_.end());
- unresolved_dependencies_.erase(node);
-}
-
-void ItemNode::SwapOutWaitingDependencySet(ItemNodeMap* out_map) {
- waiting_on_resolution_.swap(*out_map);
- DCHECK(waiting_on_resolution_.empty());
-}
-
-bool ItemNode::SetDefined(const BuildSettings* build_settings, Err* err) {
- DCHECK(state_ == REFERENCED);
- state_ = DEFINED;
-
- if (should_generate_)
- return ScheduleDepsLoad(build_settings, err);
- return true;
-}
-
-void ItemNode::SetResolved() {
- DCHECK(state_ != RESOLVED);
- state_ = RESOLVED;
-
- if (should_generate_ && !resolved_closure_.is_null())
- resolved_closure_.Run();
-}
-
-bool ItemNode::ScheduleDepsLoad(const BuildSettings* build_settings,
- Err* err) {
- DCHECK(state_ == DEFINED);
- DCHECK(should_generate_); // Shouldn't be requesting deps for ungenerated
- // items.
-
- for (ItemNodeMap::const_iterator i = unresolved_dependencies_.begin();
- i != unresolved_dependencies_.end(); ++i) {
- Label toolchain_label = i->first->item()->label().GetToolchainLabel();
- SourceDir dir_to_load = i->first->item()->label().dir();
-
- if (!build_settings->toolchain_manager().ScheduleInvocationLocked(
- i->second, toolchain_label, dir_to_load, err))
- return false;
- }
-
- state_ = PENDING_DEPS;
- return true;
-}
diff --git a/tools/gn/item_node.h b/tools/gn/item_node.h
deleted file mode 100644
index dbee141..0000000
--- a/tools/gn/item_node.h
+++ /dev/null
@@ -1,159 +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.
-
-#ifndef TOOLS_GN_ITEM_NODE_H_
-#define TOOLS_GN_ITEM_NODE_H_
-
-#include <map>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "tools/gn/location.h"
-
-class BuildSettings;
-class Err;
-class Item;
-
-// Represents a node in the depdency tree. It references an Item which is
-// the actual thing.
-//
-// The items and nodes are split apart so that the ItemTree can manipulate
-// the dependencies one one thread while the Item itself is been modified on
-// another.
-class ItemNode {
- public:
- // The state of this node. As more of the load progresses, we'll move
- // downward in this list toward the resolved state.
- enum State {
- // Another item has referenced this one by name, but we have not yet
- // encountered its definition.
- REFERENCED = 0,
-
- // We've seen the definition of this item but have not requested that its
- // dependencies be loaded. In non-greedy generation mode (see item_tree.h)
- // some nodes will stay in this state forever as long as they're not needed
- // for anything that is required.
- DEFINED,
-
- // The item has been defined and we've requested that all of the
- // dependencies be loaded. Not all of the dependencies have been resolved,
- // however, and we're still waiting on some build files to be run (or
- // perhaps there are undefined dependencies).
- PENDING_DEPS,
-
- // All of this item's transitive dependencies have been found and
- // resolved.
- RESOLVED,
- };
-
- // Stores a set of ItemNodes and the associated range that the dependency
- // was added from.
- typedef std::map<ItemNode*, LocationRange> ItemNodeMap;
-
- // Takes ownership of the pointer.
- // Initial state will be REFERENCED.
- ItemNode(Item* i);
- ~ItemNode();
-
- State state() const { return state_; }
-
- // This closure will be executed when the item is resolved and it has the
- // should_generate flag set.
- void set_resolved_closure(const base::Closure& closure) {
- resolved_closure_ = closure;
- }
-
- const Item* item() const { return item_.get(); }
- Item* item() { return item_.get(); }
-
- // True if this item should be generated. In greedy mode, this will alwyas
- // be set. Otherwise, this bit will be "pushed" through the tree to
- // generate the minimum set of targets required for some special base target.
- // Initialized to false.
- //
- // If this item has been defined, setting this flag will schedule the load
- // of dependent nodes and also set their should_generate bits.
- bool should_generate() const { return should_generate_; }
- bool SetShouldGenerate(const BuildSettings* build_settings, Err* err);
-
- // Where this was created from, which might be when it was generated or
- // when it was first referenced from another target.
- const LocationRange& originally_referenced_from_here() const {
- return originally_referenced_from_here_;
- }
- void set_originally_referenced_from_here(const LocationRange& r) {
- originally_referenced_from_here_ = r;
- }
-
- // Where this was generated from. This will be empty for items that have
- // been referenced but not generated. Note that this has to be one the
- // ItemNode because it can be changing from multiple threads and we need
- // to be sure that access is serialized.
- const LocationRange& generated_from_here() const {
- return generated_from_here_;
- }
- void set_generated_from_here(const LocationRange& r) {
- generated_from_here_ = r;
- }
-
- const ItemNodeMap& direct_dependencies() const {
- return direct_dependencies_;
- }
- const ItemNodeMap& unresolved_dependencies() const {
- return unresolved_dependencies_;
- }
-
- bool AddDependency(const BuildSettings* build_settings,
- const LocationRange& specified_from_here,
- ItemNode* node,
- Err* err);
-
- // Removes the given dependency from the unresolved list. Does not do
- // anything else to update waiters.
- void MarkDirectDependencyResolved(ItemNode* node);
-
- // Destructively retrieve the set of waiting nodes.
- void SwapOutWaitingDependencySet(ItemNodeMap* out_map);
-
- // Marks this item state as defined (see above). If the should generate
- // flag is set, this will schedule a load of the dependencies and
- // automatically transition to the PENDING_DEPS state.
- bool SetDefined(const BuildSettings* build_settings, Err* err);
-
- // Marks this item state as resolved (see above).
- void SetResolved();
-
- private:
- // Schedules loading the dependencies of this node. The current state must
- // be DEFINED, and this call will transition the state to PENDING_DEPS.
- //
- // Requesting deps can fail. On failure returns false and sets the err.
- bool ScheduleDepsLoad(const BuildSettings* build_settings, Err* err);
-
- State state_;
- scoped_ptr<Item> item_;
- bool should_generate_; // See getter above.
-
- LocationRange originally_referenced_from_here_;
- LocationRange generated_from_here_;
-
- // What to run when this item is resolved.
- base::Closure resolved_closure_;
-
- // Everything this item directly depends on.
- ItemNodeMap direct_dependencies_;
-
- // Unresolved things this item directly depends on.
- ItemNodeMap unresolved_dependencies_;
-
- // These items are waiting on us to be resolved before they can be
- // resolved. This is the backpointer for unresolved_dependencies_.
- ItemNodeMap waiting_on_resolution_;
-
- DISALLOW_COPY_AND_ASSIGN(ItemNode);
-};
-
-#endif // TOOLS_GN_ITEM_NODE_H_
diff --git a/tools/gn/item_tree.cc b/tools/gn/item_tree.cc
deleted file mode 100644
index 42e005b..0000000
--- a/tools/gn/item_tree.cc
+++ /dev/null
@@ -1,208 +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/item_tree.h"
-
-#include <algorithm>
-
-#include "base/stl_util.h"
-#include "tools/gn/err.h"
-#include "tools/gn/item.h"
-#include "tools/gn/item_node.h"
-
-namespace {
-
-// Recursively looks in the tree for a given node, returning true if it
-// was found in the dependecy graph. This is used to see if a given node
-// participates in a cycle.
-//
-// Note that "look_for" and "search_in" will be the same node when starting the
-// search, so we don't want to return true in that case.
-//
-// If a cycle is found, the return value will be true and the cycle vector will
-// be filled with the path (in reverse order).
-bool RecursiveFindCycle(const ItemNode* look_for,
- const ItemNode* search_in,
- std::vector<const ItemNode*>* cycle) {
- const ItemNode::ItemNodeMap& unresolved =
- search_in->unresolved_dependencies();
- for (ItemNode::ItemNodeMap::const_iterator i = unresolved.begin();
- i != unresolved.end(); ++i) {
- ItemNode* cur = i->first;
- if (cur == look_for) {
- cycle->push_back(cur);
- return true;
- }
-
- if (RecursiveFindCycle(look_for, cur, cycle)) {
- // Found a cycle inside this one, record our path and return.
- cycle->push_back(cur);
- return true;
- }
- }
- return false;
-}
-
-} // namespace
-
-ItemTree::ItemTree() {
-}
-
-ItemTree::~ItemTree() {
- STLDeleteContainerPairSecondPointers(items_.begin(), items_.end());
-}
-
-ItemNode* ItemTree::GetExistingNodeLocked(const Label& label) {
- lock_.AssertAcquired();
- StringToNodeHash::iterator found = items_.find(label);
- if (found == items_.end())
- return NULL;
- return found->second;
-}
-
-void ItemTree::AddNodeLocked(ItemNode* node) {
- lock_.AssertAcquired();
- DCHECK(items_.find(node->item()->label()) == items_.end());
- items_[node->item()->label()] = node;
-}
-
-bool ItemTree::MarkItemDefinedLocked(const BuildSettings* build_settings,
- const Label& label,
- Err* err) {
- lock_.AssertAcquired();
- DCHECK(items_.find(label) != items_.end());
-
- ItemNode* node = items_[label];
-
- if (!node->unresolved_dependencies().empty()) {
- // Still some pending dependencies, wait for those to be resolved.
- if (!node->SetDefined(build_settings, err))
- return false;
- return true;
- }
-
- // No more pending deps.
- MarkItemResolvedLocked(node);
- return true;
-}
-
-void ItemTree::GetAllItemNodesLocked(std::vector<const ItemNode*>* dest) const {
- lock_.AssertAcquired();
- dest->reserve(items_.size());
- for (StringToNodeHash::const_iterator i = items_.begin();
- i != items_.end(); ++i)
- dest->push_back(i->second);
-}
-
-void ItemTree::GetAllItemsLocked(std::vector<const Item*>* dest) const {
- lock_.AssertAcquired();
- dest->reserve(items_.size());
- for (StringToNodeHash::const_iterator i = items_.begin();
- i != items_.end(); ++i) {
- dest->push_back(i->second->item());
- }
-}
-
-Err ItemTree::CheckForBadItems() const {
- base::AutoLock lock(lock_);
-
- // Look for errors where we find a GENERATED node that refers to a REFERENCED
- // one. There may be other nodes depending on the GENERATED one, but listing
- // all of those isn't helpful, we want to find the broken link.
- //
- // This finds normal "missing dependency" errors but does not find circular
- // dependencies because in this case all items in the cycle will be GENERATED
- // but none will be resolved. If this happens, we'll check explicitly for
- // that below.
- std::vector<const ItemNode*> bad_nodes;
- std::string depstring;
- for (StringToNodeHash::const_iterator i = items_.begin();
- i != items_.end(); ++i) {
- const ItemNode* src = i->second;
- if (!src->should_generate())
- continue; // Skip ungenerated nodes.
-
- if (src->state() == ItemNode::DEFINED ||
- src->state() == ItemNode::PENDING_DEPS) {
- bad_nodes.push_back(src);
-
- // Check dependencies.
- for (ItemNode::ItemNodeMap::const_iterator dest =
- src->unresolved_dependencies().begin();
- dest != src->unresolved_dependencies().end();
- ++dest) {
- const ItemNode* dest_node = dest->first;
- if (dest_node->state() == ItemNode::REFERENCED) {
- depstring += "\"" + src->item()->label().GetUserVisibleName(false) +
- "\" needs " + dest_node->item()->GetItemTypeName() +
- " \"" + dest_node->item()->label().GetUserVisibleName(false) +
- "\"\n";
- }
- }
- }
- }
-
- if (!bad_nodes.empty() && depstring.empty()) {
- // Our logic above found a bad node but didn't identify the problem. This
- // normally means a circular dependency.
- depstring = CheckForCircularDependenciesLocked(bad_nodes);
- if (depstring.empty()) {
- // Something's very wrong, just dump out the bad nodes.
- depstring = "I have no idea what went wrong, but these are unresolved, "
- "possible due to an\ninternal error:";
- for (size_t i = 0; i < bad_nodes.size(); i++) {
- depstring += "\n\"" +
- bad_nodes[i]->item()->label().GetUserVisibleName(false) + "\"";
- }
- }
- }
-
- if (depstring.empty())
- return Err();
- return Err(Location(), "Unresolved dependencies.", depstring);
-}
-
-void ItemTree::MarkItemResolvedLocked(ItemNode* node) {
- node->SetResolved();
- node->item()->OnResolved();
-
- // Now update our waiters, pushing the "resolved" bit.
- ItemNode::ItemNodeMap waiting;
- node->SwapOutWaitingDependencySet(&waiting);
- for (ItemNode::ItemNodeMap::iterator i = waiting.begin();
- i != waiting.end(); ++i) {
- ItemNode* waiter = i->first;
-
- // Our node should be unresolved in the waiter.
- DCHECK(waiter->unresolved_dependencies().find(node) !=
- waiter->unresolved_dependencies().end());
- waiter->MarkDirectDependencyResolved(node);
-
- // Recursively mark nodes as resolved.
- if ((waiter->state() == ItemNode::DEFINED ||
- waiter->state() == ItemNode::PENDING_DEPS) &&
- waiter->unresolved_dependencies().empty())
- MarkItemResolvedLocked(waiter);
- }
-}
-
-std::string ItemTree::CheckForCircularDependenciesLocked(
- const std::vector<const ItemNode*>& bad_nodes) const {
- std::vector<const ItemNode*> cycle;
- if (!RecursiveFindCycle(bad_nodes[0], bad_nodes[0], &cycle))
- return std::string(); // Didn't find a cycle, something else is wrong.
-
- cycle.push_back(bad_nodes[0]);
- std::string ret = "There is a dependency cycle:";
-
- // Walk backwards since the dependency arrows point in the reverse direction.
- for (int i = static_cast<int>(cycle.size()) - 1; i >= 0; i--) {
- ret += "\n \"" + cycle[i]->item()->label().GetUserVisibleName(false) +
- "\"";
- if (i != 0)
- ret += " ->";
- }
-
- return ret;
-}
diff --git a/tools/gn/item_tree.h b/tools/gn/item_tree.h
deleted file mode 100644
index 59de49f..0000000
--- a/tools/gn/item_tree.h
+++ /dev/null
@@ -1,93 +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.
-
-#ifndef TOOLS_GN_ITEM_TREE_H_
-#define TOOLS_GN_ITEM_TREE_H_
-
-#include "base/containers/hash_tables.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "tools/gn/label.h"
-
-class BuildSettings;
-class Err;
-class Item;
-class ItemNode;
-
-// Represents the full dependency tree if labeled items in the system.
-// Generally you will interact with this through the target manager, etc.
-//
-// There are two modes for filling out the dependency tree:
-//
-// - In greedy mode, every target we encounter will be generated. This means
-// that we'll recursively load all of its subdependencies. So if you have
-// a build file that's loaded for any reason, all targets in that build file
-// will be generated.
-//
-// - In non-greedy mode, we'll only generate and load dependncies for targets
-// that have the should_generate bit set. This allows us to load the minimal
-// set of buildfiles required for one or more targets.
-//
-// The main build is generally run in greedy mode, since people expect to be
-// be able to write random tests and have them show up in the output. We'll
-// switch into non-greed mode when doing diagnostics (like displaying the
-// dependency tree on the command line) and for dependencies on targets in
-// other toolchains. The toolchain behavior is important, if target A depends
-// on B with an alternate toolchain, it doesn't mean we should recursively
-// generate all targets in the buildfile just to get B: we should generate the
-// and load the minimum number of files in order to resolve B.
-class ItemTree {
- public:
- ItemTree();
- ~ItemTree();
-
- // This lock must be held when calling the "Locked" functions below.
- base::Lock& lock() const { return lock_; }
-
- // Returns NULL if the item is not found.
- //
- // The lock must be held.
- ItemNode* GetExistingNodeLocked(const Label& label);
-
- // There must not be an item with this label in the tree already. Takes
- // ownership of the pointer.
- //
- // The lock must be held.
- void AddNodeLocked(ItemNode* node);
-
- // Mark the given item as being defined. If it has no unresolved
- // dependencies, it will be marked resolved, and the resolved state will be
- // recursively pushed into the dependency tree. Returns an error if there was
- // an error.
- bool MarkItemDefinedLocked(const BuildSettings* build_settings,
- const Label& label,
- Err* err);
-
- // Fills the given vector with all known items.
- void GetAllItemNodesLocked(std::vector<const ItemNode*>* dest) const;
- void GetAllItemsLocked(std::vector<const Item*>* dest) const;
-
- // Returns an error if there are unresolved dependencies, or no error if
- // there aren't.
- //
- // The lock should not be held.
- Err CheckForBadItems() const;
-
- private:
- void MarkItemResolvedLocked(ItemNode* node);
-
- // Given a set of unresolved nodes, looks for cycles and returns the error
- // message describing any cycles it found.
- std::string CheckForCircularDependenciesLocked(
- const std::vector<const ItemNode*>& bad_nodes) const;
-
- mutable base::Lock lock_;
-
- typedef base::hash_map<Label, ItemNode*> StringToNodeHash;
- StringToNodeHash items_; // Owning pointer.
-
- DISALLOW_COPY_AND_ASSIGN(ItemTree);
-};
-
-#endif // TOOLS_GN_ITEM_TREE_H_
diff --git a/tools/gn/label_ptr.h b/tools/gn/label_ptr.h
index 9bc3df2..9cff9d9 100644
--- a/tools/gn/label_ptr.h
+++ b/tools/gn/label_ptr.h
@@ -20,6 +20,9 @@ struct LabelPtrPair {
LabelPtrPair() : label(), ptr(NULL), origin(NULL) {}
+ explicit LabelPtrPair(const Label& l) : label(l), ptr(NULL), origin(NULL) {
+ }
+
// This contructor is typically used in unit tests, it extracts the label
// automatically from a given pointer.
explicit LabelPtrPair(const T* p) : label(p->label()), ptr(p), origin(NULL) {
diff --git a/tools/gn/loader.cc b/tools/gn/loader.cc
new file mode 100644
index 0000000..7e3ca3d
--- /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);
+}
diff --git a/tools/gn/loader.h b/tools/gn/loader.h
new file mode 100644
index 0000000..3b119c2
--- /dev/null
+++ b/tools/gn/loader.h
@@ -0,0 +1,177 @@
+// 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.
+
+#ifndef TOOLS_GN_LOADER_H_
+#define TOOLS_GN_LOADER_H_
+
+#include <map>
+#include <set>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "tools/gn/label.h"
+#include "tools/gn/scope.h"
+
+namespace base {
+class MessageLoop;
+}
+
+class BuildSettings;
+class Settings;
+class SourceFile;
+class Toolchain;
+
+// The loader manages execution of the different build files. It receives
+// requests (normally from the Builder) when new references are found, and also
+// manages loading the build config files.
+//
+// This loader class is abstract so it can be mocked out for testing the
+// Builder.
+class Loader : public base::RefCountedThreadSafe<Loader> {
+ public:
+ Loader();
+
+ // Loads the given file in the conext of the given toolchain. The initial
+ // call to this (the one that actually starts the generation) should have an
+ // empty toolchain name, which will trigger the load of the default build
+ // config.
+ virtual void Load(const SourceFile& file,
+ const Label& toolchain_name) = 0;
+
+ // Notification that the given toolchain has loaded. This will unblock files
+ // waiting on this definition.
+ virtual void ToolchainLoaded(const Toolchain* toolchain) = 0;
+
+ // Returns the label of the default toolchain.
+ virtual Label GetDefaultToolchain() const = 0;
+
+ // Returns information about the toolchain with the given label. Will return
+ // false if we haven't processed this toolchain yet.
+ virtual const Settings* GetToolchainSettings(const Label& label) = 0;
+
+ // Helper function that extracts the file and toolchain name from the given
+ // label, and calls Load().
+ void Load(const Label& label);
+
+ // Returns the build file that the given label references.
+ static SourceFile BuildFileForLabel(const Label& label);
+
+ // When processing the default build config, we want to capture the argument
+ // of set_default_build_config. The implementation of that function uses this
+ // constant as a property key to get the Label* out of the scope where the
+ // label should be stored.
+ static const void* kDefaultToolchainKey;
+
+ protected:
+ friend class base::RefCountedThreadSafe<Loader>;
+ virtual ~Loader();
+};
+
+class LoaderImpl : public Loader {
+ public:
+ // Callback to emulate InputFileManager::AsyncLoadFile.
+ typedef base::Callback<bool(const LocationRange&,
+ const BuildSettings*,
+ const SourceFile&,
+ const base::Callback<void(const ParseNode*)>&,
+ Err*)> AsyncLoadFileCallback;
+
+ LoaderImpl(const BuildSettings* build_settings);
+
+ // Loader implementation.
+ virtual void Load(const SourceFile& file,
+ const Label& toolchain_name) OVERRIDE;
+ virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE;
+ virtual Label GetDefaultToolchain() const OVERRIDE;
+ virtual const Settings* GetToolchainSettings(const Label& label) OVERRIDE;
+
+ // Sets the message loop corresponding to the main thread. By default this
+ // class will use the thread active during construction, but there is not
+ // a message loop active during construction all the time.
+ void set_main_loop(base::MessageLoop* loop) { main_loop_ = loop; }
+
+ // The complete callback is called whenever there are no more pending loads.
+ // Called on the main thread only. This may be called more than once if the
+ // queue is drained, but then more stuff gets added.
+ void set_complete_callback(const base::Closure& cb) {
+ complete_callback_ = cb;
+ }
+
+ // This callback is used when the loader finds it wants to load a file.
+ void set_async_load_file(const AsyncLoadFileCallback& cb) {
+ async_load_file_ = cb;
+ }
+
+ const Label& default_toolchain_label() const {
+ return default_toolchain_label_;
+ }
+
+ private:
+ struct LoadID;
+ struct ToolchainRecord;
+
+ virtual ~LoaderImpl();
+
+ // Schedules the input file manager to load the given file.
+ void ScheduleLoadFile(const Settings* settings,
+ const SourceFile& file);
+ void ScheduleLoadBuildConfig(
+ Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides);
+
+ // Runs the given file on the background thread. These are called by the
+ // input file manager.
+ void BackgroundLoadFile(const Settings* settings,
+ const SourceFile& file_name,
+ const ParseNode* root);
+ void BackgroundLoadBuildConfig(
+ Settings* settings,
+ const Scope::KeyValueMap& toolchain_overrides,
+ const ParseNode* root);
+
+ // Posted to the main thread when any file other than a build config file
+ // file has completed running.
+ void DidLoadFile();
+
+ // Posted to the main thread when any build config file has completed
+ // running. The label should be the name of the toolchain.
+ //
+ // If there is no defauled toolchain loaded yet, we'll assume that the first
+ // call to this indicates to the default toolchain, and this function will
+ // set the default toolchain name to the given label.
+ void DidLoadBuildConfig(const Label& label);
+
+ // Decrements the pending_loads_ variable and issues the complete callback if
+ // necessary.
+ void DecrementPendingLoads();
+
+ // Forwards to the appropriate location to load the file.
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const base::Callback<void(const ParseNode*)>& callback,
+ Err* err);
+
+ base::MessageLoop* main_loop_;
+
+ int pending_loads_;
+ base::Closure complete_callback_;
+
+ // When non-null, use this callback instead of the InputFileManager for
+ // mocking purposes.
+ AsyncLoadFileCallback async_load_file_;
+
+ typedef std::set<LoadID> LoadIDSet;
+ LoadIDSet invocations_;
+
+ const BuildSettings* build_settings_;
+ Label default_toolchain_label_;
+
+ // Records for the build config file loads.
+ // Owning pointers.
+ typedef std::map<Label, ToolchainRecord*> ToolchainRecordMap;
+ ToolchainRecordMap toolchain_records_;
+};
+
+#endif // TOOLS_GN_LOADER_H_
diff --git a/tools/gn/loader_unittest.cc b/tools/gn/loader_unittest.cc
new file mode 100644
index 0000000..6397684
--- /dev/null
+++ b/tools/gn/loader_unittest.cc
@@ -0,0 +1,191 @@
+// 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 <map>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/linked_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/build_settings.h"
+#include "tools/gn/err.h"
+#include "tools/gn/loader.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/scheduler.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+class MockInputFileManager {
+ public:
+ typedef base::Callback<void(const ParseNode*)> Callback;
+
+ MockInputFileManager() {
+ }
+
+ LoaderImpl::AsyncLoadFileCallback GetCallback();
+
+ // Sets a given response for a given source file.
+ void AddCannedResponse(const SourceFile& source_file,
+ const std::string& source);
+
+ // Returns true if there is/are pending load(s) matching the given file(s).
+ bool HasOnePending(const SourceFile& f) const;
+ bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;
+
+ void IssueAllPending();
+
+ private:
+ struct CannedResult {
+ scoped_ptr<InputFile> input_file;
+ std::vector<Token> tokens;
+ scoped_ptr<ParseNode> root;
+ };
+
+ bool AsyncLoadFile(const LocationRange& origin,
+ const BuildSettings* build_settings,
+ const SourceFile& file_name,
+ const Callback& callback,
+ Err* err) {
+ pending_.push_back(std::make_pair(file_name, callback));
+ return true;
+ }
+
+ // Owning pointers.
+ typedef std::map<SourceFile, linked_ptr<CannedResult> > CannedResponseMap;
+ CannedResponseMap canned_responses_;
+
+ std::vector< std::pair<SourceFile, Callback> > pending_;
+};
+
+LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() {
+ return base::Bind(&MockInputFileManager::AsyncLoadFile,
+ base::Unretained(this));
+}
+
+// Sets a given response for a given source file.
+void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
+ const std::string& source) {
+ CannedResult* canned = new CannedResult;
+ canned->input_file.reset(new InputFile(source_file));
+ canned->input_file->SetContents(source);
+
+ // Tokenize.
+ Err err;
+ canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
+ EXPECT_FALSE(err.has_error());
+
+ // Parse.
+ canned->root = Parser::Parse(canned->tokens, &err).Pass();
+ EXPECT_FALSE(err.has_error());
+
+ canned_responses_[source_file] = linked_ptr<CannedResult>(canned);
+}
+
+bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
+ return pending_.size() == 1u && pending_[0].first == f;
+}
+
+bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
+ const SourceFile& f2) const {
+ if (pending_.size() != 2u)
+ return false;
+ return pending_[0].first == f1 && pending_[1].first == f2;
+}
+
+void MockInputFileManager::IssueAllPending() {
+ BlockNode block(false); // Default response.
+
+ for (size_t i = 0; i < pending_.size(); i++) {
+ CannedResponseMap::const_iterator found =
+ canned_responses_.find(pending_[i].first);
+ if (found == canned_responses_.end())
+ pending_[i].second.Run(&block);
+ else
+ pending_[i].second.Run(found->second->root.get());
+ }
+ pending_.clear();
+}
+
+// LoaderTest ------------------------------------------------------------------
+
+class LoaderTest : public testing::Test {
+ public:
+ LoaderTest() {
+ build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
+ }
+ virtual ~LoaderTest() {
+ }
+
+ protected:
+ Scheduler scheduler_;
+ BuildSettings build_settings_;
+ MockInputFileManager mock_ifm_;
+};
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+TEST_F(LoaderTest, Foo) {
+ SourceFile build_config("//build/config/BUILDCONFIG.gn");
+ build_settings_.set_build_config_file(build_config);
+
+ scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
+
+ // The default toolchain needs to be set by the build config file.
+ mock_ifm_.AddCannedResponse(build_config,
+ "set_default_toolchain(\"//tc:tc\")");
+
+ loader->set_async_load_file(mock_ifm_.GetCallback());
+
+ // Request the root build file be loaded. This should kick off the default
+ // build config loading.
+ SourceFile root_build("//BUILD.gn");
+ loader->Load(root_build, Label());
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Completing the build config load should kick off the root build file load.
+ mock_ifm_.IssueAllPending();
+ scheduler_.main_loop()->RunUntilIdle();
+ EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
+
+ // Load the root build file.
+ mock_ifm_.IssueAllPending();
+ scheduler_.main_loop()->RunUntilIdle();
+
+ // Schedule some other file to load in another toolchain.
+ Label second_tc(SourceDir("//tc2/"), "tc2");
+ SourceFile second_file("//foo/BUILD.gn");
+ loader->Load(second_file, second_tc);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));
+
+ // Running the toolchain file should schedule the build config file to load
+ // for that toolchain.
+ mock_ifm_.IssueAllPending();
+ scheduler_.main_loop()->RunUntilIdle();
+
+ // We have to tell it we have a toolchain definition now (normally the
+ // builder would do this).
+ const Settings* default_settings = loader->GetToolchainSettings(Label());
+ Toolchain second_tc_object(default_settings, second_tc);
+ loader->ToolchainLoaded(&second_tc_object);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Scheduling a second file to load in that toolchain should not make it
+ // pending yet (it's waiting for the build config).
+ SourceFile third_file("//bar/BUILD.gn");
+ loader->Load(third_file, second_tc);
+ EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
+
+ // Running the build config file should make our third file pending.
+ mock_ifm_.IssueAllPending();
+ scheduler_.main_loop()->RunUntilIdle();
+ EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));
+
+ EXPECT_FALSE(scheduler_.is_failed());
+}
diff --git a/tools/gn/ninja_target_writer.cc b/tools/gn/ninja_target_writer.cc
index bdc0472..2b86626 100644
--- a/tools/gn/ninja_target_writer.cc
+++ b/tools/gn/ninja_target_writer.cc
@@ -35,14 +35,8 @@ NinjaTargetWriter::~NinjaTargetWriter() {
}
// static
-void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
- // External targets don't get written to disk, we assume they're managed by
- // an external program. If we're not using an external generator, this is
- // ignored.
- if (target->settings()->build_settings()->using_external_generator() &&
- target->external())
- return;
-
+void NinjaTargetWriter::RunAndWriteFile(const Target* target,
+ const Toolchain* toolchain) {
const Settings* settings = target->settings();
NinjaHelper helper(settings->build_settings());
@@ -57,10 +51,6 @@ void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
if (g_scheduler->verbose_logging())
g_scheduler->Log("Writing", FilePathToUTF8(ninja_file));
- const Toolchain* tc = settings->build_settings()->toolchain_manager()
- .GetToolchainDefinitionUnlocked(settings->toolchain_label());
- CHECK(tc);
-
file_util::CreateDirectory(ninja_file.DirName());
// It's rediculously faster to write to a string and then write that to
@@ -69,19 +59,19 @@ void NinjaTargetWriter::RunAndWriteFile(const Target* target) {
// Call out to the correct sub-type of writer.
if (target->output_type() == Target::COPY_FILES) {
- NinjaCopyTargetWriter writer(target, tc, file);
+ NinjaCopyTargetWriter writer(target, toolchain, file);
writer.Run();
} else if (target->output_type() == Target::CUSTOM) {
- NinjaScriptTargetWriter writer(target, tc, file);
+ NinjaScriptTargetWriter writer(target, toolchain, file);
writer.Run();
} else if (target->output_type() == Target::GROUP) {
- NinjaGroupTargetWriter writer(target, tc, file);
+ NinjaGroupTargetWriter writer(target, toolchain, file);
writer.Run();
} else if (target->output_type() == Target::EXECUTABLE ||
target->output_type() == Target::STATIC_LIBRARY ||
target->output_type() == Target::SHARED_LIBRARY ||
target->output_type() == Target::SOURCE_SET) {
- NinjaBinaryTargetWriter writer(target, tc, file);
+ NinjaBinaryTargetWriter writer(target, toolchain, file);
writer.Run();
} else {
CHECK(0);
diff --git a/tools/gn/ninja_target_writer.h b/tools/gn/ninja_target_writer.h
index e1c3bf8..937b240 100644
--- a/tools/gn/ninja_target_writer.h
+++ b/tools/gn/ninja_target_writer.h
@@ -24,7 +24,7 @@ class NinjaTargetWriter {
std::ostream& out);
virtual ~NinjaTargetWriter();
- static void RunAndWriteFile(const Target* target);
+ static void RunAndWriteFile(const Target* target, const Toolchain* toolchain);
virtual void Run() = 0;
diff --git a/tools/gn/ninja_toolchain_writer.cc b/tools/gn/ninja_toolchain_writer.cc
index 5f33e69..0a6b3de 100644
--- a/tools/gn/ninja_toolchain_writer.cc
+++ b/tools/gn/ninja_toolchain_writer.cc
@@ -9,21 +9,19 @@
#include "base/file_util.h"
#include "base/strings/stringize_macros.h"
#include "tools/gn/build_settings.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/settings.h"
#include "tools/gn/target.h"
#include "tools/gn/toolchain.h"
-#include "tools/gn/toolchain_manager.h"
#include "tools/gn/trace.h"
NinjaToolchainWriter::NinjaToolchainWriter(
const Settings* settings,
+ const Toolchain* toolchain,
const std::vector<const Target*>& targets,
- const std::set<std::string>& skip_files,
std::ostream& out)
: settings_(settings),
+ toolchain_(toolchain),
targets_(targets),
- skip_files_(skip_files),
out_(out),
path_output_(settings_->build_settings()->build_dir(),
ESCAPE_NINJA, true),
@@ -41,8 +39,8 @@ void NinjaToolchainWriter::Run() {
// static
bool NinjaToolchainWriter::RunAndWriteFile(
const Settings* settings,
- const std::vector<const Target*>& targets,
- const std::set<std::string>& skip_files) {
+ const Toolchain* toolchain,
+ const std::vector<const Target*>& targets) {
NinjaHelper helper(settings->build_settings());
base::FilePath ninja_file(settings->build_settings()->GetFullPath(
helper.GetNinjaFileForToolchain(settings).GetSourceFile(
@@ -57,16 +55,12 @@ bool NinjaToolchainWriter::RunAndWriteFile(
if (file.fail())
return false;
- NinjaToolchainWriter gen(settings, targets, skip_files, file);
+ NinjaToolchainWriter gen(settings, toolchain, targets, file);
gen.Run();
return true;
}
void NinjaToolchainWriter::WriteRules() {
- const Toolchain* tc = settings_->build_settings()->toolchain_manager()
- .GetToolchainDefinitionUnlocked(settings_->toolchain_label());
- CHECK(tc);
-
std::string indent(" ");
NinjaHelper helper(settings_->build_settings());
@@ -74,7 +68,7 @@ void NinjaToolchainWriter::WriteRules() {
for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
- const Toolchain::Tool& tool = tc->GetTool(tool_type);
+ const Toolchain::Tool& tool = toolchain_->GetTool(tool_type);
if (tool.command.empty())
continue;
@@ -100,15 +94,7 @@ void NinjaToolchainWriter::WriteRules() {
void NinjaToolchainWriter::WriteSubninjas() {
// Write subninja commands for each generated target.
for (size_t i = 0; i < targets_.size(); i++) {
- if (!targets_[i]->item_node()->should_generate() ||
- (targets_[i]->settings()->build_settings()->using_external_generator()
- && targets_[i]->external()))
- continue;
-
OutputFile ninja_file = helper_.GetNinjaFileForTarget(targets_[i]);
- if (skip_files_.find(ninja_file.value()) != skip_files_.end())
- continue;
-
out_ << "subninja ";
path_output_.WriteFile(out_, ninja_file);
out_ << std::endl;
diff --git a/tools/gn/ninja_toolchain_writer.h b/tools/gn/ninja_toolchain_writer.h
index 5d3daa5..ff0ebc0 100644
--- a/tools/gn/ninja_toolchain_writer.h
+++ b/tools/gn/ninja_toolchain_writer.h
@@ -16,20 +16,20 @@
class BuildSettings;
class Settings;
class Target;
+class Toolchain;
class NinjaToolchainWriter {
public:
// Takes the settings for the toolchain, as well as the list of all targets
- // assicoated with the toolchain. Ninja files exactly matching "skip_files"
- // will not be included in the subninja list.
+ // assicoated with the toolchain.
static bool RunAndWriteFile(const Settings* settings,
- const std::vector<const Target*>& targets,
- const std::set<std::string>& skip_files);
+ const Toolchain* toolchain,
+ const std::vector<const Target*>& targets);
private:
NinjaToolchainWriter(const Settings* settings,
+ const Toolchain* toolchain,
const std::vector<const Target*>& targets,
- const std::set<std::string>& skip_files,
std::ostream& out);
~NinjaToolchainWriter();
@@ -39,8 +39,8 @@ class NinjaToolchainWriter {
void WriteSubninjas();
const Settings* settings_;
+ const Toolchain* toolchain_;
std::vector<const Target*> targets_;
- const std::set<std::string>& skip_files_;
std::ostream& out_;
PathOutput path_output_;
diff --git a/tools/gn/ninja_writer.cc b/tools/gn/ninja_writer.cc
index 1fe893e..e4c0a22 100644
--- a/tools/gn/ninja_writer.cc
+++ b/tools/gn/ninja_writer.cc
@@ -4,25 +4,29 @@
#include "tools/gn/ninja_writer.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/loader.h"
#include "tools/gn/location.h"
#include "tools/gn/ninja_build_writer.h"
#include "tools/gn/ninja_toolchain_writer.h"
-NinjaWriter::NinjaWriter(const BuildSettings* build_settings)
- : build_settings_(build_settings) {
+NinjaWriter::NinjaWriter(const BuildSettings* build_settings,
+ Builder* builder)
+ : build_settings_(build_settings),
+ builder_(builder) {
}
NinjaWriter::~NinjaWriter() {
}
// static
-bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings) {
- NinjaWriter writer(build_settings);
+bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings,
+ Builder* builder) {
+ NinjaWriter writer(build_settings, builder);
std::vector<const Settings*> all_settings;
std::vector<const Target*> default_targets;
- if (!writer.WriteToolchains(std::set<std::string>(),
- &all_settings, &default_targets))
+ if (!writer.WriteToolchains(&all_settings, &default_targets))
return false;
return writer.WriteRootBuildfiles(all_settings, default_targets);
}
@@ -30,60 +34,54 @@ bool NinjaWriter::RunAndWriteFiles(const BuildSettings* build_settings) {
// static
bool NinjaWriter::RunAndWriteToolchainFiles(
const BuildSettings* build_settings,
- const std::set<std::string>& skip_files,
+ Builder* builder,
std::vector<const Settings*>* all_settings) {
- NinjaWriter writer(build_settings);
+ NinjaWriter writer(build_settings, builder);
std::vector<const Target*> default_targets;
- return writer.WriteToolchains(skip_files, all_settings, &default_targets);
+ return writer.WriteToolchains(all_settings, &default_targets);
}
-bool NinjaWriter::WriteToolchains(
- const std::set<std::string>& skip_files,
- std::vector<const Settings*>* all_settings,
- std::vector<const Target*>* default_targets) {
+bool NinjaWriter::WriteToolchains(std::vector<const Settings*>* all_settings,
+ std::vector<const Target*>* default_targets) {
// Categorize all targets by toolchain.
typedef std::map<Label, std::vector<const Target*> > CategorizedMap;
CategorizedMap categorized;
- std::vector<const Target*> all_targets;
- build_settings_->target_manager().GetAllTargets(&all_targets);
- if (all_targets.empty()) {
+ std::vector<const BuilderRecord*> all_records = builder_->GetAllRecords();
+ for (size_t i = 0; i < all_records.size(); i++) {
+ if (all_records[i]->type() == BuilderRecord::ITEM_TARGET &&
+ all_records[i]->should_generate()) {
+ categorized[all_records[i]->label().GetToolchainLabel()].push_back(
+ all_records[i]->item()->AsTarget());
+ }
+ }
+ if (categorized.empty()) {
Err(Location(), "No targets.",
"I could not find any targets to write, so I'm doing nothing.")
.PrintToStdout();
return false;
}
- for (size_t i = 0; i < all_targets.size(); i++) {
- categorized[all_targets[i]->label().GetToolchainLabel()].push_back(
- all_targets[i]);
- }
- Label default_label =
- build_settings_->toolchain_manager().GetDefaultToolchainUnlocked();
+ Label default_label = builder_->loader()->GetDefaultToolchain();
// Write out the toolchain buildfiles, and also accumulate the set of
// all settings and find the list of targets in the default toolchain.
for (CategorizedMap::const_iterator i = categorized.begin();
i != categorized.end(); ++i) {
- const Settings* settings;
- {
- base::AutoLock lock(build_settings_->item_tree().lock());
- Err ignored;
- settings =
- build_settings_->toolchain_manager().GetSettingsForToolchainLocked(
- LocationRange(), i->first, &ignored);
- }
+ const Settings* settings =
+ builder_->loader()->GetToolchainSettings(i->first);
+ const Toolchain* toolchain = builder_->GetToolchain(i->first);
+
all_settings->push_back(settings);
- if (!NinjaToolchainWriter::RunAndWriteFile(settings, i->second,
- skip_files)) {
+ if (!NinjaToolchainWriter::RunAndWriteFile(settings, toolchain,
+ i->second)) {
Err(Location(),
"Couldn't open toolchain buildfile(s) for writing").PrintToStdout();
return false;
}
}
- *default_targets = categorized[
- build_settings_->toolchain_manager().GetDefaultToolchainUnlocked()];
+ *default_targets = categorized[default_label];
return true;
}
diff --git a/tools/gn/ninja_writer.h b/tools/gn/ninja_writer.h
index c2efc92..8859f1a 100644
--- a/tools/gn/ninja_writer.h
+++ b/tools/gn/ninja_writer.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
+class Builder;
class BuildSettings;
class Settings;
class Target;
@@ -18,31 +19,28 @@ class Target;
class NinjaWriter {
public:
// On failure will print an error and will return false.
- static bool RunAndWriteFiles(const BuildSettings* build_settings);
+ static bool RunAndWriteFiles(const BuildSettings* build_settings,
+ Builder* builder);
// Writes only the toolchain.ninja files, skipping the root buildfile. The
// settings for the files written will be added to the vector.
- //
- // The skip files will avoid writing "subninja" rules when we're doing a
- // side-by-side GYP build. .ninja files exactly matching the ones in the set
- // will be ignored.
static bool RunAndWriteToolchainFiles(
const BuildSettings* build_settings,
- const std::set<std::string>& skip_files,
+ Builder* builder,
std::vector<const Settings*>* all_settings);
private:
- NinjaWriter(const BuildSettings* build_settings);
+ NinjaWriter(const BuildSettings* build_settings, Builder* builder);
~NinjaWriter();
bool WriteToolchains(
- const std::set<std::string>& skip_files,
std::vector<const Settings*>* all_settings,
std::vector<const Target*>* default_targets);
bool WriteRootBuildfiles(const std::vector<const Settings*>& all_settings,
const std::vector<const Target*>& default_targets);
const BuildSettings* build_settings_;
+ Builder* builder_;
DISALLOW_COPY_AND_ASSIGN(NinjaWriter);
};
diff --git a/tools/gn/scheduler.cc b/tools/gn/scheduler.cc
index 72c310b..254de0d 100644
--- a/tools/gn/scheduler.cc
+++ b/tools/gn/scheduler.cc
@@ -7,7 +7,6 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
-#include "tools/gn/ninja_target_writer.h"
#include "tools/gn/standard_out.h"
Scheduler* g_scheduler = NULL;
@@ -31,17 +30,22 @@ Scheduler::Scheduler()
input_file_manager_(new InputFileManager),
verbose_logging_(false),
work_count_(0),
- is_failed_(false) {
+ is_failed_(false),
+ has_been_shutdown_(false) {
g_scheduler = this;
}
Scheduler::~Scheduler() {
+ if (!has_been_shutdown_)
+ pool_->Shutdown();
g_scheduler = NULL;
}
bool Scheduler::Run() {
runner_.Run();
+ base::AutoLock lock(lock_);
pool_->Shutdown();
+ has_been_shutdown_ = true;
return !is_failed();
}
@@ -86,13 +90,6 @@ void Scheduler::ScheduleWork(const base::Closure& work) {
base::SequencedWorkerPool::BLOCK_SHUTDOWN);
}
-void Scheduler::ScheduleTargetFileWrite(const Target* target) {
- pool_->PostWorkerTaskWithShutdownBehavior(
- FROM_HERE, base::Bind(&Scheduler::DoTargetFileWrite,
- base::Unretained(this), target),
- base::SequencedWorkerPool::BLOCK_SHUTDOWN);
-}
-
void Scheduler::AddGenDependency(const base::FilePath& file) {
base::AutoLock lock(lock_);
gen_dependencies_.push_back(file);
@@ -130,10 +127,6 @@ void Scheduler::FailWithErrorOnMainThread(const Err& err) {
runner_.Quit();
}
-void Scheduler::DoTargetFileWrite(const Target* target) {
- NinjaTargetWriter::RunAndWriteFile(target);
-}
-
void Scheduler::DoWork(const base::Closure& closure) {
closure.Run();
DecrementWorkCount();
diff --git a/tools/gn/scheduler.h b/tools/gn/scheduler.h
index 4b2c060..8f4a133 100644
--- a/tools/gn/scheduler.h
+++ b/tools/gn/scheduler.h
@@ -40,7 +40,7 @@ class Scheduler {
void ScheduleWork(const base::Closure& work);
- void ScheduleTargetFileWrite(const Target* target);
+ void Shutdown();
// Declares that the given file was read and affected the build output.
//
@@ -79,6 +79,11 @@ class Scheduler {
mutable base::Lock lock_;
bool is_failed_;
+ // Used to track whether the worker pool has been shutdown. This is necessary
+ // to clean up after tests that make a scheduler but don't run the message
+ // loop.
+ bool has_been_shutdown_;
+
// Additional input dependencies. Protected by the lock.
std::vector<base::FilePath> gen_dependencies_;
diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc
index 42277f7..052c55d 100644
--- a/tools/gn/scope.cc
+++ b/tools/gn/scope.cc
@@ -13,8 +13,7 @@ namespace {
// FLags set in the mode_flags_ of a scope. If a bit is set, it applies
// recursively to all dependent scopes.
const unsigned kProcessingBuildConfigFlag = 1;
-const unsigned kProcessingDefaultBuildConfigFlag = 2;
-const unsigned kProcessingImportFlag = 4;
+const unsigned kProcessingImportFlag = 2;
} // namespace
@@ -303,24 +302,6 @@ bool Scope::IsProcessingBuildConfig() const {
return false;
}
-void Scope::SetProcessingDefaultBuildConfig() {
- DCHECK((mode_flags_ & kProcessingDefaultBuildConfigFlag) == 0);
- mode_flags_ |= kProcessingDefaultBuildConfigFlag;
-}
-
-void Scope::ClearProcessingDefaultBuildConfig() {
- DCHECK(mode_flags_ & kProcessingDefaultBuildConfigFlag);
- mode_flags_ &= ~(kProcessingDefaultBuildConfigFlag);
-}
-
-bool Scope::IsProcessingDefaultBuildConfig() const {
- if (mode_flags_ & kProcessingDefaultBuildConfigFlag)
- return true;
- if (containing())
- return containing()->IsProcessingDefaultBuildConfig();
- return false;
-}
-
void Scope::SetProcessingImport() {
DCHECK((mode_flags_ & kProcessingImportFlag) == 0);
mode_flags_ |= kProcessingImportFlag;
diff --git a/tools/gn/scope.h b/tools/gn/scope.h
index 0d1c156..d4a35ff 100644
--- a/tools/gn/scope.h
+++ b/tools/gn/scope.h
@@ -165,8 +165,7 @@ class Scope {
}
// Indicates if we're currently processing the build configuration file.
- // This is true when processing the config file for any toolchain. See also
- // *ProcessingDefaultBuildConfig() below.
+ // This is true when processing the config file for any toolchain.
//
// To set or clear the flag, it must currently be in the opposite state in
// the current scope. Note that querying the state of the flag recursively
@@ -176,12 +175,6 @@ class Scope {
void ClearProcessingBuildConfig();
bool IsProcessingBuildConfig() const;
- // Indicates we're currently processing the default toolchain's build
- // configuration file.
- void SetProcessingDefaultBuildConfig();
- void ClearProcessingDefaultBuildConfig();
- bool IsProcessingDefaultBuildConfig() const;
-
// Indicates if we're currently processing an import file.
//
// See SetProcessingBaseConfig for how flags work.
diff --git a/tools/gn/scope_per_file_provider.cc b/tools/gn/scope_per_file_provider.cc
index 1f4bc25..c2fdd3e 100644
--- a/tools/gn/scope_per_file_provider.cc
+++ b/tools/gn/scope_per_file_provider.cc
@@ -7,7 +7,6 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
-#include "tools/gn/toolchain_manager.h"
#include "tools/gn/value.h"
#include "tools/gn/variables.h"
@@ -50,10 +49,8 @@ const Value* ScopePerFileProvider::GetCurrentToolchain() {
const Value* ScopePerFileProvider::GetDefaultToolchain() {
if (!default_toolchain_) {
- const ToolchainManager& toolchain_manager =
- scope_->settings()->build_settings()->toolchain_manager();
default_toolchain_.reset(new Value(NULL,
- toolchain_manager.GetDefaultToolchainUnlocked().GetUserVisibleName(
+ scope_->settings()->default_toolchain_label().GetUserVisibleName(
false)));
}
return default_toolchain_.get();
diff --git a/tools/gn/script_target_generator.cc b/tools/gn/script_target_generator.cc
index 272061c..8f414f4 100644
--- a/tools/gn/script_target_generator.cc
+++ b/tools/gn/script_target_generator.cc
@@ -7,16 +7,18 @@
#include "tools/gn/build_settings.h"
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
+#include "tools/gn/parse_tree.h"
#include "tools/gn/scope.h"
#include "tools/gn/value.h"
#include "tools/gn/value_extractors.h"
#include "tools/gn/variables.h"
-ScriptTargetGenerator::ScriptTargetGenerator(Target* target,
- Scope* scope,
- const Token& function_token,
- Err* err)
- : TargetGenerator(target, scope, function_token, err) {
+ScriptTargetGenerator::ScriptTargetGenerator(
+ Target* target,
+ Scope* scope,
+ const FunctionCallNode* function_call,
+ Err* err)
+ : TargetGenerator(target, scope, function_call, err) {
}
ScriptTargetGenerator::~ScriptTargetGenerator() {
@@ -62,7 +64,7 @@ void ScriptTargetGenerator::FillScript() {
// if it doesn't have one.
const Value* value = scope_->GetValue(variables::kScript, true);
if (!value) {
- *err_ = Err(function_token_, "This target type requires a \"script\".");
+ *err_ = Err(function_call_, "This target type requires a \"script\".");
return;
}
if (!value->VerifyTypeIs(Value::STRING, err_))
diff --git a/tools/gn/script_target_generator.h b/tools/gn/script_target_generator.h
index f693145..4d79e51 100644
--- a/tools/gn/script_target_generator.h
+++ b/tools/gn/script_target_generator.h
@@ -13,7 +13,7 @@ class ScriptTargetGenerator : public TargetGenerator {
public:
ScriptTargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err);
virtual ~ScriptTargetGenerator();
@@ -29,4 +29,3 @@ class ScriptTargetGenerator : public TargetGenerator {
};
#endif // TOOLS_GN_SCRIPT_TARGET_GENERATOR_H_
-
diff --git a/tools/gn/secondary/base/BUILD.gn b/tools/gn/secondary/base/BUILD.gn
index 0f104a3..d6dc507 100644
--- a/tools/gn/secondary/base/BUILD.gn
+++ b/tools/gn/secondary/base/BUILD.gn
@@ -260,7 +260,6 @@ component("base") {
"mac/sdk_forward_declarations.h",
"memory/aligned_memory.cc",
"memory/aligned_memory.h",
- "memory/discardable_memory.cc",
"memory/discardable_memory.h",
"memory/discardable_memory_android.cc",
"memory/discardable_memory_mac.cc",
@@ -880,8 +879,6 @@ static_library("test_support_base") {
"test/expectations/parser.h",
"test/gtest_xml_util.cc",
"test/gtest_xml_util.h",
- "test/launcher/parallel_test_launcher.cc",
- "test/launcher/parallel_test_launcher.h",
"test/launcher/test_launcher.cc",
"test/launcher/test_launcher.h",
"test/launcher/test_result.cc",
diff --git a/tools/gn/settings.cc b/tools/gn/settings.cc
index b8de976..2715829 100644
--- a/tools/gn/settings.cc
+++ b/tools/gn/settings.cc
@@ -11,7 +11,6 @@
Settings::Settings(const BuildSettings* build_settings,
const std::string& output_subdir_name)
: build_settings_(build_settings),
- is_default_(false),
import_manager_(),
base_config_(this),
greedy_target_generation_(false) {
@@ -23,6 +22,7 @@ Settings::Settings(const BuildSettings* build_settings,
toolchain_output_subdir_.value().append(output_subdir_name);
toolchain_output_subdir_.value().push_back('/');
+ DCHECK(!build_settings->build_dir().is_null());
toolchain_output_dir_ = SourceDir(build_settings->build_dir().value() +
toolchain_output_subdir_.value());
}
@@ -44,5 +44,3 @@ Settings::Settings(const BuildSettings* build_settings,
Settings::~Settings() {
}
-
-
diff --git a/tools/gn/settings.h b/tools/gn/settings.h
index df6a3d6..f2e8925 100644
--- a/tools/gn/settings.h
+++ b/tools/gn/settings.h
@@ -46,9 +46,17 @@ class Settings {
const Label& toolchain_label() const { return toolchain_label_; }
void set_toolchain_label(const Label& l) { toolchain_label_ = l; }
+ const Label& default_toolchain_label() const {
+ return default_toolchain_label_;
+ }
+ void set_default_toolchain_label(const Label& default_label) {
+ default_toolchain_label_ = default_label;
+ }
+
// Indicates if this corresponds to the default toolchain.
- bool is_default() const { return is_default_; }
- void set_is_default(bool id) { is_default_ = id; }
+ bool is_default() const {
+ return toolchain_label_ == default_toolchain_label_;
+ }
bool IsMac() const { return target_os_ == MAC; }
bool IsLinux() const { return target_os_ == LINUX; }
@@ -94,7 +102,7 @@ class Settings {
const BuildSettings* build_settings_;
Label toolchain_label_;
- bool is_default_;
+ Label default_toolchain_label_;
TargetOS target_os_;
diff --git a/tools/gn/setup.cc b/tools/gn/setup.cc
index ae02bf1..4f18f57 100644
--- a/tools/gn/setup.cc
+++ b/tools/gn/setup.cc
@@ -6,6 +6,7 @@
#include <stdlib.h>
+#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
@@ -141,17 +142,37 @@ base::FilePath ExtractDepotToolsFromPath() {
}
#endif
+// Called on any thread. Post the item to the builder on the main thread.
+void ItemDefinedCallback(base::MessageLoop* main_loop,
+ scoped_refptr<Builder> builder,
+ scoped_ptr<Item> item) {
+ DCHECK(item);
+ main_loop->PostTask(FROM_HERE, base::Bind(&Builder::ItemDefined, builder,
+ base::Passed(&item)));
+}
+
+void DecrementWorkCount() {
+ g_scheduler->DecrementWorkCount();
+}
+
} // namespace
// CommonSetup -----------------------------------------------------------------
CommonSetup::CommonSetup()
- : check_for_bad_items_(true) {
+ : build_settings_(),
+ loader_(new LoaderImpl(&build_settings_)),
+ builder_(new Builder(loader_.get())),
+ check_for_bad_items_(true) {
+ loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
}
CommonSetup::CommonSetup(const CommonSetup& other)
: build_settings_(other.build_settings_),
+ loader_(new LoaderImpl(&build_settings_)),
+ builder_(new Builder(loader_.get())),
check_for_bad_items_(other.check_for_bad_items_) {
+ loader_->set_complete_callback(base::Bind(&DecrementWorkCount));
}
CommonSetup::~CommonSetup() {
@@ -159,15 +180,16 @@ CommonSetup::~CommonSetup() {
void CommonSetup::RunPreMessageLoop() {
// Load the root build file.
- build_settings_.toolchain_manager().StartLoadingUnlocked(
- SourceFile("//BUILD.gn"));
+ loader_->Load(SourceFile("//BUILD.gn"), Label());
+
+ // Will be decremented with the loader is drained.
+ g_scheduler->IncrementWorkCount();
}
bool CommonSetup::RunPostMessageLoop() {
Err err;
if (check_for_bad_items_) {
- err = build_settings_.item_tree().CheckForBadItems();
- if (err.has_error()) {
+ if (!builder_->CheckForBadItems(&err)) {
err.PrintToStdout();
return false;
}
@@ -195,6 +217,12 @@ Setup::Setup()
empty_settings_(&empty_build_settings_, std::string()),
dotfile_scope_(&empty_settings_) {
empty_settings_.set_toolchain_label(Label());
+ build_settings_.set_item_defined_callback(
+ base::Bind(&ItemDefinedCallback, scheduler_.main_loop(), builder_));
+
+ // The scheduler's main loop wasn't created when the Loader was created, so
+ // we need to set it now.
+ loader_->set_main_loop(scheduler_.main_loop());
}
Setup::~Setup() {
@@ -411,8 +439,11 @@ bool Setup::FillOtherConfig(const CommandLine& cmdline) {
// DependentSetup --------------------------------------------------------------
-DependentSetup::DependentSetup(const Setup& main_setup)
+DependentSetup::DependentSetup(Setup& main_setup)
: CommonSetup(main_setup) {
+ build_settings_.set_item_defined_callback(
+ base::Bind(&ItemDefinedCallback, main_setup.scheduler().main_loop(),
+ builder_));
}
DependentSetup::~DependentSetup() {
diff --git a/tools/gn/setup.h b/tools/gn/setup.h
index 8b6d714..a5f9e76 100644
--- a/tools/gn/setup.h
+++ b/tools/gn/setup.h
@@ -11,6 +11,8 @@
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "tools/gn/build_settings.h"
+#include "tools/gn/builder.h"
+#include "tools/gn/loader.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/scope.h"
#include "tools/gn/settings.h"
@@ -33,6 +35,8 @@ class CommonSetup {
void set_check_for_bad_items(bool s) { check_for_bad_items_ = s; }
BuildSettings& build_settings() { return build_settings_; }
+ Builder* builder() { return builder_.get(); }
+ LoaderImpl* loader() { return loader_.get(); }
protected:
CommonSetup();
@@ -43,8 +47,9 @@ class CommonSetup {
void RunPreMessageLoop();
bool RunPostMessageLoop();
- protected:
BuildSettings build_settings_;
+ scoped_refptr<LoaderImpl> loader_;
+ scoped_refptr<Builder> builder_;
bool check_for_bad_items_;
@@ -121,7 +126,7 @@ class Setup : public CommonSetup {
// so that the main setup executes the message loop, but both are run.
class DependentSetup : public CommonSetup {
public:
- DependentSetup(const Setup& main_setup);
+ DependentSetup(Setup& main_setup);
virtual ~DependentSetup();
// These are the two parts of Run() in the regular setup, not including the
diff --git a/tools/gn/target.cc b/tools/gn/target.cc
index 28dcf91..b12d284 100644
--- a/tools/gn/target.cc
+++ b/tools/gn/target.cc
@@ -60,9 +60,7 @@ Target::Target(const Settings* settings, const Label& label)
: Item(settings, label),
output_type_(UNKNOWN),
hard_dep_(false),
- external_(false),
- generated_(false),
- generator_function_(NULL) {
+ external_(false) {
}
Target::~Target() {
@@ -152,23 +150,6 @@ void Target::OnResolved() {
// pulled from G to A in case G has configs directly on it).
PullDependentTargetInfo(&unique_configs);
}
-
- // Mark as resolved.
- if (!settings()->build_settings()->target_resolved_callback().is_null()) {
- g_scheduler->ScheduleWork(base::Bind(&TargetResolvedThunk,
- settings()->build_settings()->target_resolved_callback(),
- this));
- }
-}
-
-bool Target::HasBeenGenerated() const {
- return generated_;
-}
-
-void Target::SetGenerated(const Token* token) {
- DCHECK(!generated_);
- generated_ = true;
- generator_function_ = token;
}
bool Target::IsLinkable() const {
@@ -190,11 +171,8 @@ void Target::PullDependentTargetInfo(std::set<const Config*>* unique_configs) {
inherited_libraries_.insert(dep);
// Inherited libraries and flags are inherited across static library
- // boundaries. For external targets, assume that the external_link_deps
- // will take care of this.
- if ((!dep->external() ||
- !settings()->build_settings()->using_external_generator()) &&
- dep->output_type() != SHARED_LIBRARY &&
+ // boundaries.
+ if (dep->output_type() != SHARED_LIBRARY &&
dep->output_type() != EXECUTABLE) {
const std::set<const Target*> inherited = dep->inherited_libraries();
for (std::set<const Target*>::const_iterator i = inherited.begin();
diff --git a/tools/gn/target.h b/tools/gn/target.h
index 13b278c..58f0de1 100644
--- a/tools/gn/target.h
+++ b/tools/gn/target.h
@@ -51,13 +51,6 @@ class Target : public Item {
virtual const Target* AsTarget() const OVERRIDE;
virtual void OnResolved() OVERRIDE;
- // This flag indicates if we've run the TargetGenerator for this target to
- // fill out the rest of the values. Once we've done this, we save the
- // location of the function that started the generating so that we can detect
- // duplicate declarations.
- bool HasBeenGenerated() const;
- void SetGenerated(const Token* token);
-
OutputType output_type() const { return output_type_; }
void set_output_type(OutputType t) { output_type_ = t; }
@@ -192,9 +185,6 @@ class Target : public Item {
SourceFile gyp_file_;
- bool generated_;
- const Token* generator_function_; // Who generated this: for error messages.
-
DISALLOW_COPY_AND_ASSIGN(Target);
};
diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc
index 3bb51eb..81c4394 100644
--- a/tools/gn/target_generator.cc
+++ b/tools/gn/target_generator.cc
@@ -12,12 +12,10 @@
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/functions.h"
#include "tools/gn/group_target_generator.h"
-#include "tools/gn/item_node.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/scope.h"
#include "tools/gn/script_target_generator.h"
-#include "tools/gn/target_manager.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
#include "tools/gn/value_extractors.h"
@@ -25,11 +23,11 @@
TargetGenerator::TargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err)
: target_(target),
scope_(scope),
- function_token_(function_token),
+ function_call_(function_call),
err_(err) {
}
@@ -43,26 +41,19 @@ void TargetGenerator::Run() {
FillDependencies();
FillGypFile();
- // To type-specific generation.
+ // Do type-specific generation.
DoRun();
-
- // Mark the target as complete.
- if (!err_->has_error()) {
- target_->SetGenerated(&function_token_);
- GetBuildSettings()->target_manager().TargetGenerationComplete(
- target_->label(), err_);
- }
}
// static
void TargetGenerator::GenerateTarget(Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
const std::vector<Value>& args,
const std::string& output_type,
Err* err) {
// Name is the argument to the function.
if (args.size() != 1u || args[0].type() != Value::STRING) {
- *err = Err(function_token,
+ *err = Err(function_call,
"Target generator requires one string argument.",
"Otherwise I'm not sure what to call this target.");
return;
@@ -75,44 +66,44 @@ void TargetGenerator::GenerateTarget(Scope* scope,
toolchain_label.dir(), toolchain_label.name());
if (g_scheduler->verbose_logging())
- g_scheduler->Log("Generating target", label.GetUserVisibleName(true));
+ g_scheduler->Log("Defining target", label.GetUserVisibleName(true));
- Target* target =
- scope->settings()->build_settings()->target_manager().GetTarget(
- label, function_token.range(), NULL, err);
- if (err->has_error())
- return;
+ scoped_ptr<Target> target(new Target(scope->settings(), label));
+ target->set_defined_from(function_call);
// Create and call out to the proper generator.
if (output_type == functions::kCopy) {
- CopyTargetGenerator generator(target, scope, function_token, err);
+ CopyTargetGenerator generator(target.get(), scope, function_call, err);
generator.Run();
} else if (output_type == functions::kCustom) {
- ScriptTargetGenerator generator(target, scope, function_token, err);
+ ScriptTargetGenerator generator(target.get(), scope, function_call, err);
generator.Run();
} else if (output_type == functions::kExecutable) {
- BinaryTargetGenerator generator(target, scope, function_token,
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
Target::EXECUTABLE, err);
generator.Run();
} else if (output_type == functions::kGroup) {
- GroupTargetGenerator generator(target, scope, function_token, err);
+ GroupTargetGenerator generator(target.get(), scope, function_call, err);
generator.Run();
} else if (output_type == functions::kSharedLibrary) {
- BinaryTargetGenerator generator(target, scope, function_token,
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
Target::SHARED_LIBRARY, err);
generator.Run();
} else if (output_type == functions::kSourceSet) {
- BinaryTargetGenerator generator(target, scope, function_token,
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
Target::SOURCE_SET, err);
generator.Run();
} else if (output_type == functions::kStaticLibrary) {
- BinaryTargetGenerator generator(target, scope, function_token,
+ BinaryTargetGenerator generator(target.get(), scope, function_call,
Target::STATIC_LIBRARY, err);
generator.Run();
} else {
- *err = Err(function_token, "Not a known output type",
+ *err = Err(function_call, "Not a known output type",
"I am very confused.");
}
+
+ if (!err->has_error())
+ scope->settings()->build_settings()->ItemDefined(target.PassAs<Item>());
}
const BuildSettings* TargetGenerator::GetBuildSettings() const {
@@ -155,8 +146,6 @@ void TargetGenerator::FillDependentConfigs() {
}
void TargetGenerator::FillData() {
- // TODO(brettW) hook this up to the constant when we have cleaned up
- // how data files are used.
const Value* value = scope_->GetValue(variables::kData, true);
if (!value)
return;
@@ -229,88 +218,30 @@ void TargetGenerator::FillOutputs() {
target_->script_values().outputs().swap(outputs);
}
-void TargetGenerator::SetToolchainDependency() {
- // TODO(brettw) currently we lock separately for each config, dep, and
- // toolchain we add which is bad! Do this in one lock.
- ItemTree* tree = &GetBuildSettings()->item_tree();
- base::AutoLock lock(tree->lock());
- ItemNode* tc_node =
- tree->GetExistingNodeLocked(ToolchainLabelForScope(scope_));
- target_->item_node()->AddDependency(
- GetBuildSettings(), function_token_.range(), tc_node, err_);
-}
-
void TargetGenerator::FillGenericConfigs(const char* var_name,
LabelConfigVector* dest) {
const Value* value = scope_->GetValue(var_name, true);
- if (!value)
- return;
- if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), dest, err_))
- return;
-
- for (size_t i = 0; i < dest->size(); i++) {
- LabelConfigPair& cur = (*dest)[i];
- cur.ptr = Config::GetConfig(scope_->settings(),
- value->list_value()[i].origin()->GetRange(),
- cur.label, target_, err_);
- if (err_->has_error())
- return;
+ if (value) {
+ ExtractListOfLabels(*value, scope_->GetSourceDir(),
+ ToolchainLabelForScope(scope_), dest, err_);
}
}
void TargetGenerator::FillGenericDeps(const char* var_name,
LabelTargetVector* dest) {
const Value* value = scope_->GetValue(var_name, true);
- if (!value)
- return;
- if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), dest, err_))
- return;
-
- for (size_t i = 0; i < dest->size(); i++) {
- LabelTargetPair& cur = (*dest)[i];
- cur.ptr = GetBuildSettings()->target_manager().GetTarget(
- cur.label, value->list_value()[i].origin()->GetRange(), target_, err_);
- if (err_->has_error())
- return;
+ if (value) {
+ ExtractListOfLabels(*value, scope_->GetSourceDir(),
+ ToolchainLabelForScope(scope_), dest, err_);
}
}
void TargetGenerator::FillForwardDependentConfigs() {
const Value* value = scope_->GetValue(
variables::kForwardDependentConfigsFrom, true);
- if (!value)
- return;
-
- LabelTargetVector& dest = target_->forward_dependent_configs();
- if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
- ToolchainLabelForScope(scope_), &dest, err_))
- return;
-
- // We currently assume that the list is very small and do a brute-force
- // search in the deps for the labeled target. This could be optimized.
- const LabelTargetVector& deps = target_->deps();
- std::vector<const Target*> forward_from_list;
- for (size_t dest_index = 0; dest_index < dest.size(); dest_index++) {
- LabelTargetPair& cur_dest = dest[dest_index];
- for (size_t dep_index = 0; dep_index < deps.size(); dep_index++) {
- if (deps[dep_index].label == cur_dest.label) {
- cur_dest.ptr = deps[dep_index].ptr;
- break;
- }
- }
- if (!cur_dest.ptr) {
- *err_ = Err(cur_dest.origin,
- "Can't forward from this target.",
- "forward_dependent_configs_from must contain a list of labels that\n"
- "must all appear in the deps of the same target.");
- return;
- }
+ if (value) {
+ ExtractListOfLabels(*value, scope_->GetSourceDir(),
+ ToolchainLabelForScope(scope_),
+ &target_->forward_dependent_configs(), err_);
}
}
-
-
-
-
-
diff --git a/tools/gn/target_generator.h b/tools/gn/target_generator.h
index 583e57f..6fa39b1 100644
--- a/tools/gn/target_generator.h
+++ b/tools/gn/target_generator.h
@@ -15,9 +15,9 @@
class BuildSettings;
class Err;
+class FunctionCallNode;
class Location;
class Scope;
-class Token;
class Value;
// Fills the variables in a Target object from a Scope (the result of a script
@@ -28,16 +28,16 @@ class TargetGenerator {
public:
TargetGenerator(Target* target,
Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
Err* err);
~TargetGenerator();
void Run();
- // The function token is the token of the function name of the generator for
- // this target. err() will be set on failure.
+ // The function call is the parse tree node that invoked the target.
+ // err() will be set on failure.
static void GenerateTarget(Scope* scope,
- const Token& function_token,
+ const FunctionCallNode* function_call,
const std::vector<Value>& args,
const std::string& output_type,
Err* err);
@@ -54,13 +54,9 @@ class TargetGenerator {
void FillExternal();
void FillOutputs();
- // Sets the current toolchain as a dependecy of this target. All targets with
- // a dependency on the toolchain should call this function.
- void SetToolchainDependency();
-
Target* target_;
Scope* scope_;
- const Token& function_token_;
+ const FunctionCallNode* function_call_;
Err* err_;
private:
diff --git a/tools/gn/target_manager.cc b/tools/gn/target_manager.cc
deleted file mode 100644
index eb5c86d..0000000
--- a/tools/gn/target_manager.cc
+++ /dev/null
@@ -1,129 +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/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);
- if (settings->greedy_target_generation()) {
- if (!target_node->SetShouldGenerate(build_settings_, err))
- return NULL;
- }
- 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);
-
- } 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 " +
- target_node->item()->GetItemTypeName());
- 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) {
- ItemNode* from_node = dep_from->item_node();
- if (!from_node->AddDependency(build_settings_, specified_from_here,
- target_node, err))
- return NULL;
- }
-
- return target;
-}
-
-bool TargetManager::TargetGenerationComplete(const Label& label,
- Err* err) {
- base::AutoLock lock(build_settings_->item_tree().lock());
- return build_settings_->item_tree().MarkItemDefinedLocked(
- build_settings_, label, err);
-}
-
-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);
- }
-}
diff --git a/tools/gn/target_manager.h b/tools/gn/target_manager.h
deleted file mode 100644
index f7459ff..0000000
--- a/tools/gn/target_manager.h
+++ /dev/null
@@ -1,69 +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.
-
-#ifndef TOOLS_GN_TARGET_MANAGER_H_
-#define TOOLS_GN_TARGET_MANAGER_H_
-
-#include <set>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/containers/hash_tables.h"
-#include "base/synchronization/lock.h"
-#include "tools/gn/target.h"
-
-class BuildSettings;
-class Err;
-class ItemTree;
-class LocationRange;
-class ToolchainManager;
-class Value;
-
-// Manages all the targets in the system. This integrates with the ItemTree
-// to manage the target-specific rules and creation.
-//
-// This class is threadsafe.
-class TargetManager {
- public:
- explicit TargetManager(const BuildSettings* settings);
- ~TargetManager();
-
- // Gets a reference to a named target. The given target name is created if
- // it doesn't exist.
- //
- // The label should be fully specified in that it should include an
- // explicit toolchain.
- //
- // |specified_from_here| should indicate the dependency or the target
- // generator causing this access for error message generation.
- //
- // |dep_from| should be set when a target is getting a dep that it depends
- // on. |dep_from| indicates the target that specified the dependency. It
- // will be used to track outstanding dependencies so we can know when the
- // target and all of its dependencies are complete. It should be null when
- // getting a target for other reasons.
- //
- // On failure, |err| will be set.
- //
- // The returned pointer must not be dereferenced until it's generated, since
- // it could be being generated on another thread.
- Target* GetTarget(const Label& label,
- const LocationRange& specified_from_here,
- Target* dep_from,
- Err* err);
-
- // Called by a target when it has been loaded from the .gin file. Its
- // dependencies may or may not be resolved yet.
- bool TargetGenerationComplete(const Label& label, Err* err);
-
- // Returns a list of all targets.
- void GetAllTargets(std::vector<const Target*>* all_targets) const;
-
- private:
- const BuildSettings* build_settings_;
-
- DISALLOW_COPY_AND_ASSIGN(TargetManager);
-};
-
-#endif // TOOLS_GN_TARGET_MANAGER_H
diff --git a/tools/gn/target_manager_unittest.cc b/tools/gn/target_manager_unittest.cc
deleted file mode 100644
index a1a3ad2..0000000
--- a/tools/gn/target_manager_unittest.cc
+++ /dev/null
@@ -1,95 +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 "testing/gtest/include/gtest/gtest.h"
-#include "tools/gn/err.h"
-#include "tools/gn/settings.h"
-#include "tools/gn/target_manager.h"
-#include "tools/gn/value.h"
-
-/* TODO(brettw) make this compile again
-namespace {
-
-class TestTargetManagerDelegate : public TargetManager::Delegate {
- public:
- TestTargetManagerDelegate() {}
-
- virtual bool ScheduleInvocation(ToolchainManager* toolchain_manager,
- const LocationRange& origin,
- const Label& toolchain_name,
- const SourceDir& dir,
- Err* err) OVERRIDE {
- invokes.push_back(dir.value());
- return true;
- }
- virtual void ScheduleTargetFileWrite(const Target* target) {
- writes.push_back(target);
- }
-
- std::vector<std::string> invokes;
- std::vector<const Target*> writes;
-};
-
-} // namespace
-
-TEST(TargetManager, ResolveDeps) {
- TestTargetManagerDelegate ttmd;
- BuildSettings build_settings(&ttmd);
-
- TargetManager& target_manager = build_settings.target_manager();
-
- SourceDir tc_dir("/chrome/");
- std::string tc_name("toolchain");
-
- // Get a root target. This should not invoke anything.
- Err err;
- Label chromelabel(SourceDir("/chrome/"), "chrome", tc_dir, tc_name);
- Target* chrome = target_manager.GetTarget(
- chromelabel, LocationRange(), NULL, &err);
- EXPECT_EQ(0u, ttmd.invokes.size());
-
- // Declare it has a dependency on content1 and 2. We should get one
- // invocation of the content build file.
- Label content1label(SourceDir("/content/"), "content1", tc_dir, tc_name);
- Target* content1 = target_manager.GetTarget(
- content1label, LocationRange(), chrome, &err);
- EXPECT_EQ(1u, ttmd.invokes.size());
-
- Label content2label(SourceDir("/content/"), "content2", tc_dir, tc_name);
- Target* content2 = target_manager.GetTarget(
- content2label, LocationRange(), chrome, &err);
- EXPECT_EQ(2u, ttmd.invokes.size());
-
- // Declare chrome has a depdency on base, this should load it.
- Label baselabel(SourceDir("/base/"), "base", tc_dir, tc_name);
- Target* base1 = target_manager.GetTarget(
- baselabel, LocationRange(), chrome, &err);
- EXPECT_EQ(3u, ttmd.invokes.size());
-
- // Declare content1 has a dependency on base.
- Target* base2 = target_manager.GetTarget(
- baselabel, LocationRange(), content1, &err);
- EXPECT_EQ(3u, ttmd.invokes.size());
- EXPECT_EQ(base1, base2);
-
- // Mark content1 and chrome as done. They have unresolved depdendencies so
- // shouldn't be written out yet.
- target_manager.TargetGenerationComplete(content1label);
- target_manager.TargetGenerationComplete(chromelabel);
- EXPECT_EQ(0u, ttmd.writes.size());
-
- // Mark content2 as done. It has no dependencies so should be written.
- target_manager.TargetGenerationComplete(content2label);
- ASSERT_EQ(1u, ttmd.writes.size());
- EXPECT_EQ(content2label, ttmd.writes[0]->label());
-
- // Mark base as complete. It should have caused itself, content1 and then
- // chrome to be written.
- target_manager.TargetGenerationComplete(baselabel);
- ASSERT_EQ(4u, ttmd.writes.size());
- EXPECT_EQ(baselabel, ttmd.writes[1]->label());
- EXPECT_EQ(content1label, ttmd.writes[2]->label());
- EXPECT_EQ(chromelabel, ttmd.writes[3]->label());
-}
-*/
diff --git a/tools/gn/test_with_scope.cc b/tools/gn/test_with_scope.cc
index e149e92..97eb2fb 100644
--- a/tools/gn/test_with_scope.cc
+++ b/tools/gn/test_with_scope.cc
@@ -12,7 +12,7 @@ TestWithScope::TestWithScope()
build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
settings_.set_toolchain_label(toolchain_.label());
- settings_.set_is_default(true);
+ settings_.set_default_toolchain_label(toolchain_.label());
}
TestWithScope::~TestWithScope() {
diff --git a/tools/gn/toolchain_manager.cc b/tools/gn/toolchain_manager.cc
deleted file mode 100644
index 4edf212..0000000
--- a/tools/gn/toolchain_manager.cc
+++ /dev/null
@@ -1,593 +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 "build/build_config.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"
-#include "tools/gn/trace.h"
-
-// How toolchain loading works
-// ---------------------------
-// When we start loading a build, we'll load the build config file and that
-// will call set_default_toolchain. We'll schedule a load of the file
-// containing the default toolchain definition, and can do this in parallel
-// with all other build files. Targets will have an implicit dependency on the
-// toolchain so we won't write out any files until we've loaded the toolchain
-// definition.
-//
-// When we see a reference to a target using a different toolchain, it gets
-// more complicated. In this case, the toolchain definition contains arguments
-// to pass into the build config file when it is invoked in the context of that
-// toolchain. As a result, we have to actually see the definition of the
-// toolchain before doing anything else.
-//
-// So when we see a reference to a non-default toolchain we do the following:
-//
-// 1. Schedule a load of the file containing the toolchain definition, if it
-// isn't loaded already.
-// 2. When the toolchain definition is loaded, we schedule a load of the
-// build config file in the context of that toolchain. We'll use the
-// arguments from the toolchain definition to execute it.
-// 3. When the build config is set up, then we can load all of the individual
-// buildfiles in the context of that config that we require.
-
-namespace {
-
-enum ToolchainState {
- // Toolchain settings have not requested to be loaded. This means we
- // haven't seen any targets that require this toolchain yet. This means that
- // we have seen a toolchain definition, but no targets that use it. Not
- // loading the settings automatically allows you to define a bunch of
- // toolchains and potentially not use them without much overhead.
- TOOLCHAIN_NOT_LOADED,
-
- // The toolchain definition for non-default toolchains has been scheduled
- // to be loaded but has not completed. When this is done, we can load the
- // settings file. This is needed to get the arguments out of the toolchain
- // definition. This is skipped for the default toolchain which has no
- // arguments (see summary above).
- TOOLCHAIN_DEFINITION_LOADING,
-
- // The settings have been scheduled to be loaded but have not completed.
- TOOLCHAIN_SETTINGS_LOADING,
-
- // The settings are done being loaded.
- TOOLCHAIN_SETTINGS_LOADED
-};
-
-SourceFile DirToBuildFile(const SourceDir& dir) {
- return SourceFile(dir.value() + "BUILD.gn");
-}
-
-} // namespace
-
-struct ToolchainManager::Info {
- Info(const BuildSettings* build_settings,
- const Label& toolchain_name,
- const std::string& output_subdir_name)
- : state(TOOLCHAIN_NOT_LOADED),
- settings(build_settings, output_subdir_name),
- toolchain(new Toolchain(&settings, toolchain_name)),
- toolchain_set(false),
- toolchain_file_loaded(false),
- item_node(NULL) {
- settings.set_toolchain_label(toolchain_name);
- }
-
- ~Info() {
- if (!item_node) // See toolchain definition for more.
- delete toolchain;
- }
-
- // 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);
- }
- }
-
- ToolchainState state;
-
- // The first place in the build that we saw a reference for this toolchain.
- // This allows us to report errors if it can't be loaded and blame some
- // reasonable place of the code. This will be empty for the default toolchain.
- LocationRange specified_from;
-
- // 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;
-
- // When we create an item node, this pointer will be owned by that node
- // so it's lifetime is managed by the dependency graph. Before we've created
- // the ItemNode, this class has to takre responsibility for this pointer.
- Toolchain* toolchain;
- bool toolchain_set;
- LocationRange toolchain_definition_location;
-
- // Set when the file corresponding to the toolchain definition is loaded.
- // This will normally be set right after "toolchain_set". However, if the
- // toolchain definition is missing, the file might be marked loaded but the
- // toolchain definition could still be unset.
- bool toolchain_file_loaded;
-
- // While state == TOOLCHAIN_SETTINGS_LOADING || TOOLCHAIN_DEFINITION_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);
-
- if (!ScheduleBuildConfigLoadLocked(info, true, &err))
- g_scheduler->FailWithError(err);
-}
-
-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());
-
- // Save the toolchain. We can just overwrite our definition.
- *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));
-
- // If there's no specified toolchain name, use the default.
- ToolchainMap::iterator found;
- if (toolchain_name.is_null())
- found = toolchains_.find(default_toolchain_);
- else
- 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);
-
- switch (info->state) {
- case TOOLCHAIN_NOT_LOADED:
- // Toolchain needs to be loaded. Start loading it and push this buildfile
- // on the wait queue. The actual toolchain build file will have been
- // scheduled to be loaded by LoadNewToolchainLocked() above, so we just
- // need to request that the build settings be loaded when that toolchain
- // file is done executing (recall toolchain files are executed in the
- // context of the default toolchain, which is why we need to do the extra
- // Info lookup in the make_pair).
- DCHECK(!default_toolchain_.is_null());
- pending_build_config_map_[
- std::make_pair(DirToBuildFile(toolchain_name.dir()),
- toolchains_[default_toolchain_])] =
- info;
- info->scheduled_invocations[build_file] = specified_from;
-
- // Transition to the loading state.
- info->specified_from = specified_from;
- info->state = TOOLCHAIN_DEFINITION_LOADING;
- return true;
-
- case TOOLCHAIN_DEFINITION_LOADING:
- case TOOLCHAIN_SETTINGS_LOADING:
- // Toolchain is in the process of loading, push this buildfile on the
- // wait queue to run when the config is ready.
- info->scheduled_invocations[build_file] = specified_from;
- return true;
-
- case TOOLCHAIN_SETTINGS_LOADED:
- // Everything is ready, just schedule the build file to load.
- return ScheduleBackgroundInvoke(info, specified_from, build_file, err);
-
- default:
- NOTREACHED();
- return false;
- }
-}
-
-// 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(&info->settings, default_toolchain_);
- info->settings.set_is_default(true);
- info->settings.set_toolchain_label(default_toolchain_);
- info->EnsureItemNode();
-
- // The default toolchain is loaded in greedy mode so all targets we
- // encounter are generated. Non-default toolchain settings stay in non-greedy
- // so we only generate the minimally required set.
- info->settings.set_greedy_target_generation(true);
-
- // 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);
-}
-
-bool ToolchainManager::ScheduleBackgroundInvoke(
- Info* info,
- const LocationRange& specified_from,
- const SourceFile& build_file,
- Err* err) {
- 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;
- }
- return true;
-}
-
-bool ToolchainManager::ScheduleBuildConfigLoadLocked(Info* info,
- bool is_default,
- Err* err) {
- GetLock().AssertAcquired();
-
- g_scheduler->IncrementWorkCount();
- if (!g_scheduler->input_file_manager()->AsyncLoadFile(
- info->specified_from, build_settings_,
- build_settings_->build_config_file(),
- base::Bind(&ToolchainManager::BackgroundLoadBuildConfig,
- base::Unretained(this), info, is_default),
- err)) {
- g_scheduler->DecrementWorkCount();
- return false;
- }
- info->state = TOOLCHAIN_SETTINGS_LOADING;
- return true;
-}
-
-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();
- base_config->set_source_dir(SourceDir("//"));
-
- info->settings.build_settings()->build_args().SetupRootScope(
- base_config, info->toolchain->args());
-
- base_config->SetProcessingBuildConfig();
- if (is_default)
- base_config->SetProcessingDefaultBuildConfig();
-
- ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE,
- info->settings.build_settings()->build_config_file().value());
- trace.SetToolchain(info->settings.toolchain_label());
-
- const BlockNode* root_block = root->AsBlock();
- Err err;
- root_block->ExecuteBlockInScope(base_config, &err);
-
- trace.Done();
-
- 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) {
- if (!ScheduleBackgroundInvoke(info, i->second, i->first, &err)) {
- g_scheduler->FailWithError(err);
- 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() + " with toolchain " +
- info->toolchain->label().GetUserVisibleName(false));
- }
-
- Scope our_scope(info->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(info->settings.toolchain_label());
-
- Err err;
- root->Execute(&our_scope, &err);
- if (err.has_error())
- g_scheduler->FailWithError(err);
-
- trace.Done();
-
- {
- // Check to see if any build config invocations depend on this file and
- // invoke them.
- base::AutoLock lock(GetLock());
- BuildConfigInvokeMap::iterator found_file =
- pending_build_config_map_.find(std::make_pair(file_name, info));
- if (found_file != pending_build_config_map_.end()) {
- // The toolchain state should be waiting on the definition, which
- // should be the thing we just loaded.
- Info* info_to_load = found_file->second;
- DCHECK(info_to_load->state == TOOLCHAIN_DEFINITION_LOADING);
- DCHECK(!info_to_load->toolchain_file_loaded);
- info_to_load->toolchain_file_loaded = true;
-
- if (!ScheduleBuildConfigLoadLocked(info_to_load, false, &err))
- g_scheduler->FailWithError(err);
- pending_build_config_map_.erase(found_file);
- }
- }
- }
-
- g_scheduler->DecrementWorkCount();
-}
-
-base::Lock& ToolchainManager::GetLock() const {
- return build_settings_->item_tree().lock();
-}
diff --git a/tools/gn/toolchain_manager.h b/tools/gn/toolchain_manager.h
deleted file mode 100644
index 9e75f33..0000000
--- a/tools/gn/toolchain_manager.h
+++ /dev/null
@@ -1,170 +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.
-
-#ifndef TOOLS_GN_TOOLCHAIN_MANAGER_H_
-#define TOOLS_GN_TOOLCHAIN_MANAGER_H_
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "base/synchronization/lock.h"
-#include "tools/gn/label.h"
-#include "tools/gn/location.h"
-#include "tools/gn/source_file.h"
-#include "tools/gn/toolchain.h"
-
-class Err;
-class BuildSettings;
-class ParseNode;
-class Settings;
-
-// The toolchain manager manages the mapping of toolchain names to the
-// settings and toolchain object. It also loads build files in the context of a
-// toolchain context of a toolchain, and manages running the build config
-// script when necessary.
-//
-// This class uses the lock from the item tree to manage threadsafety. The
-// functions requiring this lock to be held are named "Locked" to make this
-// more clear. The "Unlocked" versions will acquire the lock themselves so will
-// break if you call it while locked. (The rationale behind which is which is
-// just based on the needs of the callers, so it can be changed.) There are two
-// reasons for this:
-//
-// The first is that when resolving a target, we do a bunch of script
-// stuff (slow) and then lookup the target, config, and toolchain dependencies
-// based on that. The options are to do a lock around each dependency lookup
-// or do a lock around the entire operation. Given that there's not a huge
-// amount of work, the "big lock" approach is likely a bit better since it
-// avoids lots of locking overhead.
-//
-// The second reason is that if we had a separate lock here, we would need to
-// lock around creating a new toolchain. But creating a new toolchain involves
-// adding it to the item tree, and this needs to be done atomically to prevent
-// other threads from seeing a partially initialized toolchain. This sets up
-// having deadlock do to acquiring multiple locks, or recursive locking
-// problems.
-class ToolchainManager {
- public:
- ToolchainManager(const BuildSettings* build_settings);
- ~ToolchainManager();
-
- // At the very beginning of processing, this begins loading build files.
- // This will scheduler loadin the default build config and the given build
- // file in that context, going out from there.
- //
- // This returns immediately, you need to run the Scheduler to actually
- // process anything. It's assumed this function is called on the main thread
- // before doing anything, so it does not need locking.
- void StartLoadingUnlocked(const SourceFile& build_file_name);
-
- // Returns the settings object for a given toolchain. This does not
- // schedule loading the given toolchain if it's not loaded yet: you actually
- // need to invoke a target with that toolchain to get that.
- //
- // On error, returns NULL and sets the error.
- const Settings* GetSettingsForToolchainLocked(const LocationRange& from_here,
- const Label& toolchain_name,
- Err* err);
-
- // Returns the toolchain definition or NULL if the toolchain hasn't been
- // defined yet.
- const Toolchain* GetToolchainDefinitionUnlocked(const Label& toolchain_name);
-
- // Sets the default toolchain. If the default toolchain is already set,
- // this function will return false and fill in the given err.
- bool SetDefaultToolchainUnlocked(const Label& dt,
- const LocationRange& defined_from,
- Err* err);
-
- // Returns the default toolchain name. This will be empty if it hasn't been
- // set.
- Label GetDefaultToolchainUnlocked() const;
-
- // Saves the given named toolchain (the name will be taken from the toolchain
- // parameter). This will fail and return false if the given toolchain was
- // already defined. In this case, the given error will be set.
- bool SetToolchainDefinitionLocked(const Toolchain& tc,
- const LocationRange& defined_from,
- Err* err);
-
- // Schedules an invocation of the given file under the given toolchain. The
- // toolchain file will be loaded if necessary. If the toolchain is an is_null
- // label, the default toolchain will be used.
- //
- // The origin should be the node that will be blamed for this invocation if
- // an error occurs. If a synchronous error occurs, the given error will be
- // set and it will return false. If an async error occurs, the error will be
- // sent to the scheduler.
- bool ScheduleInvocationLocked(const LocationRange& origin,
- const Label& toolchain_name,
- const SourceDir& dir,
- Err* err);
-
- private:
- struct Info;
-
- static std::string ToolchainToOutputSubdir(const Label& toolchain_name);
-
- // Creates a new info struct and saves it in the map. A pointer to the
- // struct is returned. No loads are scheduled.
- //
- // If the label is non-empty, the toolchain will be added to the ItemTree
- // so that other nodes can depend on it. THe empty label case is for the
- // default build config file (when the toolchain name isn't known yet). It
- // will be added later.
- //
- // On error, will return NULL and the error will be set.
- Info* LoadNewToolchainLocked(const LocationRange& specified_from,
- const Label& toolchain_name,
- Err* err);
-
- // Fixes up the default toolchain names once they're known when processing
- // the default build config, or throw an error if the default toolchain
- // hasn't been set. See the StartLoading() implementation for more.
- void FixupDefaultToolchainLocked();
-
- // Schedules the buildfile to be invoked for the given toolchain info. The
- // toolchain must be already loaded. Can be called with the lock held or not
- // (it does not use any class vars).
- bool ScheduleBackgroundInvoke(Info* info,
- const LocationRange& specified_from,
- const SourceFile& build_file,
- Err* err);
-
- // Schedules the build config file for the given info to be loaded and
- // invoked. The toolchain definition (for non-default toolchains) should
- // already be loaded.
- bool ScheduleBuildConfigLoadLocked(Info* info, bool is_default, Err* err);
-
- // Invokes the given file for a toolchain with loaded settings. Run on a
- // background thread asynchronously.
- void BackgroundInvoke(const Info* info,
- const SourceFile& file_name,
- const ParseNode* root);
-
- // Loads the build config file for the given Info.
- void BackgroundLoadBuildConfig(Info* info,
- bool is_default,
- const ParseNode* root);
-
- // Returns the lock to use.
- base::Lock& GetLock() const;
-
- const BuildSettings* build_settings_;
-
- // We own the info pointers.
- typedef std::map<Label, Info*> ToolchainMap;
- ToolchainMap toolchains_;
-
- Label default_toolchain_;
- LocationRange default_toolchain_defined_here_;
-
- typedef std::map<std::pair<SourceFile, const Info*>, Info*>
- BuildConfigInvokeMap;
- BuildConfigInvokeMap pending_build_config_map_;
-
- DISALLOW_COPY_AND_ASSIGN(ToolchainManager);
-};
-
-#endif // TOOLS_GN_TOOLCHAIN_MANAGER_H_
diff --git a/tools/gn/trace.cc b/tools/gn/trace.cc
index 429cc71..aa73eba 100644
--- a/tools/gn/trace.cc
+++ b/tools/gn/trace.cc
@@ -134,6 +134,16 @@ ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name)
}
}
+ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label)
+ : item_(NULL),
+ done_(false) {
+ if (trace_log) {
+ item_ = new TraceItem(t, label.GetUserVisibleName(false),
+ base::PlatformThread::CurrentId());
+ item_->set_begin(base::TimeTicks::HighResNow());
+ }
+}
+
ScopedTrace::~ScopedTrace() {
Done();
}
@@ -190,6 +200,7 @@ std::string SummarizeTraces() {
break;
case TraceItem::TRACE_FILE_LOAD:
case TraceItem::TRACE_FILE_WRITE:
+ case TraceItem::TRACE_DEFINE_TARGET:
break; // Ignore these for the summary.
}
}
@@ -212,6 +223,12 @@ void SaveTraces(const base::FilePath& file_name) {
std::string quote_buffer; // Allocate outside loop to prevent reallocationg.
+ // Write main thread metadata (assume this is being written on the main
+ // thread).
+ out << "{\"pid\":0,\"tid\":" << base::PlatformThread::CurrentId();
+ out << ",\"ts\":0,\"ph\":\"M\",";
+ out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
+
std::vector<TraceItem*> events = trace_log->events();
for (size_t i = 0; i < events.size(); i++) {
const TraceItem& item = *events[i];
@@ -244,6 +261,8 @@ void SaveTraces(const base::FilePath& file_name) {
case TraceItem::TRACE_SCRIPT_EXECUTE:
out << "\"script_exec\"";
break;
+ case TraceItem::TRACE_DEFINE_TARGET:
+ out << "\"define\"";
}
if (!item.toolchain().empty() || !item.cmdline().empty()) {
diff --git a/tools/gn/trace.h b/tools/gn/trace.h
index dcd8060..b795bf77 100644
--- a/tools/gn/trace.h
+++ b/tools/gn/trace.h
@@ -23,7 +23,8 @@ class TraceItem {
TRACE_FILE_PARSE,
TRACE_FILE_EXECUTE,
TRACE_FILE_WRITE,
- TRACE_SCRIPT_EXECUTE
+ TRACE_SCRIPT_EXECUTE,
+ TRACE_DEFINE_TARGET
};
TraceItem(Type type,
@@ -65,6 +66,7 @@ class TraceItem {
class ScopedTrace {
public:
ScopedTrace(TraceItem::Type t, const std::string& name);
+ ScopedTrace(TraceItem::Type t, const Label& label);
~ScopedTrace();
void SetToolchain(const Label& label);