mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
1297 lines
43 KiB
C++
1297 lines
43 KiB
C++
|
// 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 <memory>
|
||
|
#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_node_value_adapter.h"
|
||
|
#include "tools/gn/parse_tree.h"
|
||
|
#include "tools/gn/pool.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;
|
||
|
}
|
||
|
|
||
|
// This key is set as a scope property on the scope of a declare_args() block,
|
||
|
// in order to prevent reading a variable defined earlier in the same call
|
||
|
// (see `gn help declare_args` for more).
|
||
|
const void *kInDeclareArgsKey = nullptr;
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
|
||
|
bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
|
||
|
const Scope* cur_scope,
|
||
|
const Scope* val_scope,
|
||
|
Err* err) {
|
||
|
// If the value didn't come from a scope at all, we're safe.
|
||
|
if (!val_scope)
|
||
|
return true;
|
||
|
|
||
|
const Scope* val_args_scope = nullptr;
|
||
|
val_scope->GetProperty(&kInDeclareArgsKey, &val_args_scope);
|
||
|
|
||
|
const Scope* cur_args_scope = nullptr;
|
||
|
cur_scope->GetProperty(&kInDeclareArgsKey, &cur_args_scope);
|
||
|
if (!val_args_scope || !cur_args_scope || (val_args_scope != cur_args_scope))
|
||
|
return true;
|
||
|
|
||
|
*err = Err(node,
|
||
|
"Reading a variable defined in the same declare_args() call.\n"
|
||
|
"\n"
|
||
|
"If you need to set the value of one arg based on another, put\n"
|
||
|
"them in two separate declare_args() calls, one after the other.\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
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(variables::kTargetName);
|
||
|
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[] =
|
||
|
R"(assert: Assert an expression is true at generation time.
|
||
|
|
||
|
assert(<condition> [, <error string>])
|
||
|
|
||
|
If the condition is false, the build will fail with an error. If the
|
||
|
optional second argument is provided, that string will be printed
|
||
|
with the error message.
|
||
|
|
||
|
Examples
|
||
|
|
||
|
assert(is_win)
|
||
|
assert(defined(sources), "Sources must be defined");
|
||
|
)";
|
||
|
|
||
|
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[] =
|
||
|
R"(config: Defines a configuration object.
|
||
|
|
||
|
Configuration objects can be applied to targets and specify sets of compiler
|
||
|
flags, includes, defines, etc. They provide a way to conveniently group sets
|
||
|
of this configuration information.
|
||
|
|
||
|
A config is referenced by its label just like a target.
|
||
|
|
||
|
The values in a config are additive only. If you want to remove a flag you
|
||
|
need to remove the corresponding config that sets it. The final set of flags,
|
||
|
defines, etc. for a target is generated in this order:
|
||
|
|
||
|
1. The values specified directly on the target (rather than using a config.
|
||
|
2. The configs specified in the target's "configs" list, in order.
|
||
|
3. Public_configs from a breadth-first traversal of the dependency tree in
|
||
|
the order that the targets appear in "deps".
|
||
|
4. All dependent configs from a breadth-first traversal of the dependency
|
||
|
tree in the order that the targets appear in "deps".
|
||
|
|
||
|
Variables valid in a config definition
|
||
|
)"
|
||
|
|
||
|
CONFIG_VALUES_VARS_HELP
|
||
|
|
||
|
R"( Nested configs: configs
|
||
|
|
||
|
Variables on a target used to apply configs
|
||
|
|
||
|
all_dependent_configs, configs, public_configs
|
||
|
|
||
|
Example
|
||
|
|
||
|
config("myconfig") {
|
||
|
includes = [ "include/common" ]
|
||
|
defines = [ "ENABLE_DOOM_MELON" ]
|
||
|
}
|
||
|
|
||
|
executable("mything") {
|
||
|
configs = [ ":myconfig" ]
|
||
|
}
|
||
|
)";
|
||
|
|
||
|
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 = std::make_unique<Config>(
|
||
|
scope->settings(), label, scope->build_dependency_files());
|
||
|
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(std::move(config));
|
||
|
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// declare_args ----------------------------------------------------------------
|
||
|
|
||
|
const char kDeclareArgs[] = "declare_args";
|
||
|
const char kDeclareArgs_HelpShort[] =
|
||
|
"declare_args: Declare build arguments.";
|
||
|
const char kDeclareArgs_Help[] =
|
||
|
R"(declare_args: Declare build arguments.
|
||
|
|
||
|
Introduces the given arguments into the current scope. If they are not
|
||
|
specified on the command line or in a toolchain's arguments, the default
|
||
|
values given in the declare_args block will be used. However, these defaults
|
||
|
will not override command-line values.
|
||
|
|
||
|
See also "gn help buildargs" for an overview.
|
||
|
|
||
|
The precise behavior of declare args is:
|
||
|
|
||
|
1. The declare_args() block executes. Any variable defined in the enclosing
|
||
|
scope is available for reading, but any variable defined earlier in
|
||
|
the current scope is not (since the overrides haven't been applied yet).
|
||
|
|
||
|
2. At the end of executing the block, any variables set within that scope
|
||
|
are saved globally as build arguments, with their current values being
|
||
|
saved as the "default value" for that argument.
|
||
|
|
||
|
3. User-defined overrides are applied. Anything set in "gn args" now
|
||
|
overrides any default values. The resulting set of variables is promoted
|
||
|
to be readable from the following code in the file.
|
||
|
|
||
|
This has some ramifications that may not be obvious:
|
||
|
|
||
|
- You should not perform difficult work inside a declare_args block since
|
||
|
this only sets a default value that may be discarded. In particular,
|
||
|
don't use the result of exec_script() to set the default value. If you
|
||
|
want to have a script-defined default, set some default "undefined" value
|
||
|
like [], "", or -1, and after the declare_args block, call exec_script if
|
||
|
the value is unset by the user.
|
||
|
|
||
|
- Because you cannot read the value of a variable defined in the same
|
||
|
block, if you need to make the default value of one arg depend
|
||
|
on the possibly-overridden value of another, write two separate
|
||
|
declare_args() blocks:
|
||
|
|
||
|
declare_args() {
|
||
|
enable_foo = true
|
||
|
}
|
||
|
declare_args() {
|
||
|
# Bar defaults to same user-overridden state as foo.
|
||
|
enable_bar = enable_foo
|
||
|
}
|
||
|
|
||
|
Example
|
||
|
|
||
|
declare_args() {
|
||
|
enable_teleporter = true
|
||
|
enable_doom_melon = false
|
||
|
}
|
||
|
|
||
|
If you want to override the (default disabled) Doom Melon:
|
||
|
gn --args="enable_doom_melon=true enable_teleporter=true"
|
||
|
This also sets the teleporter, but it's already defaulted to on so it will
|
||
|
have no effect.
|
||
|
)";
|
||
|
|
||
|
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_scope.SetProperty(&kInDeclareArgsKey, &block_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[] =
|
||
|
R"(defined: Returns whether an identifier is defined.
|
||
|
|
||
|
Returns true if the given argument is defined. This is most useful in
|
||
|
templates to assert that the caller set things up properly.
|
||
|
|
||
|
You can pass an identifier:
|
||
|
defined(foo)
|
||
|
which will return true or false depending on whether foo is defined in the
|
||
|
current scope.
|
||
|
|
||
|
You can also check a named scope:
|
||
|
defined(foo.bar)
|
||
|
which will return true or false depending on whether bar is defined in the
|
||
|
named scope foo. It will throw an error if foo is not defined or is not a
|
||
|
scope.
|
||
|
|
||
|
Example
|
||
|
|
||
|
template("mytemplate") {
|
||
|
# To help users call this template properly...
|
||
|
assert(defined(invoker.sources), "Sources must be defined")
|
||
|
|
||
|
# If we want to accept an optional "values" argument, we don't
|
||
|
# want to dereference something that may not be defined.
|
||
|
if (defined(invoker.values)) {
|
||
|
values = invoker.values
|
||
|
} else {
|
||
|
values = "some default value"
|
||
|
}
|
||
|
}
|
||
|
)";
|
||
|
|
||
|
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[] =
|
||
|
R"(getenv: Get an environment variable.
|
||
|
|
||
|
value = getenv(env_var_name)
|
||
|
|
||
|
Returns the value of the given environment variable. If the value is not
|
||
|
found, it will try to look up the variable with the "opposite" case (based on
|
||
|
the case of the first letter of the variable), but is otherwise
|
||
|
case-sensitive.
|
||
|
|
||
|
If the environment variable is not found, the empty string will be returned.
|
||
|
Note: it might be nice to extend this if we had the concept of "none" in the
|
||
|
language to indicate lookup failure.
|
||
|
|
||
|
Example
|
||
|
|
||
|
home_dir = getenv("HOME")
|
||
|
)";
|
||
|
|
||
|
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[] =
|
||
|
R"(import: Import a file into the current scope.
|
||
|
|
||
|
The import command loads the rules and variables resulting from executing the
|
||
|
given file into the current scope.
|
||
|
|
||
|
By convention, imported files are named with a .gni extension.
|
||
|
|
||
|
An import is different than a C++ "include". The imported file is executed in
|
||
|
a standalone environment from the caller of the import command. The results
|
||
|
of this execution are cached for other files that import the same .gni file.
|
||
|
|
||
|
Note that you can not import a BUILD.gn file that's otherwise used in the
|
||
|
build. Files must either be imported or implicitly loaded as a result of deps
|
||
|
rules, but not both.
|
||
|
|
||
|
The imported file's scope will be merged with the scope at the point import
|
||
|
was called. If there is a conflict (both the current scope and the imported
|
||
|
file define some variable or rule with the same name but different value), a
|
||
|
runtime error will be thrown. Therefore, it's good practice to minimize the
|
||
|
stuff that an imported file defines.
|
||
|
|
||
|
Variables and templates beginning with an underscore '_' are considered
|
||
|
private and will not be imported. Imported files can use such variables for
|
||
|
internal computation without affecting other files.
|
||
|
|
||
|
Examples
|
||
|
|
||
|
import("//build/rules/idl_compilation_rule.gni")
|
||
|
|
||
|
# Looks in the current directory.
|
||
|
import("my_vars.gni")
|
||
|
)";
|
||
|
|
||
|
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());
|
||
|
scope->AddBuildDependencyFile(import_file);
|
||
|
if (!err->has_error()) {
|
||
|
scope->settings()->import_manager().DoImport(import_file, function,
|
||
|
scope, err);
|
||
|
}
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// not_needed -----------------------------------------------------------------
|
||
|
|
||
|
const char kNotNeeded[] = "not_needed";
|
||
|
const char kNotNeeded_HelpShort[] =
|
||
|
"not_needed: Mark variables from scope as not needed.";
|
||
|
const char kNotNeeded_Help[] =
|
||
|
R"(not_needed: Mark variables from scope as not needed.
|
||
|
|
||
|
not_needed(variable_list_or_star, variable_to_ignore_list = [])
|
||
|
not_needed(from_scope, variable_list_or_star,
|
||
|
variable_to_ignore_list = [])
|
||
|
|
||
|
Mark the variables in the current or given scope as not needed, which means
|
||
|
you will not get an error about unused variables for these. The
|
||
|
variable_to_ignore_list allows excluding variables from "all matches" if
|
||
|
variable_list_or_star is "*".
|
||
|
|
||
|
Example
|
||
|
|
||
|
not_needed("*", [ "config" ])
|
||
|
not_needed([ "data_deps", "deps" ])
|
||
|
not_needed(invoker, "*", [ "config" ])
|
||
|
not_needed(invoker, [ "data_deps", "deps" ])
|
||
|
)";
|
||
|
|
||
|
Value RunNotNeeded(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const ListNode* args_list,
|
||
|
Err* err) {
|
||
|
const auto& args_vector = args_list->contents();
|
||
|
if (args_vector.size() < 1 || args_vector.size() > 3) {
|
||
|
*err = Err(function, "Wrong number of arguments.",
|
||
|
"Expecting one, two or three arguments.");
|
||
|
return Value();
|
||
|
}
|
||
|
auto args_cur = args_vector.begin();
|
||
|
|
||
|
Value* value = nullptr; // Value to use, may point to result_value.
|
||
|
Value result_value; // Storage for the "evaluate" case.
|
||
|
const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
|
||
|
if (identifier) {
|
||
|
// Optimize the common case where the input scope is an identifier. This
|
||
|
// prevents a copy of a potentially large Scope object.
|
||
|
value = scope->GetMutableValue(identifier->value().value(),
|
||
|
Scope::SEARCH_NESTED, true);
|
||
|
if (!value) {
|
||
|
*err = Err(identifier, "Undefined identifier.");
|
||
|
return Value();
|
||
|
}
|
||
|
} else {
|
||
|
// Non-optimized case, just evaluate the argument.
|
||
|
result_value = (*args_cur)->Execute(scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
value = &result_value;
|
||
|
}
|
||
|
args_cur++;
|
||
|
|
||
|
// Extract the source scope if different from current one.
|
||
|
Scope* source = scope;
|
||
|
if (value->type() == Value::SCOPE) {
|
||
|
source = value->scope_value();
|
||
|
result_value = (*args_cur)->Execute(scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
value = &result_value;
|
||
|
args_cur++;
|
||
|
}
|
||
|
|
||
|
// Extract the exclusion list if defined.
|
||
|
Value exclusion_value;
|
||
|
std::set<std::string> exclusion_set;
|
||
|
if (args_cur != args_vector.end()) {
|
||
|
exclusion_value = (*args_cur)->Execute(source, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
if (exclusion_value.type() != Value::LIST) {
|
||
|
*err = Err(exclusion_value, "Not a valid list of variables to exclude.",
|
||
|
"Expecting a list of strings.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
for (const Value& cur : exclusion_value.list_value()) {
|
||
|
if (!cur.VerifyTypeIs(Value::STRING, err))
|
||
|
return Value();
|
||
|
|
||
|
exclusion_set.insert(cur.string_value());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (value->type() == Value::STRING) {
|
||
|
if (value->string_value() == "*") {
|
||
|
source->MarkAllUsed(exclusion_set);
|
||
|
return Value();
|
||
|
}
|
||
|
} else if (value->type() == Value::LIST) {
|
||
|
if (exclusion_value.type() != Value::NONE) {
|
||
|
*err = Err(exclusion_value, "Not supported with a variable list.",
|
||
|
"Exclusion list can only be used with the string \"*\".");
|
||
|
return Value();
|
||
|
}
|
||
|
for (const Value& cur : value->list_value()) {
|
||
|
if (!cur.VerifyTypeIs(Value::STRING, err))
|
||
|
return Value();
|
||
|
if (!source->GetValue(cur.string_value(), true)) {
|
||
|
*err = Err(cur, "Undefined identifier");
|
||
|
return Value();
|
||
|
}
|
||
|
}
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// Not the right type of argument.
|
||
|
*err = Err(*value, "Not a valid list of variables.",
|
||
|
"Expecting either the string \"*\" or a list of strings.");
|
||
|
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[] =
|
||
|
R"(set_sources_assignment_filter: Set a pattern to filter source files.
|
||
|
|
||
|
The sources assignment filter is a list of patterns that remove files from
|
||
|
the list implicitly whenever the "sources" variable is assigned to. This will
|
||
|
do nothing for non-lists.
|
||
|
|
||
|
This is intended to be used to globally filter out files with
|
||
|
platform-specific naming schemes when they don't apply, for example you may
|
||
|
want to filter out all "*_win.cc" files on non-Windows platforms.
|
||
|
|
||
|
Typically this will be called once in the master build config script to set
|
||
|
up the filter for the current platform. Subsequent calls will overwrite the
|
||
|
previous values.
|
||
|
|
||
|
If you want to bypass the filter and add a file even if it might be filtered
|
||
|
out, call set_sources_assignment_filter([]) to clear the list of filters.
|
||
|
This will apply until the current scope exits
|
||
|
|
||
|
How to use patterns
|
||
|
|
||
|
File patterns are VERY limited regular expressions. They must match the
|
||
|
entire input string to be counted as a match. In regular expression parlance,
|
||
|
there is an implicit "^...$" surrounding your input. If you want to match a
|
||
|
substring, you need to use wildcards at the beginning and end.
|
||
|
|
||
|
There are only two special tokens understood by the pattern matcher.
|
||
|
Everything else is a literal.
|
||
|
|
||
|
- "*" Matches zero or more of any character. It does not depend on the
|
||
|
preceding character (in regular expression parlance it is equivalent to
|
||
|
".*").
|
||
|
|
||
|
- "\b" Matches a path boundary. This will match the beginning or end of a
|
||
|
string, or a slash.
|
||
|
|
||
|
Pattern examples
|
||
|
|
||
|
"*asdf*"
|
||
|
Matches a string containing "asdf" anywhere.
|
||
|
|
||
|
"asdf"
|
||
|
Matches only the exact string "asdf".
|
||
|
|
||
|
"*.cc"
|
||
|
Matches strings ending in the literal ".cc".
|
||
|
|
||
|
"\bwin/*"
|
||
|
Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
|
||
|
|
||
|
Sources assignment example
|
||
|
|
||
|
# Filter out all _win files.
|
||
|
set_sources_assignment_filter([ "*_win.cc", "*_win.h" ])
|
||
|
sources = [ "a.cc", "b_win.cc" ]
|
||
|
print(sources)
|
||
|
# Will print [ "a.cc" ]. b_win one was filtered out.
|
||
|
)";
|
||
|
|
||
|
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 = std::make_unique<PatternList>();
|
||
|
f->SetFromValue(args[0], err);
|
||
|
if (!err->has_error())
|
||
|
scope->set_sources_assignment_filter(std::move(f));
|
||
|
}
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// pool ------------------------------------------------------------------------
|
||
|
|
||
|
const char kPool[] = "pool";
|
||
|
const char kPool_HelpShort[] =
|
||
|
"pool: Defines a pool object.";
|
||
|
const char kPool_Help[] =
|
||
|
R"*(pool: Defines a pool object.
|
||
|
|
||
|
Pool objects can be applied to a tool to limit the parallelism of the
|
||
|
build. This object has a single property "depth" corresponding to
|
||
|
the number of tasks that may run simultaneously.
|
||
|
|
||
|
As the file containing the pool definition may be executed in the
|
||
|
context of more than one toolchain it is recommended to specify an
|
||
|
explicit toolchain when defining and referencing a pool.
|
||
|
|
||
|
A pool named "console" defined in the root build file represents Ninja's
|
||
|
console pool. Targets using this pool will have access to the console's
|
||
|
stdin and stdout, and output will not be buffered. This special pool must
|
||
|
have a depth of 1. Pools not defined in the root must not be named "console".
|
||
|
The console pool can only be defined for the default toolchain.
|
||
|
Refer to the Ninja documentation on the console pool for more info.
|
||
|
|
||
|
A pool is referenced by its label just like a target.
|
||
|
|
||
|
Variables
|
||
|
|
||
|
depth*
|
||
|
* = required
|
||
|
|
||
|
Example
|
||
|
|
||
|
if (current_toolchain == default_toolchain) {
|
||
|
pool("link_pool") {
|
||
|
depth = 1
|
||
|
}
|
||
|
}
|
||
|
|
||
|
toolchain("toolchain") {
|
||
|
tool("link") {
|
||
|
command = "..."
|
||
|
pool = ":link_pool($default_toolchain)")
|
||
|
}
|
||
|
}
|
||
|
)*";
|
||
|
|
||
|
const char kDepth[] = "depth";
|
||
|
|
||
|
Value RunPool(const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
Scope* scope,
|
||
|
Err* err) {
|
||
|
NonNestableBlock non_nestable(scope, function, "pool");
|
||
|
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 pool", label.GetUserVisibleName(true));
|
||
|
|
||
|
// Get the pool depth. It is an error to define a pool without a depth,
|
||
|
// so check first for the presence of the value.
|
||
|
const Value* depth = scope->GetValue(kDepth, true);
|
||
|
if (!depth) {
|
||
|
*err = Err(function, "Can't define a pool without depth.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
if (!depth->VerifyTypeIs(Value::INTEGER, err))
|
||
|
return Value();
|
||
|
|
||
|
if (depth->int_value() < 0) {
|
||
|
*err = Err(*depth, "depth must be positive or 0.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// Create the new pool.
|
||
|
std::unique_ptr<Pool> pool = std::make_unique<Pool>(
|
||
|
scope->settings(), label, scope->build_dependency_files());
|
||
|
|
||
|
if (label.name() == "console") {
|
||
|
const Settings* settings = scope->settings();
|
||
|
if (!settings->is_default()) {
|
||
|
*err = Err(
|
||
|
function,
|
||
|
"\"console\" pool must be defined only in the default toolchain.");
|
||
|
return Value();
|
||
|
}
|
||
|
if (label.dir() != settings->build_settings()->root_target_label().dir()) {
|
||
|
*err = Err(function, "\"console\" pool must be defined in the root //.");
|
||
|
return Value();
|
||
|
}
|
||
|
if (depth->int_value() != 1) {
|
||
|
*err = Err(*depth, "\"console\" pool must have depth 1.");
|
||
|
return Value();
|
||
|
}
|
||
|
}
|
||
|
pool->set_depth(depth->int_value());
|
||
|
|
||
|
// Save the generated item.
|
||
|
Scope::ItemVector* collector = scope->GetItemCollector();
|
||
|
if (!collector) {
|
||
|
*err = Err(function, "Can't define a pool in this context.");
|
||
|
return Value();
|
||
|
}
|
||
|
collector->push_back(std::move(pool));
|
||
|
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// print -----------------------------------------------------------------------
|
||
|
|
||
|
const char kPrint[] = "print";
|
||
|
const char kPrint_HelpShort[] =
|
||
|
"print: Prints to the console.";
|
||
|
const char kPrint_Help[] =
|
||
|
R"(print: Prints to the console.
|
||
|
|
||
|
Prints all arguments to the console separated by spaces. A newline is
|
||
|
automatically appended to the end.
|
||
|
|
||
|
This function is intended for debugging. Note that build files are run in
|
||
|
parallel so you may get interleaved prints. A buildfile may also be executed
|
||
|
more than once in parallel in the context of different toolchains so the
|
||
|
prints from one file may be duplicated or
|
||
|
interleaved with itself.
|
||
|
|
||
|
Examples
|
||
|
|
||
|
print("Hello world")
|
||
|
|
||
|
print(sources, deps)
|
||
|
)";
|
||
|
|
||
|
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());
|
||
|
fflush(stdout);
|
||
|
} else
|
||
|
cb.Run(output);
|
||
|
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// split_list ------------------------------------------------------------------
|
||
|
|
||
|
const char kSplitList[] = "split_list";
|
||
|
const char kSplitList_HelpShort[] =
|
||
|
"split_list: Splits a list into N different sub-lists.";
|
||
|
const char kSplitList_Help[] =
|
||
|
R"(split_list: Splits a list into N different sub-lists.
|
||
|
|
||
|
result = split_list(input, n)
|
||
|
|
||
|
Given a list and a number N, splits the list into N sub-lists of
|
||
|
approximately equal size. The return value is a list of the sub-lists. The
|
||
|
result will always be a list of size N. If N is greater than the number of
|
||
|
elements in the input, it will be padded with empty lists.
|
||
|
|
||
|
The expected use is to divide source files into smaller uniform chunks.
|
||
|
|
||
|
Example
|
||
|
|
||
|
The code:
|
||
|
mylist = [1, 2, 3, 4, 5, 6]
|
||
|
print(split_list(mylist, 3))
|
||
|
|
||
|
Will print:
|
||
|
[[1, 2], [3, 4], [5, 6]
|
||
|
)";
|
||
|
Value RunSplitList(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const ListNode* args_list,
|
||
|
Err* err) {
|
||
|
const auto& args_vector = args_list->contents();
|
||
|
if (args_vector.size() != 2) {
|
||
|
*err = Err(function, "Wrong number of arguments to split_list().",
|
||
|
"Expecting exactly two.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
ParseNodeValueAdapter list_adapter;
|
||
|
if (!list_adapter.InitForType(scope, args_vector[0].get(), Value::LIST, err))
|
||
|
return Value();
|
||
|
const std::vector<Value>& input = list_adapter.get().list_value();
|
||
|
|
||
|
ParseNodeValueAdapter count_adapter;
|
||
|
if (!count_adapter.InitForType(scope, args_vector[1].get(), Value::INTEGER,
|
||
|
err))
|
||
|
return Value();
|
||
|
int64_t count = count_adapter.get().int_value();
|
||
|
if (count <= 0) {
|
||
|
*err = Err(function, "Requested result size is not positive.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
Value result(function, Value::LIST);
|
||
|
result.list_value().resize(count);
|
||
|
|
||
|
// Every result list gets at least this many items in it.
|
||
|
int64_t min_items_per_list = static_cast<int64_t>(input.size()) / count;
|
||
|
|
||
|
// This many result lists get an extra item which is the remainder from above.
|
||
|
int64_t extra_items = static_cast<int64_t>(input.size()) % count;
|
||
|
|
||
|
// Allocate all lists that have a remainder assigned to them (max items).
|
||
|
int64_t max_items_per_list = min_items_per_list + 1;
|
||
|
auto last_item_end = input.begin();
|
||
|
for (int64_t i = 0; i < extra_items; i++) {
|
||
|
result.list_value()[i] = Value(function, Value::LIST);
|
||
|
|
||
|
auto begin_add = last_item_end;
|
||
|
last_item_end += max_items_per_list;
|
||
|
result.list_value()[i].list_value().assign(begin_add, last_item_end);
|
||
|
}
|
||
|
|
||
|
// Allocate all smaller items that don't have a remainder.
|
||
|
for (int64_t i = extra_items; i < count; i++) {
|
||
|
result.list_value()[i] = Value(function, Value::LIST);
|
||
|
|
||
|
auto begin_add = last_item_end;
|
||
|
last_item_end += min_items_per_list;
|
||
|
result.list_value()[i].list_value().assign(begin_add, last_item_end);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
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(NotNeeded, false)
|
||
|
INSERT_FUNCTION(Pool, 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(SplitList, false)
|
||
|
INSERT_FUNCTION(Template, false)
|
||
|
INSERT_FUNCTION(Tool, false)
|
||
|
INSERT_FUNCTION(Toolchain, 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();
|
||
|
|
||
|
std::string template_name = function->function().value().as_string();
|
||
|
const Template* templ = scope->GetTemplate(template_name);
|
||
|
if (templ) {
|
||
|
Value args = args_list->Execute(scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
return templ->Invoke(scope, function, template_name, args.list_value(),
|
||
|
block, err);
|
||
|
}
|
||
|
|
||
|
// No template matching this, check for a built-in function.
|
||
|
const FunctionInfoMap& function_map = GetFunctions();
|
||
|
FunctionInfoMap::const_iterator found_function =
|
||
|
function_map.find(name.value());
|
||
|
if (found_function == function_map.end()) {
|
||
|
*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
|