Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Unified Diff: tools/gn/functions.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/functions.h ('k') | tools/gn/functions_target.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/functions.cc
diff --git a/tools/gn/functions.cc b/tools/gn/functions.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7f32db27ba4910646c6c4887d6a3888d310794dd
--- /dev/null
+++ b/tools/gn/functions.cc
@@ -0,0 +1,443 @@
+// 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/functions.h"
+
+#include <iostream>
+
+#include "base/strings/string_util.h"
+#include "tools/gn/config.h"
+#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"
+
+namespace {
+
+void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
+ *err = Err(function->function(), "This function call requires a block.",
+ "The block's \"{\" must be on the same line as the function "
+ "call's \")\".");
+}
+
+Value ExecuteAssert(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function->function(), "Wrong number of arguments.",
+ "assert() takes one argument, "
+ "were you expecting somethig else?");
+ } else if (args[0].InterpretAsInt() == 0) {
+ *err = Err(function->function(), "Assertion failed.");
+ if (args[0].origin()) {
+ // If you do "assert(foo)" we'd ideally like to show you where foo was
+ // set, and in this case the origin of the args will tell us that.
+ // However, if you do "assert(foo && bar)" the source of the value will
+ // be the assert like, which isn't so helpful.
+ //
+ // So we try to see if the args are from the same line or not. This will
+ // break if you do "assert(\nfoo && bar)" and we may show the second line
+ // as the source, oh well. The way around this is to check to see if the
+ // origin node is inside our function call block.
+ Location origin_location = args[0].origin()->GetRange().begin();
+ if (origin_location.file() != function->function().location().file() ||
+ origin_location.line_number() !=
+ function->function().location().line_number()) {
+ err->AppendSubErr(Err(args[0].origin()->GetRange(), "",
+ "This is where it was set."));
+ }
+ }
+ }
+ return Value();
+}
+
+Value ExecuteConfig(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Generating 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();
+
+ // Fill it.
+ const SourceDir input_dir = SourceDirForFunctionCall(function);
+ ConfigValuesGenerator gen(&config->config_values(), scope,
+ function->function(), input_dir, err);
+ gen.Run();
+ if (err->has_error())
+ return Value();
+
+ // Mark as complete.
+ {
+ base::AutoLock lock(tree->lock());
+ tree->MarkItemGeneratedLocked(label);
+ }
+ return Value();
+}
+
+Value ExecuteDeclareArgs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ // Only allow this to be called once. We use a variable in the current scope
+ // with a name the parser will reject if the user tried to type it.
+ const char did_declare_args_var[] = "@@declared_args";
+ if (scope->GetValue(did_declare_args_var)) {
+ *err = Err(function->function(), "Duplicate call to declared_args.");
+ err->AppendSubErr(
+ Err(scope->GetValue(did_declare_args_var)->origin()->GetRange(),
+ "See the original call."));
+ return Value();
+ }
+
+ // Find the root scope where the values will be set.
+ Scope* root = scope->mutable_containing();
+ if (!root || root->containing() || !scope->IsProcessingBuildConfig()) {
+ *err = Err(function->function(), "declare_args called incorrectly."
+ "It must be called only from the build config script and in the "
+ "root scope.");
+ return Value();
+ }
+
+ // Take all variables set in the current scope as default values and put
+ // them in the parent scope. The values in the current scope are the defaults,
+ // then we apply the external args to this list.
+ Scope::KeyValueVector values;
+ scope->GetCurrentScopeValues(&values);
+ for (size_t i = 0; i < values.size(); i++) {
+ // TODO(brettw) actually import the arguments from the command line rather
+ // than only using the defaults.
+ root->SetValue(values[i].first, values[i].second,
+ values[i].second.origin());
+ }
+
+ scope->SetValue(did_declare_args_var, Value(function, 1), NULL);
+ return Value();
+}
+
+Value ExecuteImport(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ const SourceDir input_dir = SourceDirForFunctionCall(function);
+ SourceFile import_file =
+ input_dir.ResolveRelativeFile(args[0].string_value());
+ scope->settings()->import_manager().DoImport(import_file, function,
+ scope, err);
+ return Value();
+}
+
+Value ExecuteTemplate(Scope* scope,
+ const FunctionCallNode* invocation,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ const FunctionCallNode* rule,
+ Err* err) {
+ if (!EnsureNotProcessingImport(invocation, scope, err))
+ return Value();
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, invocation,
+ invocation->function().value().data(),
+ block, args, &block_scope, err))
+ return Value();
+
+ // Run the block for the rule invocation.
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Now run the rule itself with that block as the current scope.
+ rule->block()->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ return Value();
+}
+
+Value ExecuteSetDefaults(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+ const std::string& target_type(args[0].string_value());
+
+ // Ensure there aren't defaults already set.
+ if (scope->GetTargetDefaults(target_type)) {
+ *err = Err(function->function(),
+ "This target type defaults were already set.");
+ return Value();
+ }
+
+ // Execute the block in a new scope that has a parent of the containing
+ // scope.
+ Scope block_scope(scope);
+ if (!FillTargetBlockScope(scope, function,
+ function->function().value().data(),
+ block, args, &block_scope, err))
+ return Value();
+
+ // Run the block for the rule invocation.
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Now copy the values set on the scope we made into the free-floating one
+ // (with no containing scope) used to hold the target defaults.
+ Scope* dest = scope->MakeTargetDefaults(target_type);
+ block_scope.NonRecursiveMergeTo(dest, function, "<SHOULD NOT FAIL>", err);
+ return Value();
+}
+
+Value ExecuteSetSourcesAssignmentFilter(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function, "set_sources_assignment_filter takes one argument.");
+ } else {
+ scoped_ptr<PatternList> f(new PatternList);
+ f->SetFromValue(args[0], err);
+ if (!err->has_error())
+ scope->set_sources_assignment_filter(f.Pass());
+ }
+ return Value();
+}
+
+// void print(...)
+// prints all arguments to the console separated by spaces.
+Value ExecutePrint(const std::vector<Value>& args, Err* err) {
+ for (size_t i = 0; i < args.size(); i++) {
+ if (i != 0)
+ std::cout << " ";
+ std::cout << args[i].ToString();
+ }
+ std::cout << std::endl;
+ return Value();
+}
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+namespace functions {
+
+const char kAssert[] = "assert";
+const char kComponent[] = "component";
+const char kConfig[] = "config";
+const char kCopy[] = "copy";
+const char kCustom[] = "custom";
+const char kDeclareArgs[] = "declare_args";
+const char kExecScript[] = "exec_script";
+const char kExecutable[] = "executable";
+const char kGroup[] = "group";
+const char kImport[] = "import";
+const char kPrint[] = "print";
+const char kProcessFileTemplate[] = "process_file_template";
+const char kReadFile[] = "read_file";
+const char kSetDefaults[] = "set_defaults";
+const char kSetDefaultToolchain[] = "set_default_toolchain";
+const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
+const char kSharedLibrary[] = "shared_library";
+const char kStaticLibrary[] = "static_library";
+const char kTemplate[] = "template";
+const char kTool[] = "tool";
+const char kToolchain[] = "toolchain";
+const char kTest[] = "test";
+const char kWriteFile[] = "write_file";
+
+} // namespace functions
+
+// ----------------------------------------------------------------------------
+
+bool EnsureNotProcessingImport(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingImport()) {
+ *err = Err(node, "Not valid from an import.",
+ "We need to talk about this thing you are doing here. Doing this\n"
+ "kind of thing from an imported file makes me feel like you are\n"
+ "abusing me. Imports are for defining defaults, variables, and rules.\n"
+ "The appropriate place for this kind of thing is really in a normal\n"
+ "BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool EnsureNotProcessingBuildConfig(const ParseNode* node,
+ const Scope* scope,
+ Err* err) {
+ if (scope->IsProcessingBuildConfig()) {
+ *err = Err(node, "Not valid from the build config.",
+ "You can't do this kind of thing from the build config script, "
+ "silly!\nPut it in a regular BUILD file.");
+ return false;
+ }
+ return true;
+}
+
+bool FillTargetBlockScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const char* target_type,
+ const BlockNode* block,
+ const std::vector<Value>& args,
+ Scope* block_scope,
+ Err* err) {
+ if (!block) {
+ FillNeedsBlockError(function, err);
+ return false;
+ }
+
+ // Copy the target defaults, if any, into the scope we're going to execute
+ // the block in.
+ const Scope* default_scope = scope->GetTargetDefaults(target_type);
+ if (default_scope) {
+ if (!default_scope->NonRecursiveMergeTo(block_scope, function,
+ "target defaults", err))
+ return false;
+ }
+
+ // The name is the single argument to the target function.
+ if (!EnsureSingleStringArg(function, args, err))
+ return false;
+
+ // Set the target name variable to the current target, and mark it used
+ // because we don't want to issue an error if the script ignores it.
+ const base::StringPiece target_name("target_name");
+ block_scope->SetValue(target_name, Value(function, args[0].string_value()),
+ function);
+ block_scope->MarkUsed(target_name);
+ return true;
+}
+
+bool EnsureSingleStringArg(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function->function(), "Incorrect arguments.",
+ "This function requires a single string argument.");
+ return false;
+ }
+ return args[0].VerifyTypeIs(Value::STRING, err);
+}
+
+const SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function) {
+ return function->function().location().file()->dir();
+}
+
+const Label& ToolchainLabelForScope(const Scope* scope) {
+ return scope->settings()->toolchain()->label();
+}
+
+Label MakeLabelForScope(const Scope* scope,
+ const FunctionCallNode* function,
+ const std::string& name) {
+ const SourceDir& input_dir = SourceDirForFunctionCall(function);
+ const Label& toolchain_label = ToolchainLabelForScope(scope);
+ return Label(input_dir, name, toolchain_label.dir(), toolchain_label.name());
+}
+
+Value ExecuteFunction(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ const Token& name = function->function();
+ if (block) {
+ // These target generators need to execute the block themselves.
+ if (name.IsIdentifierEqualTo(functions::kComponent))
+ return ExecuteComponent(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kCustom))
+ return ExecuteCustom(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kExecutable))
+ return ExecuteExecutable(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kSetDefaults))
+ return ExecuteSetDefaults(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kSharedLibrary))
+ return ExecuteSharedLibrary(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kStaticLibrary))
+ return ExecuteStaticLibrary(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kGroup))
+ return ExecuteGroup(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTest))
+ return ExecuteExecutable(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTemplate))
+ return ExecuteTemplate(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kTool))
+ return ExecuteTool(scope, function, args, block, err);
+ if (name.IsIdentifierEqualTo(functions::kToolchain))
+ return ExecuteToolchain(scope, function, args, block, err);
+
+ const FunctionCallNode* rule =
+ scope->GetTemplate(function->function().value().as_string());
+ if (rule)
+ return ExecuteTemplate(scope, function, args, block, rule, err);
+
+ // FIXME(brettw) This is not right, what if you specify a function that
+ // doesn't take a block but specify one?!?!?
+
+ // The rest of the functions can take a pre-executed block for simplicity.
+ Scope block_scope(scope);
+ block->ExecuteBlockInScope(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ if (name.IsIdentifierEqualTo(functions::kConfig))
+ return ExecuteConfig(&block_scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kCopy))
+ return ExecuteCopy(&block_scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kDeclareArgs))
+ return ExecuteDeclareArgs(&block_scope, function, args, err);
+
+ *err = Err(name, "Unknown function.");
+ return Value();
+ }
+
+ if (name.IsIdentifierEqualTo(functions::kAssert))
+ return ExecuteAssert(function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kExecScript))
+ return ExecuteExecScript(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kImport))
+ return ExecuteImport(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kPrint))
+ return ExecutePrint(args, err);
+ if (name.IsIdentifierEqualTo(functions::kProcessFileTemplate))
+ return ExecuteProcessFileTemplate(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kReadFile))
+ return ExecuteReadFile(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kSetDefaultToolchain))
+ return ExecuteSetDefaultToolchain(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kSetSourcesAssignmentFilter))
+ return ExecuteSetSourcesAssignmentFilter(scope, function, args, err);
+ if (name.IsIdentifierEqualTo(functions::kWriteFile))
+ return ExecuteWriteFile(scope, function, args, err);
+
+ *err = Err(function, "Unknown function.");
+ return Value();
+}
« no previous file with comments | « tools/gn/functions.h ('k') | tools/gn/functions_target.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698