summaryrefslogtreecommitdiffstats
path: root/tools/gn
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gn')
-rw-r--r--tools/gn/BUILD.gn2
-rw-r--r--tools/gn/file_template.cc17
-rw-r--r--tools/gn/file_template.h6
-rw-r--r--tools/gn/file_template_unittest.cc1
-rw-r--r--tools/gn/function_get_target_outputs.cc181
-rw-r--r--tools/gn/function_get_target_outputs_unittest.cc115
-rw-r--r--tools/gn/function_toolchain.cc8
-rw-r--r--tools/gn/functions.cc11
-rw-r--r--tools/gn/functions.h8
-rw-r--r--tools/gn/gn.gyp2
-rw-r--r--tools/gn/loader.cc8
-rw-r--r--tools/gn/scope.cc17
-rw-r--r--tools/gn/scope.h27
-rw-r--r--tools/gn/target_generator.cc12
-rw-r--r--tools/gn/template.cc3
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).