| // 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 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". | |
| 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.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; | |
| } | |
| // 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().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 |