diff options
Diffstat (limited to 'tools/gn')
-rw-r--r-- | tools/gn/BUILD.gn | 2 | ||||
-rw-r--r-- | tools/gn/file_template.cc | 17 | ||||
-rw-r--r-- | tools/gn/file_template.h | 6 | ||||
-rw-r--r-- | tools/gn/file_template_unittest.cc | 1 | ||||
-rw-r--r-- | tools/gn/function_get_target_outputs.cc | 181 | ||||
-rw-r--r-- | tools/gn/function_get_target_outputs_unittest.cc | 115 | ||||
-rw-r--r-- | tools/gn/function_toolchain.cc | 8 | ||||
-rw-r--r-- | tools/gn/functions.cc | 11 | ||||
-rw-r--r-- | tools/gn/functions.h | 8 | ||||
-rw-r--r-- | tools/gn/gn.gyp | 2 | ||||
-rw-r--r-- | tools/gn/loader.cc | 8 | ||||
-rw-r--r-- | tools/gn/scope.cc | 17 | ||||
-rw-r--r-- | tools/gn/scope.h | 27 | ||||
-rw-r--r-- | tools/gn/target_generator.cc | 12 | ||||
-rw-r--r-- | tools/gn/template.cc | 3 |
15 files changed, 406 insertions, 12 deletions
diff --git a/tools/gn/BUILD.gn b/tools/gn/BUILD.gn index 4657c57..ee7b3b8 100644 --- a/tools/gn/BUILD.gn +++ b/tools/gn/BUILD.gn @@ -52,6 +52,7 @@ static_library("gn_lib") { "functions.h", "functions_target.cc", "function_exec_script.cc", + "function_get_target_outputs.cc", "function_process_file_template.cc", "function_read_file.cc", "function_rebase_path.cc", @@ -177,6 +178,7 @@ test("gn_unittests") { "escape_unittest.cc", "filesystem_utils_unittest.cc", "file_template_unittest.cc", + "function_get_target_outputs_unittest.cc", "function_rebase_path_unittest.cc", "functions_unittest.cc", "header_checker_unittest.cc", diff --git a/tools/gn/file_template.cc b/tools/gn/file_template.cc index ec34993..b27087b 100644 --- a/tools/gn/file_template.cc +++ b/tools/gn/file_template.cc @@ -91,6 +91,13 @@ FileTemplate::FileTemplate(const std::vector<std::string>& t) ParseOneTemplateString(t[i]); } +FileTemplate::FileTemplate(const std::vector<SourceFile>& t) + : has_substitutions_(false) { + std::fill(types_required_, &types_required_[Subrange::NUM_TYPES], false); + for (size_t i = 0; i < t.size(); i++) + ParseOneTemplateString(t[i].value()); +} + FileTemplate::~FileTemplate() { } @@ -121,6 +128,7 @@ void FileTemplate::Apply(const Value& sources, const std::vector<Value>& sources_list = sources.list_value(); for (size_t i = 0; i < sources_list.size(); i++) { + string_output.clear(); if (!sources_list[i].VerifyTypeIs(Value::STRING, err)) return; @@ -140,17 +148,18 @@ void FileTemplate::ApplyString(const std::string& str, subst[i] = GetSubstitution(str, static_cast<Subrange::Type>(i)); } - output->resize(templates_.container().size()); + size_t first_output_index = output->size(); + output->resize(output->size() + templates_.container().size()); for (size_t template_i = 0; template_i < templates_.container().size(); template_i++) { const Template& t = templates_[template_i]; - (*output)[template_i].clear(); + std::string& cur_output = (*output)[first_output_index + template_i]; for (size_t subrange_i = 0; subrange_i < t.container().size(); subrange_i++) { if (t[subrange_i].type == Subrange::LITERAL) - (*output)[template_i].append(t[subrange_i].literal); + cur_output.append(t[subrange_i].literal); else - (*output)[template_i].append(subst[t[subrange_i].type]); + cur_output.append(subst[t[subrange_i].type]); } } } diff --git a/tools/gn/file_template.h b/tools/gn/file_template.h index d66b3e3..30b40da 100644 --- a/tools/gn/file_template.h +++ b/tools/gn/file_template.h @@ -14,6 +14,7 @@ struct EscapeOptions; class ParseNode; +class SourceFile; class Target; extern const char kSourceExpansion_Help[]; @@ -61,6 +62,8 @@ class FileTemplate { // set. In this case you should not use this object. FileTemplate(const Value& t, Err* err); FileTemplate(const std::vector<std::string>& t); + FileTemplate(const std::vector<SourceFile>& t); + ~FileTemplate(); // Returns an output template representing the given target's script @@ -80,6 +83,9 @@ class FileTemplate { const ParseNode* origin, std::vector<Value>* dest, Err* err) const; + + // Low-level version of Apply that handles one source file. The results + // will be *appended* to the output. void ApplyString(const std::string& input, std::vector<std::string>* output) const; diff --git a/tools/gn/file_template_unittest.cc b/tools/gn/file_template_unittest.cc index e05bda0..0120a8b 100644 --- a/tools/gn/file_template_unittest.cc +++ b/tools/gn/file_template_unittest.cc @@ -19,6 +19,7 @@ TEST(FileTemplate, Static) { ASSERT_EQ(1u, result.size()); EXPECT_EQ("something_static", result[0]); + result.clear(); t.ApplyString("lalala", &result); ASSERT_EQ(1u, result.size()); EXPECT_EQ("something_static", result[0]); diff --git a/tools/gn/function_get_target_outputs.cc b/tools/gn/function_get_target_outputs.cc new file mode 100644 index 0000000..370fee9 --- /dev/null +++ b/tools/gn/function_get_target_outputs.cc @@ -0,0 +1,181 @@ +// Copyright 2014 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/build_settings.h" +#include "tools/gn/file_template.h" +#include "tools/gn/functions.h" +#include "tools/gn/ninja_helper.h" +#include "tools/gn/parse_tree.h" +#include "tools/gn/settings.h" +#include "tools/gn/target.h" +#include "tools/gn/value.h" + +namespace functions { + +namespace { + +void GetOutputsForTarget(const BuildSettings* build_settings, + const Target* target, + std::vector<std::string>* ret) { + switch (target->output_type()) { + case Target::ACTION: + case Target::COPY_FILES: { + // Actions and copy targets: return the outputs specified. + const std::vector<SourceFile>& outs = target->action_values().outputs(); + ret->reserve(outs.size()); + for (size_t i = 0; i < outs.size(); i++) + ret->push_back(outs[i].value()); + break; + } + + case Target::ACTION_FOREACH: { + // Action_foreach: return the result of the template in the outputs. + FileTemplate file_template(target->action_values().outputs()); + const std::vector<SourceFile>& sources = target->sources(); + for (size_t i = 0; i < sources.size(); i++) + file_template.ApplyString(sources[i].value(), ret); + break; + } + + case Target::EXECUTABLE: + case Target::SHARED_LIBRARY: + case Target::STATIC_LIBRARY: + // Return the resulting binary file. Currently, fall through to the + // Ninja helper below which will compute the main output name. + // + // TODO(brettw) some targets have secondary files which should go into + // the list after the main (like shared libraries on Windows have an + // import library). + case Target::GROUP: + case Target::SOURCE_SET: { + // These return the stamp file, which is computed by the NinjaHelper. + NinjaHelper helper(build_settings); + OutputFile output_file = helper.GetTargetOutputFile(target); + + // The output file is relative to the build dir. + std::string absolute_output_file = build_settings->build_dir().value(); + absolute_output_file.append(output_file.value()); + + ret->push_back(absolute_output_file); + break; + } + + default: + NOTREACHED(); + } +} + +} // namespace + +const char kGetTargetOutputs[] = "get_target_outputs"; +const char kGetTargetOutputs_HelpShort[] = + "get_target_outputs: [file list] Get the list of outputs from a target."; +const char kGetTargetOutputs_Help[] = + "get_target_outputs: [file list] Get the list of outputs from a target.\n" + "\n" + " get_target_outputs(target_label)\n" + "\n" + " Returns a list of output files for the named target. The named target\n" + " must have been previously defined in the current file before this\n" + " function is called (it can't reference targets in other files because\n" + " there isn't a defined execution order, and it obviously can't\n" + " reference targets that are defined after the function call).\n" + "\n" + "Return value\n" + "\n" + " The names in the resulting list will be absolute file paths (normally\n" + " like \"//out/Debug/bar.exe\", depending on the build directory).\n" + "\n" + " action targets: this will just return the files specified in the\n" + " \"outputs\" variable of the target.\n" + "\n" + " action_foreach targets: this will return the result of applying\n" + " the output template to the sources (see \"gn help source_expansion\").\n" + " This will be the same result (though with guaranteed absolute file\n" + " paths), as process_file_template will return for those inputs\n" + " (see \"gn help process_file_template\").\n" + "\n" + " binary targets (executables, libraries): this will return a list\n" + " of the resulting binary file(s). The \"main output\" (the actual\n" + " binary or library) will always be the 0th element in the result.\n" + " Depending on the platform and output type, there may be other output\n" + " files as well (like import libraries) which will follow.\n" + "\n" + " source sets and groups: this will return a list containing the path of\n" + " the \"stamp\" file that Ninja will produce once all outputs are\n" + " generated. This probably isn't very useful.\n" + "\n" + "Example\n" + "\n" + " # Say this action generates a bunch of C source files.\n" + " action_foreach(\"my_action\") {\n" + " sources = [ ... ]\n" + " outputs = [ ... ]\n" + " }\n" + "\n" + " # Compile the resulting source files into a source set.\n" + " source_set(\"my_lib\") {\n" + " sources = get_target_outputs(\":my_action\")\n" + " }\n"; + +Value RunGetTargetOutputs(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err) { + if (args.size() != 1) { + *err = Err(function, "Expected one argument."); + return Value(); + } + + // Resolve the requested label. + Label label = Label::Resolve(scope->GetSourceDir(), + ToolchainLabelForScope(scope), args[0], err); + if (label.is_null()) + return Value(); + + // Find the referenced target. The targets previously encountered in this + // scope will have been stashed in the item collector (they'll be dispatched + // when this file is done running) so we can look through them. + const Target* target = NULL; + Scope::ItemVector* collector = scope->GetItemCollector(); + if (!collector) { + *err = Err(function, "No targets defined in this context."); + return Value(); + } + for (size_t i = 0; i < collector->size(); i++) { + const Item* item = (*collector)[i]->get(); + if (item->label() != label) + continue; + + const Target* as_target = item->AsTarget(); + if (!as_target) { + *err = Err(function, "Label does not refer to a target.", + label.GetUserVisibleName(false) + + "\nrefers to a " + item->GetItemTypeName()); + return Value(); + } + target = as_target; + break; + } + + if (!target) { + *err = Err(function, "Target not found in this context.", + label.GetUserVisibleName(false) + + "\nwas not found. get_target_outputs() can only be used for targets\n" + "previously defined in the current file."); + return Value(); + } + + std::vector<std::string> files; + GetOutputsForTarget(scope->settings()->build_settings(), target, &files); + + Value ret(function, Value::LIST); + ret.list_value().reserve(files.size()); + for (size_t i = 0; i < files.size(); i++) + ret.list_value().push_back(Value(function, files[i])); + + return ret; +} + +} // namespace functions diff --git a/tools/gn/function_get_target_outputs_unittest.cc b/tools/gn/function_get_target_outputs_unittest.cc new file mode 100644 index 0000000..2833fdd --- /dev/null +++ b/tools/gn/function_get_target_outputs_unittest.cc @@ -0,0 +1,115 @@ +// Copyright 2014 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/functions.h" +#include "tools/gn/target.h" +#include "tools/gn/test_with_scope.h" + +namespace { + +class GetTargetOutputsTest : public testing::Test { + public: + GetTargetOutputsTest() { + // Want consistent target names so explicitly set platform. + setup_.settings()->set_target_os(Settings::LINUX); + setup_.scope()->set_item_collector(&items_); + } + + Value GetTargetOutputs(const std::string& name, Err* err) { + FunctionCallNode function; + std::vector<Value> args; + args.push_back(Value(NULL, name)); + return functions::RunGetTargetOutputs(setup_.scope(), &function, args, err); + } + + // Shortcut to get a label with the current toolchain. + Label GetLabel(const std::string& dir, const std::string& name) { + return Label(SourceDir(dir), name, setup_.toolchain()->label().dir(), + setup_.toolchain()->label().name()); + } + + // Asserts that the given list contains a single string with the given value. + void AssertSingleStringEquals(const Value& list, + const std::string& expected) { + ASSERT_TRUE(list.type() == Value::LIST); + ASSERT_EQ(1u, list.list_value().size()); + ASSERT_TRUE(list.list_value()[0].type() == Value::STRING); + ASSERT_EQ(expected, list.list_value()[0].string_value()); + } + + void AssertTwoStringsEqual(const Value& list, + const std::string& expected1, + const std::string& expected2) { + ASSERT_TRUE(list.type() == Value::LIST); + ASSERT_EQ(2u, list.list_value().size()); + ASSERT_TRUE(list.list_value()[0].type() == Value::STRING); + ASSERT_EQ(expected1, list.list_value()[0].string_value()); + ASSERT_TRUE(list.list_value()[1].type() == Value::STRING); + ASSERT_EQ(expected2, list.list_value()[1].string_value()); + } + + protected: + TestWithScope setup_; + + Scope::ItemVector items_; +}; + +} // namespace + +TEST_F(GetTargetOutputsTest, Executable) { + Target* exe = new Target(setup_.settings(), GetLabel("//foo/", "bar")); + exe->set_output_type(Target::EXECUTABLE); + items_.push_back(new scoped_ptr<Item>(exe)); + + Err err; + Value result = GetTargetOutputs("//foo:bar", &err); + ASSERT_FALSE(err.has_error()); + // The TestWithScope declares that the build is Linux, so we'll have no + // extension for the binaries. + AssertSingleStringEquals(result, "//out/Debug/bar"); +} + +TEST_F(GetTargetOutputsTest, SourceSet) { + Target* source_set = new Target(setup_.settings(), GetLabel("//foo/", "bar")); + source_set->set_output_type(Target::SOURCE_SET); + items_.push_back(new scoped_ptr<Item>(source_set)); + + Err err; + Value result = GetTargetOutputs("//foo:bar", &err); + ASSERT_FALSE(err.has_error()); + AssertSingleStringEquals(result, "//out/Debug/obj/foo/bar.stamp"); +} + +TEST_F(GetTargetOutputsTest, Action) { + Target* action = new Target(setup_.settings(), GetLabel("//foo/", "bar")); + action->set_output_type(Target::ACTION); + action->action_values().outputs().push_back(SourceFile("//output1.txt")); + action->action_values().outputs().push_back(SourceFile("//output2.txt")); + + items_.push_back(new scoped_ptr<Item>(action)); + + Err err; + Value result = GetTargetOutputs("//foo:bar", &err); + ASSERT_FALSE(err.has_error()); + AssertTwoStringsEqual(result, "//output1.txt", "//output2.txt"); +} + +TEST_F(GetTargetOutputsTest, ActionForeach) { + Target* action = new Target(setup_.settings(), GetLabel("//foo/", "bar")); + action->set_output_type(Target::ACTION_FOREACH); + action->sources().push_back(SourceFile("//file.txt")); + action->action_values().outputs().push_back( + SourceFile("//out/Debug/{{source_file_part}}.one")); + action->action_values().outputs().push_back( + SourceFile("//out/Debug/{{source_file_part}}.two")); + + items_.push_back(new scoped_ptr<Item>(action)); + + Err err; + Value result = GetTargetOutputs("//foo:bar", &err); + ASSERT_FALSE(err.has_error()); + AssertTwoStringsEqual(result, "//out/Debug/file.txt.one", + "//out/Debug/file.txt.two"); +} diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc index 43d8cd7..c930df2 100644 --- a/tools/gn/function_toolchain.cc +++ b/tools/gn/function_toolchain.cc @@ -119,7 +119,13 @@ Value RunToolchain(Scope* scope, if (!block_scope.CheckForUnusedVars(err)) return Value(); - scope->settings()->build_settings()->ItemDefined(toolchain.PassAs<Item>()); + // Save this target for the file. + Scope::ItemVector* collector = scope->GetItemCollector(); + if (!collector) { + *err = Err(function, "Can't define a toolchain in this context."); + return Value(); + } + collector->push_back(new scoped_ptr<Item>(toolchain.PassAs<Item>())); return Value(); } diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc index 0146350..3a19dfb 100644 --- a/tools/gn/functions.cc +++ b/tools/gn/functions.cc @@ -243,8 +243,14 @@ Value RunConfig(const FunctionCallNode* function, if (err->has_error()) return Value(); - // Mark as complete. - scope->settings()->build_settings()->ItemDefined(config.PassAs<Item>()); + // Save the generated item. + Scope::ItemVector* collector = scope->GetItemCollector(); + if (!collector) { + *err = Err(function, "Can't define a config in this context."); + return Value(); + } + collector->push_back(new scoped_ptr<Item>(config.PassAs<Item>())); + return Value(); } @@ -646,6 +652,7 @@ struct FunctionInfoInitializer { INSERT_FUNCTION(Defined, false) INSERT_FUNCTION(ExecScript, false) INSERT_FUNCTION(GetEnv, false) + INSERT_FUNCTION(GetTargetOutputs, false) INSERT_FUNCTION(Import, false) INSERT_FUNCTION(Print, false) INSERT_FUNCTION(ProcessFileTemplate, false) diff --git a/tools/gn/functions.h b/tools/gn/functions.h index 8a1bf3a..d3eb364 100644 --- a/tools/gn/functions.h +++ b/tools/gn/functions.h @@ -147,6 +147,14 @@ Value RunGetEnv(Scope* scope, const std::vector<Value>& args, Err* err); +extern const char kGetTargetOutputs[]; +extern const char kGetTargetOutputs_HelpShort[]; +extern const char kGetTargetOutputs_Help[]; +Value RunGetTargetOutputs(Scope* scope, + const FunctionCallNode* function, + const std::vector<Value>& args, + Err* err); + extern const char kGroup[]; extern const char kGroup_HelpShort[]; extern const char kGroup_Help[]; diff --git a/tools/gn/gn.gyp b/tools/gn/gn.gyp index 8be6a3b..93560ed 100644 --- a/tools/gn/gn.gyp +++ b/tools/gn/gn.gyp @@ -56,6 +56,7 @@ 'functions.cc', 'functions.h', 'function_exec_script.cc', + 'function_get_target_outputs.cc', 'function_process_file_template.cc', 'function_read_file.cc', 'function_rebase_path.cc', @@ -178,6 +179,7 @@ 'escape_unittest.cc', 'filesystem_utils_unittest.cc', 'file_template_unittest.cc', + 'function_get_target_outputs_unittest.cc', 'function_rebase_path_unittest.cc', 'functions_unittest.cc', 'header_checker_unittest.cc', diff --git a/tools/gn/loader.cc b/tools/gn/loader.cc index 2893e23..885670a 100644 --- a/tools/gn/loader.cc +++ b/tools/gn/loader.cc @@ -247,6 +247,10 @@ void LoaderImpl::BackgroundLoadFile(const Settings* settings, ScopePerFileProvider per_file_provider(&our_scope, true); our_scope.set_source_dir(file_name.GetDir()); + // Targets, etc. generated as part of running this file will end up here. + Scope::ItemVector collected_items; + our_scope.set_item_collector(&collected_items); + ScopedTrace trace(TraceItem::TRACE_FILE_EXECUTE, file_name.value()); trace.SetToolchain(settings->toolchain_label()); @@ -255,6 +259,10 @@ void LoaderImpl::BackgroundLoadFile(const Settings* settings, if (err.has_error()) g_scheduler->FailWithError(err); + // Pass all of the items that were defined off to the builder. + for (size_t i = 0; i < collected_items.size(); i++) + settings->build_settings()->ItemDefined(collected_items[i]->Pass()); + trace.Done(); main_loop_->PostTask(FROM_HERE, base::Bind(&LoaderImpl::DidLoadFile, this)); diff --git a/tools/gn/scope.cc b/tools/gn/scope.cc index 9315749..437b31f 100644 --- a/tools/gn/scope.cc +++ b/tools/gn/scope.cc @@ -22,21 +22,24 @@ Scope::Scope(const Settings* settings) : const_containing_(NULL), mutable_containing_(NULL), settings_(settings), - mode_flags_(0) { + mode_flags_(0), + item_collector_(NULL) { } Scope::Scope(Scope* parent) : const_containing_(NULL), mutable_containing_(parent), settings_(parent->settings()), - mode_flags_(0) { + mode_flags_(0), + item_collector_(NULL) { } Scope::Scope(const Scope* parent) : const_containing_(parent), mutable_containing_(NULL), settings_(parent->settings()), - mode_flags_(0) { + mode_flags_(0), + item_collector_(NULL) { } Scope::~Scope() { @@ -390,6 +393,14 @@ const SourceDir& Scope::GetSourceDir() const { return source_dir_; } +Scope::ItemVector* Scope::GetItemCollector() { + if (item_collector_) + return item_collector_; + if (mutable_containing()) + return mutable_containing()->GetItemCollector(); + return NULL; +} + void Scope::SetProperty(const void* key, void* value) { if (!value) { DCHECK(properties_.find(key) != properties_.end()); diff --git a/tools/gn/scope.h b/tools/gn/scope.h index 82e037f..208ee55 100644 --- a/tools/gn/scope.h +++ b/tools/gn/scope.h @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/containers/hash_tables.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "tools/gn/err.h" #include "tools/gn/pattern.h" #include "tools/gn/source_dir.h" @@ -18,6 +19,7 @@ class FunctionCallNode; class ImportManager; +class Item; class ParseNode; class Settings; class TargetManager; @@ -37,6 +39,9 @@ class Template; class Scope { public: typedef base::hash_map<base::StringPiece, Value> KeyValueMap; + // Holds an owning list of scoped_ptrs of Items (since we can't make a vector + // of scoped_ptrs). + typedef ScopedVector< scoped_ptr<Item> > ItemVector; // Allows code to provide values for built-in variables. This class will // automatically register itself on construction and deregister itself on @@ -224,6 +229,26 @@ class Scope { const SourceDir& GetSourceDir() const; void set_source_dir(const SourceDir& d) { source_dir_ = d; } + // The item collector is where Items (Targets, Configs, etc.) go that have + // been defined. If a scope can generate items, this non-owning pointer will + // point to the storage for such items. The creator of this scope will be + // responsible for setting up the collector and then dealing with the + // collected items once execution of the context is complete. + // + // The items in a scope are collected as we go and then dispatched at the end + // of execution of a scope so that we can query the previously-generated + // targets (like getting the outputs). + // + // This can be null if the current scope can not generate items (like for + // imports and such). + // + // When retrieving the collector, the non-const scopes are recursively + // queried. The collector is not copied for closures, etc. + void set_item_collector(ItemVector* collector) { + item_collector_ = collector; + } + ItemVector* GetItemCollector(); + // Properties are opaque pointers that code can use to set state on a Scope // that it can retrieve later. // @@ -283,6 +308,8 @@ class Scope { typedef std::map<std::string, const Template*> TemplateMap; TemplateMap templates_; + ItemVector* item_collector_; + // Opaque pointers. See SetProperty() above. typedef std::map<const void*, void*> PropertyMap; PropertyMap properties_; diff --git a/tools/gn/target_generator.cc b/tools/gn/target_generator.cc index 2820298..cf6bc1f 100644 --- a/tools/gn/target_generator.cc +++ b/tools/gn/target_generator.cc @@ -117,8 +117,16 @@ void TargetGenerator::GenerateTarget(Scope* scope, "I am very confused."); } - if (!err->has_error()) - scope->settings()->build_settings()->ItemDefined(target.PassAs<Item>()); + if (err->has_error()) + return; + + // Save this target for the file. + Scope::ItemVector* collector = scope->GetItemCollector(); + if (!collector) { + *err = Err(function_call, "Can't define a target in this context."); + return; + } + collector->push_back(new scoped_ptr<Item>(target.PassAs<Item>())); } const BuildSettings* TargetGenerator::GetBuildSettings() const { diff --git a/tools/gn/template.cc b/tools/gn/template.cc index bf4eb61..11dcf1f 100644 --- a/tools/gn/template.cc +++ b/tools/gn/template.cc @@ -62,6 +62,9 @@ Value Template::Invoke(Scope* scope, ScopePerFileProvider per_file_provider(&template_scope, true); + // Targets defined in the template go in the collector for the invoking file. + template_scope.set_item_collector(scope->GetItemCollector()); + // We jump through some hoops to avoid copying the invocation scope when // setting it in the template scope (since the invocation scope may have // large lists of source files in it and could be expensive to copy). |