blob: af413d655b3cc33ecb4f8e79dc1c5a8f5f87703b [file] [log] [blame]
// 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 <stddef.h>
#include <iostream>
#include <utility>
#include "base/environment.h"
#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/parse_tree.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/scope.h"
#include "tools/gn/settings.h"
#include "tools/gn/template.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
#include "tools/gn/value_extractors.h"
#include "tools/gn/variables.h"
namespace {
// Some functions take a {} following them, and some don't. For the ones that
// don't, this is used to verify that the given block node is null and will
// set the error accordingly if it's not. Returns true if the block is null.
bool VerifyNoBlockForFunctionCall(const FunctionCallNode* function,
const BlockNode* block,
Err* err) {
if (!block)
return true;
*err = Err(block, "Unexpected '{'.",
"This function call doesn't take a {} block following it, and you\n"
"can't have a {} block that's not connected to something like an if\n"
"statement or a target declaration.");
err->AppendRange(function->function().range());
return false;
}
} // namespace
bool EnsureNotProcessingImport(const ParseNode* node,
const Scope* scope,
Err* err) {
if (scope->IsProcessingImport()) {
*err = Err(node, "Not valid from an import.",
"Imports are for defining defaults, variables, and rules. The\n"
"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 std::string& 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) {
Scope::MergeOptions merge_options;
merge_options.skip_private_vars = true;
if (!default_scope->NonRecursiveMergeTo(block_scope, merge_options,
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;
}
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 \")\".");
}
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 Label& ToolchainLabelForScope(const Scope* scope) {
return scope->settings()->toolchain_label();
}
Label MakeLabelForScope(const Scope* scope,
const FunctionCallNode* function,
const std::string& name) {
const Label& toolchain_label = ToolchainLabelForScope(scope);
return Label(scope->GetSourceDir(), name, toolchain_label.dir(),
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 ----------------------------------------------------------------------
const char kAssert[] = "assert";
const char kAssert_HelpShort[] =
"assert: Assert an expression is true at generation time.";
const char kAssert_Help[] =
"assert: Assert an expression is true at generation time.\n"
"\n"
" assert(<condition> [, <error string>])\n"
"\n"
" If the condition is false, the build will fail with an error. If the\n"
" optional second argument is provided, that string will be printed\n"
" with the error message.\n"
"\n"
"Examples:\n"
" assert(is_win)\n"
" assert(defined(sources), \"Sources must be defined\")\n";
Value RunAssert(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
Err* err) {
if (args.size() != 1 && args.size() != 2) {
*err = Err(function->function(), "Wrong number of arguments.",
"assert() takes one or two argument, "
"were you expecting somethig else?");
} else if (args[0].type() != Value::BOOLEAN) {
*err = Err(function->function(), "Assertion value not a bool.");
} else if (!args[0].boolean_value()) {
if (args.size() == 2) {
// Optional string message.
if (args[1].type() != Value::STRING) {
*err = Err(function->function(), "Assertion failed.",
"<<<ERROR MESSAGE IS NOT A STRING>>>");
} else {
*err = Err(function->function(), "Assertion failed.",
args[1].string_value());
}
} else {
*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();
}
// config ----------------------------------------------------------------------
const char kConfig[] = "config";
const char kConfig_HelpShort[] =
"config: Defines a configuration object.";
const char kConfig_Help[] =
"config: Defines a configuration object.\n"
"\n"
" Configuration objects can be applied to targets and specify sets of\n"
" compiler flags, includes, defines, etc. They provide a way to\n"
" conveniently group sets of this configuration information.\n"
"\n"
" A config is referenced by its label just like a target.\n"
"\n"
" The values in a config are additive only. If you want to remove a flag\n"
" you need to remove the corresponding config that sets it. The final\n"
" set of flags, defines, etc. for a target is generated in this order:\n"
"\n"
" 1. The values specified directly on the target (rather than using a\n"
" config.\n"
" 2. The configs specified in the target's \"configs\" list, in order.\n"
" 3. Public_configs from a breadth-first traversal of the dependency\n"
" tree in the order that the targets appear in \"deps\".\n"
" 4. All dependent configs from a breadth-first traversal of the\n"
" dependency tree in the order that the targets appear in \"deps\".\n"
"\n"
"Variables valid in a config definition\n"
"\n"
CONFIG_VALUES_VARS_HELP
" Nested configs: configs\n"
"\n"
"Variables on a target used to apply configs\n"
"\n"
" all_dependent_configs, configs, public_configs\n"
"\n"
"Example\n"
"\n"
" config(\"myconfig\") {\n"
" includes = [ \"include/common\" ]\n"
" defines = [ \"ENABLE_DOOM_MELON\" ]\n"
" }\n"
"\n"
" executable(\"mything\") {\n"
" configs = [ \":myconfig\" ]\n"
" }\n";
Value RunConfig(const FunctionCallNode* function,
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();
Label label(MakeLabelForScope(scope, function, args[0].string_value()));
if (g_scheduler->verbose_logging())
g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
// Create the new config.
std::unique_ptr<Config> config(new Config(scope->settings(), label));
config->set_defined_from(function);
if (!Visibility::FillItemVisibility(config.get(), scope, err))
return Value();
// Fill the flags and such.
const SourceDir& input_dir = scope->GetSourceDir();
ConfigValuesGenerator gen(&config->own_values(), scope, input_dir, err);
gen.Run();
if (err->has_error())
return Value();
// Read sub-configs.
const Value* configs_value = scope->GetValue(variables::kConfigs, true);
if (configs_value) {
ExtractListOfUniqueLabels(*configs_value, scope->GetSourceDir(),
ToolchainLabelForScope(scope),
&config->configs(), err);
}
if (err->has_error())
return Value();
// 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(config.release());
return Value();
}
// declare_args ----------------------------------------------------------------
const char kDeclareArgs[] = "declare_args";
const char kDeclareArgs_HelpShort[] =
"declare_args: Declare build arguments.";
const char kDeclareArgs_Help[] =
"declare_args: Declare build arguments.\n"
"\n"
" Introduces the given arguments into the current scope. If they are\n"
" not specified on the command line or in a toolchain's arguments,\n"
" the default values given in the declare_args block will be used.\n"
" However, these defaults will not override command-line values.\n"
"\n"
" See also \"gn help buildargs\" for an overview.\n"
"\n"
" The precise behavior of declare args is:\n"
"\n"
" 1. The declare_arg block executes. Any variables in the enclosing\n"
" scope are available for reading.\n"
"\n"
" 2. At the end of executing the block, any variables set within that\n"
" scope are saved globally as build arguments, with their current\n"
" values being saved as the \"default value\" for that argument.\n"
"\n"
" 3. User-defined overrides are applied. Anything set in \"gn args\"\n"
" now overrides any default values. The resulting set of variables\n"
" is promoted to be readable from the following code in the file.\n"
"\n"
" This has some ramifications that may not be obvious:\n"
"\n"
" - You should not perform difficult work inside a declare_args block\n"
" since this only sets a default value that may be discarded. In\n"
" particular, don't use the result of exec_script() to set the\n"
" default value. If you want to have a script-defined default, set\n"
" some default \"undefined\" value like [], \"\", or -1, and after\n"
" the declare_args block, call exec_script if the value is unset by\n"
" the user.\n"
"\n"
" - Any code inside of the declare_args block will see the default\n"
" values of previous variables defined in the block rather than\n"
" the user-overridden value. This can be surprising because you will\n"
" be used to seeing the overridden value. If you need to make the\n"
" default value of one arg dependent on the possibly-overridden\n"
" value of another, write two separate declare_args blocks:\n"
"\n"
" declare_args() {\n"
" enable_foo = true\n"
" }\n"
" declare_args() {\n"
" # Bar defaults to same user-overridden state as foo.\n"
" enable_bar = enable_foo\n"
" }\n"
"\n"
"Example\n"
"\n"
" declare_args() {\n"
" enable_teleporter = true\n"
" enable_doom_melon = false\n"
" }\n"
"\n"
" If you want to override the (default disabled) Doom Melon:\n"
" gn --args=\"enable_doom_melon=true enable_teleporter=false\"\n"
" This also sets the teleporter, but it's already defaulted to on so\n"
" it will have no effect.\n";
Value RunDeclareArgs(Scope* scope,
const FunctionCallNode* function,
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())
return Value();
// Pass the values from our scope into the Args object for adding to the
// scope with the proper values (taking into account the defaults given in
// the block_scope, and arguments passed into the build).
Scope::KeyValueMap values;
block_scope.GetCurrentScopeValues(&values);
scope->settings()->build_settings()->build_args().DeclareArgs(
values, scope, err);
return Value();
}
// defined ---------------------------------------------------------------------
const char kDefined[] = "defined";
const char kDefined_HelpShort[] =
"defined: Returns whether an identifier is defined.";
const char kDefined_Help[] =
"defined: Returns whether an identifier is defined.\n"
"\n"
" Returns true if the given argument is defined. This is most useful in\n"
" templates to assert that the caller set things up properly.\n"
"\n"
" You can pass an identifier:\n"
" defined(foo)\n"
" which will return true or false depending on whether foo is defined in\n"
" the current scope.\n"
"\n"
" You can also check a named scope:\n"
" defined(foo.bar)\n"
" which will return true or false depending on whether bar is defined in\n"
" the named scope foo. It will throw an error if foo is not defined or\n"
" is not a scope.\n"
"\n"
"Example:\n"
"\n"
" template(\"mytemplate\") {\n"
" # To help users call this template properly...\n"
" assert(defined(invoker.sources), \"Sources must be defined\")\n"
"\n"
" # If we want to accept an optional \"values\" argument, we don't\n"
" # want to dereference something that may not be defined.\n"
" if (defined(invoker.values)) {\n"
" values = invoker.values\n"
" } else {\n"
" values = \"some default value\"\n"
" }\n"
" }\n";
Value RunDefined(Scope* scope,
const FunctionCallNode* function,
const ListNode* args_list,
Err* err) {
const auto& args_vector = args_list->contents();
if (args_vector.size() != 1) {
*err = Err(function, "Wrong number of arguments to defined().",
"Expecting exactly one.");
return Value();
}
const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
if (identifier) {
// Passed an identifier "defined(foo)".
if (scope->GetValue(identifier->value().value()))
return Value(function, true);
return Value(function, false);
}
const AccessorNode* accessor = args_vector[0]->AsAccessor();
if (accessor) {
// Passed an accessor "defined(foo.bar)".
if (accessor->member()) {
// The base of the accessor must be a scope if it's defined.
const Value* base = scope->GetValue(accessor->base().value());
if (!base) {
*err = Err(accessor, "Undefined identifier");
return Value();
}
if (!base->VerifyTypeIs(Value::SCOPE, err))
return Value();
// Check the member inside the scope to see if its defined.
if (base->scope_value()->GetValue(accessor->member()->value().value()))
return Value(function, true);
return Value(function, false);
}
}
// Argument is invalid.
*err = Err(function, "Bad thing passed to defined().",
"It should be of the form defined(foo) or defined(foo.bar).");
return Value();
}
// getenv ----------------------------------------------------------------------
const char kGetEnv[] = "getenv";
const char kGetEnv_HelpShort[] =
"getenv: Get an environment variable.";
const char kGetEnv_Help[] =
"getenv: Get an environment variable.\n"
"\n"
" value = getenv(env_var_name)\n"
"\n"
" Returns the value of the given enironment variable. If the value is\n"
" not found, it will try to look up the variable with the \"opposite\"\n"
" case (based on the case of the first letter of the variable), but\n"
" is otherwise case-sensitive.\n"
"\n"
" If the environment variable is not found, the empty string will be\n"
" returned. Note: it might be nice to extend this if we had the concept\n"
" of \"none\" in the language to indicate lookup failure.\n"
"\n"
"Example:\n"
"\n"
" home_dir = getenv(\"HOME\")\n";
Value RunGetEnv(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
Err* err) {
if (!EnsureSingleStringArg(function, args, err))
return Value();
std::unique_ptr<base::Environment> env(base::Environment::Create());
std::string result;
if (!env->GetVar(args[0].string_value().c_str(), &result))
return Value(function, ""); // Not found, return empty string.
return Value(function, result);
}
// import ----------------------------------------------------------------------
const char kImport[] = "import";
const char kImport_HelpShort[] =
"import: Import a file into the current scope.";
const char kImport_Help[] =
"import: Import a file into the current scope.\n"
"\n"
" The import command loads the rules and variables resulting from\n"
" executing the given file into the current scope.\n"
"\n"
" By convention, imported files are named with a .gni extension.\n"
"\n"
" An import is different than a C++ \"include\". The imported file is\n"
" executed in a standalone environment from the caller of the import\n"
" command. The results of this execution are cached for other files that\n"
" import the same .gni file.\n"
"\n"
" Note that you can not import a BUILD.gn file that's otherwise used\n"
" in the build. Files must either be imported or implicitly loaded as\n"
" a result of deps rules, but not both.\n"
"\n"
" The imported file's scope will be merged with the scope at the point\n"
" import was called. If there is a conflict (both the current scope and\n"
" the imported file define some variable or rule with the same name but\n"
" different value), a runtime error will be thrown. Therefore, it's good\n"
" practice to minimize the stuff that an imported file defines.\n"
"\n"
" Variables and templates beginning with an underscore '_' are\n"
" considered private and will not be imported. Imported files can use\n"
" such variables for internal computation without affecting other files.\n"
"\n"
"Examples:\n"
"\n"
" import(\"//build/rules/idl_compilation_rule.gni\")\n"
"\n"
" # Looks in the current directory.\n"
" import(\"my_vars.gni\")\n";
Value RunImport(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
Err* err) {
if (!EnsureSingleStringArg(function, args, err))
return Value();
const SourceDir& input_dir = scope->GetSourceDir();
SourceFile import_file =
input_dir.ResolveRelativeFile(args[0], err,
scope->settings()->build_settings()->root_path_utf8());
if (!err->has_error()) {
scope->settings()->import_manager().DoImport(import_file, function,
scope, err);
}
return Value();
}
// set_sources_assignment_filter -----------------------------------------------
const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
const char kSetSourcesAssignmentFilter_HelpShort[] =
"set_sources_assignment_filter: Set a pattern to filter source files.";
const char kSetSourcesAssignmentFilter_Help[] =
"set_sources_assignment_filter: Set a pattern to filter source files.\n"
"\n"
" The sources assignment filter is a list of patterns that remove files\n"
" from the list implicitly whenever the \"sources\" variable is\n"
" assigned to. This is intended to be used to globally filter out files\n"
" with platform-specific naming schemes when they don't apply, for\n"
" example, you may want to filter out all \"*_win.cc\" files on non-\n"
" Windows platforms.\n"
"\n"
" Typically this will be called once in the master build config script\n"
" to set up the filter for the current platform. Subsequent calls will\n"
" overwrite the previous values.\n"
"\n"
" If you want to bypass the filter and add a file even if it might\n"
" be filtered out, call set_sources_assignment_filter([]) to clear the\n"
" list of filters. This will apply until the current scope exits\n"
"\n"
"How to use patterns\n"
"\n"
" File patterns are VERY limited regular expressions. They must match\n"
" the entire input string to be counted as a match. In regular\n"
" expression parlance, there is an implicit \"^...$\" surrounding your\n"
" input. If you want to match a substring, you need to use wildcards at\n"
" the beginning and end.\n"
"\n"
" There are only two special tokens understood by the pattern matcher.\n"
" Everything else is a literal.\n"
"\n"
" * Matches zero or more of any character. It does not depend on the\n"
" preceding character (in regular expression parlance it is\n"
" equivalent to \".*\").\n"
"\n"
" \\b Matches a path boundary. This will match the beginning or end of\n"
" a string, or a slash.\n"
"\n"
"Pattern examples\n"
"\n"
" \"*asdf*\"\n"
" Matches a string containing \"asdf\" anywhere.\n"
"\n"
" \"asdf\"\n"
" Matches only the exact string \"asdf\".\n"
"\n"
" \"*.cc\"\n"
" Matches strings ending in the literal \".cc\".\n"
"\n"
" \"\\bwin/*\"\n"
" Matches \"win/foo\" and \"foo/win/bar.cc\" but not \"iwin/foo\".\n"
"\n"
"Sources assignment example\n"
"\n"
" # Filter out all _win files.\n"
" set_sources_assignment_filter([ \"*_win.cc\", \"*_win.h\" ])\n"
" sources = [ \"a.cc\", \"b_win.cc\" ]\n"
" print(sources)\n"
" # Will print [ \"a.cc\" ]. b_win one was filtered out.\n";
Value RunSetSourcesAssignmentFilter(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 {
std::unique_ptr<PatternList> f(new PatternList);
f->SetFromValue(args[0], err);
if (!err->has_error())
scope->set_sources_assignment_filter(std::move(f));
}
return Value();
}
// print -----------------------------------------------------------------------
const char kPrint[] = "print";
const char kPrint_HelpShort[] =
"print: Prints to the console.";
const char kPrint_Help[] =
"print: Prints to the console.\n"
"\n"
" Prints all arguments to the console separated by spaces. A newline is\n"
" automatically appended to the end.\n"
"\n"
" This function is intended for debugging. Note that build files are run\n"
" in parallel so you may get interleaved prints. A buildfile may also\n"
" be executed more than once in parallel in the context of different\n"
" toolchains so the prints from one file may be duplicated or\n"
" interleaved with itself.\n"
"\n"
"Examples:\n"
" print(\"Hello world\")\n"
"\n"
" print(sources, deps)\n";
Value RunPrint(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
Err* err) {
std::string output;
for (size_t i = 0; i < args.size(); i++) {
if (i != 0)
output.push_back(' ');
output.append(args[i].ToString(false));
}
output.push_back('\n');
const BuildSettings::PrintCallback& cb =
scope->settings()->build_settings()->print_callback();
if (cb.is_null())
printf("%s", output.c_str());
else
cb.Run(output);
return Value();
}
// -----------------------------------------------------------------------------
FunctionInfo::FunctionInfo()
: self_evaluating_args_runner(nullptr),
generic_block_runner(nullptr),
executed_block_runner(nullptr),
no_block_runner(nullptr),
help_short(nullptr),
help(nullptr),
is_target(false) {
}
FunctionInfo::FunctionInfo(SelfEvaluatingArgsFunction seaf,
const char* in_help_short,
const char* in_help,
bool in_is_target)
: self_evaluating_args_runner(seaf),
generic_block_runner(nullptr),
executed_block_runner(nullptr),
no_block_runner(nullptr),
help_short(in_help_short),
help(in_help),
is_target(in_is_target) {
}
FunctionInfo::FunctionInfo(GenericBlockFunction gbf,
const char* in_help_short,
const char* in_help,
bool in_is_target)
: self_evaluating_args_runner(nullptr),
generic_block_runner(gbf),
executed_block_runner(nullptr),
no_block_runner(nullptr),
help_short(in_help_short),
help(in_help),
is_target(in_is_target) {
}
FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf,
const char* in_help_short,
const char* in_help,
bool in_is_target)
: self_evaluating_args_runner(nullptr),
generic_block_runner(nullptr),
executed_block_runner(ebf),
no_block_runner(nullptr),
help_short(in_help_short),
help(in_help),
is_target(in_is_target) {
}
FunctionInfo::FunctionInfo(NoBlockFunction nbf,
const char* in_help_short,
const char* in_help,
bool in_is_target)
: self_evaluating_args_runner(nullptr),
generic_block_runner(nullptr),
executed_block_runner(nullptr),
no_block_runner(nbf),
help_short(in_help_short),
help(in_help),
is_target(in_is_target) {
}
// Setup the function map via a static initializer. We use this because it
// avoids race conditions without having to do some global setup function or
// locking-heavy singleton checks at runtime. In practice, we always need this
// before we can do anything interesting, so it's OK to wait for the
// initializer.
struct FunctionInfoInitializer {
FunctionInfoMap map;
FunctionInfoInitializer() {
#define INSERT_FUNCTION(command, is_target) \
map[k##command] = FunctionInfo(&Run##command, \
k##command##_HelpShort, \
k##command##_Help, \
is_target);
INSERT_FUNCTION(Action, true)
INSERT_FUNCTION(ActionForEach, true)
INSERT_FUNCTION(BundleData, true)
INSERT_FUNCTION(CreateBundle, true)
INSERT_FUNCTION(Copy, true)
INSERT_FUNCTION(Executable, true)
INSERT_FUNCTION(Group, true)
INSERT_FUNCTION(LoadableModule, true)
INSERT_FUNCTION(SharedLibrary, true)
INSERT_FUNCTION(SourceSet, true)
INSERT_FUNCTION(StaticLibrary, true)
INSERT_FUNCTION(Target, true)
INSERT_FUNCTION(Assert, false)
INSERT_FUNCTION(Config, false)
INSERT_FUNCTION(DeclareArgs, false)
INSERT_FUNCTION(Defined, false)
INSERT_FUNCTION(ExecScript, false)
INSERT_FUNCTION(ForEach, false)
INSERT_FUNCTION(ForwardVariablesFrom, false)
INSERT_FUNCTION(GetEnv, false)
INSERT_FUNCTION(GetLabelInfo, false)
INSERT_FUNCTION(GetPathInfo, false)
INSERT_FUNCTION(GetTargetOutputs, false)
INSERT_FUNCTION(Import, false)
INSERT_FUNCTION(Print, false)
INSERT_FUNCTION(ProcessFileTemplate, false)
INSERT_FUNCTION(ReadFile, false)
INSERT_FUNCTION(RebasePath, false)
INSERT_FUNCTION(SetDefaults, false)
INSERT_FUNCTION(SetDefaultToolchain, false)
INSERT_FUNCTION(SetSourcesAssignmentFilter, false)
INSERT_FUNCTION(Template, false)
INSERT_FUNCTION(Tool, false)
INSERT_FUNCTION(Toolchain, false)
INSERT_FUNCTION(ToolchainArgs, false)
INSERT_FUNCTION(WriteFile, false)
#undef INSERT_FUNCTION
}
};
const FunctionInfoInitializer function_info;
const FunctionInfoMap& GetFunctions() {
return function_info.map;
}
Value RunFunction(Scope* scope,
const FunctionCallNode* function,
const ListNode* args_list,
BlockNode* block,
Err* err) {
const Token& name = function->function();
const FunctionInfoMap& function_map = GetFunctions();
FunctionInfoMap::const_iterator found_function =
function_map.find(name.value());
if (found_function == function_map.end()) {
// No built-in function matching this, check for a template.
const Template* templ =
scope->GetTemplate(function->function().value().as_string());
if (templ) {
Value args = args_list->Execute(scope, err);
if (err->has_error())
return Value();
return templ->Invoke(scope, function, args.list_value(), block, err);
}
*err = Err(name, "Unknown function.");
return Value();
}
if (found_function->second.self_evaluating_args_runner) {
// Self evaluating args functions are special weird built-ins like foreach.
// Rather than force them all to check that they have a block or no block
// and risk bugs for new additions, check a whitelist here.
if (found_function->second.self_evaluating_args_runner != &RunForEach) {
if (!VerifyNoBlockForFunctionCall(function, block, err))
return Value();
}
return found_function->second.self_evaluating_args_runner(
scope, function, args_list, err);
}
// All other function types take a pre-executed set of args.
Value args = args_list->Execute(scope, err);
if (err->has_error())
return Value();
if (found_function->second.generic_block_runner) {
if (!block) {
FillNeedsBlockError(function, err);
return Value();
}
return found_function->second.generic_block_runner(
scope, function, args.list_value(), block, err);
}
if (found_function->second.executed_block_runner) {
if (!block) {
FillNeedsBlockError(function, err);
return Value();
}
Scope block_scope(scope);
block->Execute(&block_scope, err);
if (err->has_error())
return Value();
Value result = found_function->second.executed_block_runner(
function, args.list_value(), &block_scope, err);
if (err->has_error())
return Value();
if (!block_scope.CheckForUnusedVars(err))
return Value();
return result;
}
// Otherwise it's a no-block function.
if (!VerifyNoBlockForFunctionCall(function, block, err))
return Value();
return found_function->second.no_block_runner(scope, function,
args.list_value(), err);
}
} // namespace functions