|  | // 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 <regex> | 
|  | #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 std::string_view 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". | 
|  |  | 
|  | More background | 
|  |  | 
|  | Configs solve a problem where the build system needs to have a higher-level | 
|  | understanding of various compiler settings. For example, some compiler flags | 
|  | have to appear in a certain order relative to each other, some settings like | 
|  | defines and flags logically go together, and the build system needs to | 
|  | de-duplicate flags even though raw command-line parameters can't always be | 
|  | operated on in that way. | 
|  |  | 
|  | The config gives a name to a group of settings that can then be reasoned | 
|  | about by GN. GN can know that configs with the same label are the same thing | 
|  | so can be de-duplicated. It allows related settings to be grouped so they | 
|  | are added or removed as a unit. And it allows targets to refer to settings | 
|  | with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than | 
|  | having to hard-coding every compiler's flags each time they are referred to. | 
|  |  | 
|  | 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") { | 
|  | include_dirs = [ "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. | 
|  | Value scope_value;       // Storage for an evaluated scope. | 
|  | 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) { | 
|  | if (args_cur == args_vector.end()) { | 
|  | *err = Err( | 
|  | function, "Wrong number of arguments.", | 
|  | "The first argument is a scope, expecting two or three arguments."); | 
|  | return Value(); | 
|  | } | 
|  | // Copy the scope value if it will be overridden. | 
|  | if (value == &result_value) { | 
|  | scope_value = Value(nullptr, value->scope_value()->MakeClosure()); | 
|  | source = scope_value.scope_value(); | 
|  | } else { | 
|  | source = value->scope_value(); | 
|  | } | 
|  | result_value = (*args_cur)->Execute(scope, err); | 
|  | if (err->has_error()) | 
|  | return Value(); | 
|  | value = &result_value; | 
|  | args_cur++; | 
|  | } else if (args_vector.size() > 2) { | 
|  | *err = Err( | 
|  | function, "Wrong number of arguments.", | 
|  | "The first argument is not a scope, expecting one or two arguments."); | 
|  | return Value(); | 
|  | } | 
|  |  | 
|  | // 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) { | 
|  | cb(output); | 
|  | } else { | 
|  | printf("%s", output.c_str()); | 
|  | fflush(stdout); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | // string_replace -------------------------------------------------------------- | 
|  |  | 
|  | const char kStringReplace[] = "string_replace"; | 
|  | const char kStringReplace_HelpShort[] = | 
|  | "string_replace: Replaces substring in the given string."; | 
|  | const char kStringReplace_Help[] = | 
|  | R"(string_replace: Replaces substring in the given string. | 
|  |  | 
|  | result = string_replace(str, old, new[, max]) | 
|  |  | 
|  | Returns a copy of the string str in which the occurrences of old have been | 
|  | replaced with new, optionally restricting the number of replacements. The | 
|  | replacement is performed sequentially, so if new contains old, it won't be | 
|  | replaced. | 
|  |  | 
|  | Example | 
|  |  | 
|  | The code: | 
|  | mystr = "Hello, world!" | 
|  | print(string_replace(mystr, "world", "GN")) | 
|  |  | 
|  | Will print: | 
|  | Hello, GN! | 
|  | )"; | 
|  |  | 
|  | Value RunStringReplace(Scope* scope, | 
|  | const FunctionCallNode* function, | 
|  | const std::vector<Value>& args, | 
|  | Err* err) { | 
|  | if (args.size() < 3 || args.size() > 4) { | 
|  | *err = Err(function, "Wrong number of arguments to string_replace()."); | 
|  | return Value(); | 
|  | } | 
|  |  | 
|  | if (!args[0].VerifyTypeIs(Value::STRING, err)) | 
|  | return Value(); | 
|  | const std::string str = args[0].string_value(); | 
|  |  | 
|  | if (!args[1].VerifyTypeIs(Value::STRING, err)) | 
|  | return Value(); | 
|  | const std::string& old = args[1].string_value(); | 
|  |  | 
|  | if (!args[2].VerifyTypeIs(Value::STRING, err)) | 
|  | return Value(); | 
|  | const std::string& new_ = args[2].string_value(); | 
|  |  | 
|  | int64_t max = INT64_MAX; | 
|  | if (args.size() > 3) { | 
|  | if (!args[3].VerifyTypeIs(Value::INTEGER, err)) | 
|  | return Value(); | 
|  | max = args[3].int_value(); | 
|  | if (max <= 0) { | 
|  | *err = Err(function, "Requested number of replacements is not positive."); | 
|  | return Value(); | 
|  | } | 
|  | } | 
|  |  | 
|  | int64_t n = 0; | 
|  | std::string val(str); | 
|  | size_t start_pos = 0; | 
|  | while ((start_pos = val.find(old, start_pos)) != std::string::npos) { | 
|  | val.replace(start_pos, old.length(), new_); | 
|  | start_pos += new_.length(); | 
|  | if (++n >= max) | 
|  | break; | 
|  | } | 
|  | return Value(function, std::move(val)); | 
|  | } | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | 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(GeneratedFile, true) | 
|  | INSERT_FUNCTION(RustLibrary, 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(StringReplace, 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()); | 
|  | 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 |