summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 20:27:53 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 20:27:53 +0000
commite1bd79f30d777a17411bf0efd54d90b59c00f600 (patch)
tree8ec4d5560c1df26a3c655ce98ae8586c23eaf096 /tools
parentda22c3c95242b05675da6b5e452c5dad8c5db1ee (diff)
downloadchromium_src-e1bd79f30d777a17411bf0efd54d90b59c00f600.zip
chromium_src-e1bd79f30d777a17411bf0efd54d90b59c00f600.tar.gz
chromium_src-e1bd79f30d777a17411bf0efd54d90b59c00f600.tar.bz2
Add get_target_outputs function to GN
This function returns the outputs for a target that appeared previously in the same file. This is frequently needed for getting the files resulting from an action. The main operational change is that targets that are generated are stashed in a vector as we process a file and then dispatched once that file is finished, rather than being dispatched as-we-go. This way we can ask questions about the targets that appeared previously in the file. R=scottmg@chromium.org Review URL: https://codereview.chromium.org/269723006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268260 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
-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).