| 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();
|
| +}
|
|
|