Throw errors for nested targets in GN.
People sometimes nest targets or configs, usually with the assumption that
this limits the visibility of a config to within a target. But this nesting
provides no visibility restrictions over declaring it outside of a block.
For clarity, force certain types of blocks to be non-nested.
Adds missing ordering documentation to the ldflags variable.
Review URL: https://codereview.chromium.org/1314773005
Cr-Original-Commit-Position: refs/heads/master@{#348186}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: a88eb18a5cb883cbde7a1e055958642a8dafb4ac
diff --git a/tools/gn/function_template.cc b/tools/gn/function_template.cc
index 1f1da82..290eb78 100644
--- a/tools/gn/function_template.cc
+++ b/tools/gn/function_template.cc
@@ -163,6 +163,14 @@
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
+ // Of course you can have configs and targets in a template. But here, we're
+ // not actually executing the block, only declaring it. Marking the template
+ // declaration as non-nestable means that you can't put it inside a target,
+ // for example.
+ NonNestableBlock non_nestable(scope, function, "template");
+ if (!non_nestable.Enter(err))
+ return Value();
+
// TODO(brettw) determine if the function is built-in and throw an error if
// it is.
if (args.size() != 1) {
diff --git a/tools/gn/function_toolchain.cc b/tools/gn/function_toolchain.cc
index 2fe5f53..4e1fe2d 100644
--- a/tools/gn/function_toolchain.cc
+++ b/tools/gn/function_toolchain.cc
@@ -301,6 +301,10 @@
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
+ NonNestableBlock non_nestable(scope, function, "toolchain");
+ if (!non_nestable.Enter(err))
+ return Value();
+
if (!EnsureNotProcessingImport(function, scope, err) ||
!EnsureNotProcessingBuildConfig(function, scope, err))
return Value();
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
index 0d86093..71d05aa 100644
--- a/tools/gn/functions.cc
+++ b/tools/gn/functions.cc
@@ -131,6 +131,42 @@
toolchain_label.name());
}
+// static
+const int NonNestableBlock::kKey = 0;
+
+NonNestableBlock::NonNestableBlock(
+ Scope* scope,
+ const FunctionCallNode* function,
+ const char* type_description)
+ : scope_(scope),
+ function_(function),
+ type_description_(type_description),
+ key_added_(false) {
+}
+
+NonNestableBlock::~NonNestableBlock() {
+ if (key_added_)
+ scope_->SetProperty(&kKey, nullptr);
+}
+
+bool NonNestableBlock::Enter(Err* err) {
+ void* scope_value = scope_->GetProperty(&kKey, nullptr);
+ if (scope_value) {
+ // Existing block.
+ const NonNestableBlock* existing =
+ reinterpret_cast<const NonNestableBlock*>(scope_value);
+ *err = Err(function_, "Can't nest these things.",
+ std::string("You are trying to nest a ") + type_description_ +
+ " inside a " + existing->type_description_ + ".");
+ err->AppendSubErr(Err(existing->function_, "The enclosing block."));
+ return false;
+ }
+
+ scope_->SetProperty(&kKey, this);
+ key_added_ = true;
+ return true;
+}
+
namespace functions {
// assert ----------------------------------------------------------------------
@@ -244,6 +280,10 @@
const std::vector<Value>& args,
Scope* scope,
Err* err) {
+ NonNestableBlock non_nestable(scope, function, "config");
+ if (!non_nestable.Enter(err))
+ return Value();
+
if (!EnsureSingleStringArg(function, args, err) ||
!EnsureNotProcessingImport(function, scope, err))
return Value();
@@ -308,6 +348,10 @@
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
+ NonNestableBlock non_nestable(scope, function, "declare_args");
+ if (!non_nestable.Enter(err))
+ return Value();
+
Scope block_scope(scope);
block->Execute(&block_scope, err);
if (err->has_error())
diff --git a/tools/gn/functions.h b/tools/gn/functions.h
index 6015375..f9c867d 100644
--- a/tools/gn/functions.h
+++ b/tools/gn/functions.h
@@ -430,4 +430,40 @@
const FunctionCallNode* function,
const std::string& name);
+// Some tyesp of blocks can't be nested inside other ones. For such cases,
+// instantiate this object upon entering the block and Enter() will fail if
+// there is already another non-nestable block on the stack.
+class NonNestableBlock {
+ public:
+ enum Type {
+ CONFIG,
+ DECLARE_ARGS,
+ TARGET,
+ TEMPLATE,
+ TOOLCHAIN,
+ };
+
+ // type_description is a string that will be used in error messages
+ // describing the type of the block, for example, "template" or "config".
+ NonNestableBlock(Scope* scope,
+ const FunctionCallNode* function,
+ const char* type_description);
+ ~NonNestableBlock();
+
+ bool Enter(Err* err);
+
+ private:
+ // Used as a void* key for the Scope to track our property. The actual value
+ // is never used.
+ static const int kKey;
+
+ Scope* scope_;
+ const FunctionCallNode* function_;
+ const char* type_description_;
+
+ // Set to true when the key is added to the scope so we don't try to
+ // delete nonexistant keys which will cause assertions.
+ bool key_added_;
+};
+
#endif // TOOLS_GN_FUNCTIONS_H_
diff --git a/tools/gn/functions_target.cc b/tools/gn/functions_target.cc
index 655c32a..45e6859 100644
--- a/tools/gn/functions_target.cc
+++ b/tools/gn/functions_target.cc
@@ -31,6 +31,10 @@
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
+ NonNestableBlock non_nestable(scope, function, "target");
+ if (!non_nestable.Enter(err))
+ return Value();
+
if (!EnsureNotProcessingImport(function, scope, err) ||
!EnsureNotProcessingBuildConfig(function, scope, err))
return Value();
diff --git a/tools/gn/variables.cc b/tools/gn/variables.cc
index 0f3ede7..83b2957 100644
--- a/tools/gn/variables.cc
+++ b/tools/gn/variables.cc
@@ -767,7 +767,8 @@
" ldflags are NOT pushed to dependents, so applying ldflags to source\n"
" sets or static libraries will be a no-op. If you want to apply ldflags\n"
" to dependent targets, put them in a config and set it in the\n"
- " all_dependent_configs or public_configs.\n";
+ " all_dependent_configs or public_configs.\n"
+ COMMON_ORDERING_HELP;
#define COMMON_LIB_INHERITANCE_HELP \
"\n" \