diff options
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); |