summaryrefslogtreecommitdiff
path: root/gn/tools/gn/functions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gn/tools/gn/functions.cc')
-rw-r--r--gn/tools/gn/functions.cc1354
1 files changed, 1354 insertions, 0 deletions
diff --git a/gn/tools/gn/functions.cc b/gn/tools/gn/functions.cc
new file mode 100644
index 00000000000..7fc8b62b3db
--- /dev/null
+++ b/gn/tools/gn/functions.cc
@@ -0,0 +1,1354 @@
+// 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".
+
+Variables valid in a config definition
+)"
+
+ CONFIG_VALUES_VARS_HELP
+
+ R"( Nested configs: configs
+
+Variables on a target used to apply configs
+
+ all_dependent_configs, configs, public_configs
+
+Example
+
+ config("myconfig") {
+ includes = [ "include/common" ]
+ defines = [ "ENABLE_DOOM_MELON" ]
+ }
+
+ executable("mything") {
+ configs = [ ":myconfig" ]
+ }
+)";
+
+Value RunConfig(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* scope,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "config");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
+
+ // Create the new config.
+ std::unique_ptr<Config> config = std::make_unique<Config>(
+ scope->settings(), label, scope->build_dependency_files());
+ config->set_defined_from(function);
+ if (!Visibility::FillItemVisibility(config.get(), scope, err))
+ return Value();
+
+ // Fill the flags and such.
+ const SourceDir& input_dir = scope->GetSourceDir();
+ ConfigValuesGenerator gen(&config->own_values(), scope, input_dir, err);
+ gen.Run();
+ if (err->has_error())
+ return Value();
+
+ // Read sub-configs.
+ const Value* configs_value = scope->GetValue(variables::kConfigs, true);
+ if (configs_value) {
+ ExtractListOfUniqueLabels(*configs_value, scope->GetSourceDir(),
+ ToolchainLabelForScope(scope), &config->configs(),
+ err);
+ }
+ if (err->has_error())
+ return Value();
+
+ // Save the generated item.
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "Can't define a config in this context.");
+ return Value();
+ }
+ collector->push_back(std::move(config));
+
+ return Value();
+}
+
+// declare_args ----------------------------------------------------------------
+
+const char kDeclareArgs[] = "declare_args";
+const char kDeclareArgs_HelpShort[] = "declare_args: Declare build arguments.";
+const char kDeclareArgs_Help[] =
+ R"(declare_args: Declare build arguments.
+
+ Introduces the given arguments into the current scope. If they are not
+ specified on the command line or in a toolchain's arguments, the default
+ values given in the declare_args block will be used. However, these defaults
+ will not override command-line values.
+
+ See also "gn help buildargs" for an overview.
+
+ The precise behavior of declare args is:
+
+ 1. The declare_args() block executes. Any variable defined in the enclosing
+ scope is available for reading, but any variable defined earlier in
+ the current scope is not (since the overrides haven't been applied yet).
+
+ 2. At the end of executing the block, any variables set within that scope
+ are saved globally as build arguments, with their current values being
+ saved as the "default value" for that argument.
+
+ 3. User-defined overrides are applied. Anything set in "gn args" now
+ overrides any default values. The resulting set of variables is promoted
+ to be readable from the following code in the file.
+
+ This has some ramifications that may not be obvious:
+
+ - You should not perform difficult work inside a declare_args block since
+ this only sets a default value that may be discarded. In particular,
+ don't use the result of exec_script() to set the default value. If you
+ want to have a script-defined default, set some default "undefined" value
+ like [], "", or -1, and after the declare_args block, call exec_script if
+ the value is unset by the user.
+
+ - Because you cannot read the value of a variable defined in the same
+ block, if you need to make the default value of one arg depend
+ on the possibly-overridden value of another, write two separate
+ declare_args() blocks:
+
+ declare_args() {
+ enable_foo = true
+ }
+ declare_args() {
+ # Bar defaults to same user-overridden state as foo.
+ enable_bar = enable_foo
+ }
+
+Example
+
+ declare_args() {
+ enable_teleporter = true
+ enable_doom_melon = false
+ }
+
+ If you want to override the (default disabled) Doom Melon:
+ gn --args="enable_doom_melon=true enable_teleporter=true"
+ This also sets the teleporter, but it's already defaulted to on so it will
+ have no effect.
+)";
+
+Value RunDeclareArgs(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ BlockNode* block,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "declare_args");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ Scope block_scope(scope);
+ block_scope.SetProperty(&kInDeclareArgsKey, &block_scope);
+ block->Execute(&block_scope, err);
+ if (err->has_error())
+ return Value();
+
+ // Pass the values from our scope into the Args object for adding to the
+ // scope with the proper values (taking into account the defaults given in
+ // the block_scope, and arguments passed into the build).
+ Scope::KeyValueMap values;
+ block_scope.GetCurrentScopeValues(&values);
+ scope->settings()->build_settings()->build_args().DeclareArgs(values, scope,
+ err);
+ return Value();
+}
+
+// defined ---------------------------------------------------------------------
+
+const char kDefined[] = "defined";
+const char kDefined_HelpShort[] =
+ "defined: Returns whether an identifier is defined.";
+const char kDefined_Help[] =
+ R"(defined: Returns whether an identifier is defined.
+
+ Returns true if the given argument is defined. This is most useful in
+ templates to assert that the caller set things up properly.
+
+ You can pass an identifier:
+ defined(foo)
+ which will return true or false depending on whether foo is defined in the
+ current scope.
+
+ You can also check a named scope:
+ defined(foo.bar)
+ which will return true or false depending on whether bar is defined in the
+ named scope foo. It will throw an error if foo is not defined or is not a
+ scope.
+
+Example
+
+ template("mytemplate") {
+ # To help users call this template properly...
+ assert(defined(invoker.sources), "Sources must be defined")
+
+ # If we want to accept an optional "values" argument, we don't
+ # want to dereference something that may not be defined.
+ if (defined(invoker.values)) {
+ values = invoker.values
+ } else {
+ values = "some default value"
+ }
+ }
+)";
+
+Value RunDefined(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 1) {
+ *err = Err(function, "Wrong number of arguments to defined().",
+ "Expecting exactly one.");
+ return Value();
+ }
+
+ const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
+ if (identifier) {
+ // Passed an identifier "defined(foo)".
+ if (scope->GetValue(identifier->value().value()))
+ return Value(function, true);
+ return Value(function, false);
+ }
+
+ const AccessorNode* accessor = args_vector[0]->AsAccessor();
+ if (accessor) {
+ // Passed an accessor "defined(foo.bar)".
+ if (accessor->member()) {
+ // The base of the accessor must be a scope if it's defined.
+ const Value* base = scope->GetValue(accessor->base().value());
+ if (!base) {
+ *err = Err(accessor, "Undefined identifier");
+ return Value();
+ }
+ if (!base->VerifyTypeIs(Value::SCOPE, err))
+ return Value();
+
+ // Check the member inside the scope to see if its defined.
+ if (base->scope_value()->GetValue(accessor->member()->value().value()))
+ return Value(function, true);
+ return Value(function, false);
+ }
+ }
+
+ // Argument is invalid.
+ *err = Err(function, "Bad thing passed to defined().",
+ "It should be of the form defined(foo) or defined(foo.bar).");
+ return Value();
+}
+
+// getenv ----------------------------------------------------------------------
+
+const char kGetEnv[] = "getenv";
+const char kGetEnv_HelpShort[] = "getenv: Get an environment variable.";
+const char kGetEnv_Help[] =
+ R"(getenv: Get an environment variable.
+
+ value = getenv(env_var_name)
+
+ Returns the value of the given environment variable. If the value is not
+ found, it will try to look up the variable with the "opposite" case (based on
+ the case of the first letter of the variable), but is otherwise
+ case-sensitive.
+
+ If the environment variable is not found, the empty string will be returned.
+ Note: it might be nice to extend this if we had the concept of "none" in the
+ language to indicate lookup failure.
+
+Example
+
+ home_dir = getenv("HOME")
+)";
+
+Value RunGetEnv(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+
+ std::unique_ptr<base::Environment> env(base::Environment::Create());
+
+ std::string result;
+ if (!env->GetVar(args[0].string_value().c_str(), &result))
+ return Value(function, ""); // Not found, return empty string.
+ return Value(function, result);
+}
+
+// import ----------------------------------------------------------------------
+
+const char kImport[] = "import";
+const char kImport_HelpShort[] =
+ "import: Import a file into the current scope.";
+const char kImport_Help[] =
+ R"(import: Import a file into the current scope.
+
+ The import command loads the rules and variables resulting from executing the
+ given file into the current scope.
+
+ By convention, imported files are named with a .gni extension.
+
+ An import is different than a C++ "include". The imported file is executed in
+ a standalone environment from the caller of the import command. The results
+ of this execution are cached for other files that import the same .gni file.
+
+ Note that you can not import a BUILD.gn file that's otherwise used in the
+ build. Files must either be imported or implicitly loaded as a result of deps
+ rules, but not both.
+
+ The imported file's scope will be merged with the scope at the point import
+ was called. If there is a conflict (both the current scope and the imported
+ file define some variable or rule with the same name but different value), a
+ runtime error will be thrown. Therefore, it's good practice to minimize the
+ stuff that an imported file defines.
+
+ Variables and templates beginning with an underscore '_' are considered
+ private and will not be imported. Imported files can use such variables for
+ internal computation without affecting other files.
+
+Examples
+
+ import("//build/rules/idl_compilation_rule.gni")
+
+ # Looks in the current directory.
+ import("my_vars.gni")
+)";
+
+Value RunImport(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (!EnsureSingleStringArg(function, args, err))
+ return Value();
+
+ const SourceDir& input_dir = scope->GetSourceDir();
+ SourceFile import_file = input_dir.ResolveRelativeFile(
+ args[0], err, scope->settings()->build_settings()->root_path_utf8());
+ scope->AddBuildDependencyFile(import_file);
+ if (!err->has_error()) {
+ scope->settings()->import_manager().DoImport(import_file, function, scope,
+ err);
+ }
+ return Value();
+}
+
+// not_needed -----------------------------------------------------------------
+
+const char kNotNeeded[] = "not_needed";
+const char kNotNeeded_HelpShort[] =
+ "not_needed: Mark variables from scope as not needed.";
+const char kNotNeeded_Help[] =
+ R"(not_needed: Mark variables from scope as not needed.
+
+ not_needed(variable_list_or_star, variable_to_ignore_list = [])
+ not_needed(from_scope, variable_list_or_star,
+ variable_to_ignore_list = [])
+
+ Mark the variables in the current or given scope as not needed, which means
+ you will not get an error about unused variables for these. The
+ variable_to_ignore_list allows excluding variables from "all matches" if
+ variable_list_or_star is "*".
+
+Example
+
+ not_needed("*", [ "config" ])
+ not_needed([ "data_deps", "deps" ])
+ not_needed(invoker, "*", [ "config" ])
+ not_needed(invoker, [ "data_deps", "deps" ])
+)";
+
+Value RunNotNeeded(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() < 1 || args_vector.size() > 3) {
+ *err = Err(function, "Wrong number of arguments.",
+ "Expecting one, two or three arguments.");
+ return Value();
+ }
+ auto args_cur = args_vector.begin();
+
+ Value* value = nullptr; // Value to use, may point to result_value.
+ Value result_value; // Storage for the "evaluate" case.
+ const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
+ if (identifier) {
+ // Optimize the common case where the input scope is an identifier. This
+ // prevents a copy of a potentially large Scope object.
+ value = scope->GetMutableValue(identifier->value().value(),
+ Scope::SEARCH_NESTED, true);
+ if (!value) {
+ *err = Err(identifier, "Undefined identifier.");
+ return Value();
+ }
+ } else {
+ // Non-optimized case, just evaluate the argument.
+ result_value = (*args_cur)->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ }
+ args_cur++;
+
+ // Extract the source scope if different from current one.
+ Scope* source = scope;
+ if (value->type() == Value::SCOPE) {
+ source = value->scope_value();
+ result_value = (*args_cur)->Execute(scope, err);
+ if (err->has_error())
+ return Value();
+ value = &result_value;
+ args_cur++;
+ }
+
+ // Extract the exclusion list if defined.
+ Value exclusion_value;
+ std::set<std::string> exclusion_set;
+ if (args_cur != args_vector.end()) {
+ exclusion_value = (*args_cur)->Execute(source, err);
+ if (err->has_error())
+ return Value();
+
+ if (exclusion_value.type() != Value::LIST) {
+ *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
+ "Expecting a list of strings.");
+ return Value();
+ }
+
+ for (const Value& cur : exclusion_value.list_value()) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return Value();
+
+ exclusion_set.insert(cur.string_value());
+ }
+ }
+
+ if (value->type() == Value::STRING) {
+ if (value->string_value() == "*") {
+ source->MarkAllUsed(exclusion_set);
+ return Value();
+ }
+ } else if (value->type() == Value::LIST) {
+ if (exclusion_value.type() != Value::NONE) {
+ *err = Err(exclusion_value, "Not supported with a variable list.",
+ "Exclusion list can only be used with the string \"*\".");
+ return Value();
+ }
+ for (const Value& cur : value->list_value()) {
+ if (!cur.VerifyTypeIs(Value::STRING, err))
+ return Value();
+ if (!source->GetValue(cur.string_value(), true)) {
+ *err = Err(cur, "Undefined identifier");
+ return Value();
+ }
+ }
+ return Value();
+ }
+
+ // Not the right type of argument.
+ *err = Err(*value, "Not a valid list of variables.",
+ "Expecting either the string \"*\" or a list of strings.");
+ return Value();
+}
+
+// set_sources_assignment_filter -----------------------------------------------
+
+const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
+const char kSetSourcesAssignmentFilter_HelpShort[] =
+ "set_sources_assignment_filter: Set a pattern to filter source files.";
+const char kSetSourcesAssignmentFilter_Help[] =
+ R"(set_sources_assignment_filter: Set a pattern to filter source files.
+
+ The sources assignment filter is a list of patterns that remove files from
+ the list implicitly whenever the "sources" variable is assigned to. This will
+ do nothing for non-lists.
+
+ This is intended to be used to globally filter out files with
+ platform-specific naming schemes when they don't apply, for example you may
+ want to filter out all "*_win.cc" files on non-Windows platforms.
+
+ Typically this will be called once in the master build config script to set
+ up the filter for the current platform. Subsequent calls will overwrite the
+ previous values.
+
+ If you want to bypass the filter and add a file even if it might be filtered
+ out, call set_sources_assignment_filter([]) to clear the list of filters.
+ This will apply until the current scope exits
+
+How to use patterns
+
+ File patterns are VERY limited regular expressions. They must match the
+ entire input string to be counted as a match. In regular expression parlance,
+ there is an implicit "^...$" surrounding your input. If you want to match a
+ substring, you need to use wildcards at the beginning and end.
+
+ There are only two special tokens understood by the pattern matcher.
+ Everything else is a literal.
+
+ - "*" Matches zero or more of any character. It does not depend on the
+ preceding character (in regular expression parlance it is equivalent to
+ ".*").
+
+ - "\b" Matches a path boundary. This will match the beginning or end of a
+ string, or a slash.
+
+Pattern examples
+
+ "*asdf*"
+ Matches a string containing "asdf" anywhere.
+
+ "asdf"
+ Matches only the exact string "asdf".
+
+ "*.cc"
+ Matches strings ending in the literal ".cc".
+
+ "\bwin/*"
+ Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
+
+Sources assignment example
+
+ # Filter out all _win files.
+ set_sources_assignment_filter([ "*_win.cc", "*_win.h" ])
+ sources = [ "a.cc", "b_win.cc" ]
+ print(sources)
+ # Will print [ "a.cc" ]. b_win one was filtered out.
+)";
+
+Value RunSetSourcesAssignmentFilter(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ if (args.size() != 1) {
+ *err = Err(function, "set_sources_assignment_filter takes one argument.");
+ } else {
+ std::unique_ptr<PatternList> f = std::make_unique<PatternList>();
+ f->SetFromValue(args[0], err);
+ if (!err->has_error())
+ scope->set_sources_assignment_filter(std::move(f));
+ }
+ return Value();
+}
+
+// pool ------------------------------------------------------------------------
+
+const char kPool[] = "pool";
+const char kPool_HelpShort[] = "pool: Defines a pool object.";
+const char kPool_Help[] =
+ R"*(pool: Defines a pool object.
+
+ Pool objects can be applied to a tool to limit the parallelism of the
+ build. This object has a single property "depth" corresponding to
+ the number of tasks that may run simultaneously.
+
+ As the file containing the pool definition may be executed in the
+ context of more than one toolchain it is recommended to specify an
+ explicit toolchain when defining and referencing a pool.
+
+ A pool named "console" defined in the root build file represents Ninja's
+ console pool. Targets using this pool will have access to the console's
+ stdin and stdout, and output will not be buffered. This special pool must
+ have a depth of 1. Pools not defined in the root must not be named "console".
+ The console pool can only be defined for the default toolchain.
+ Refer to the Ninja documentation on the console pool for more info.
+
+ A pool is referenced by its label just like a target.
+
+Variables
+
+ depth*
+ * = required
+
+Example
+
+ if (current_toolchain == default_toolchain) {
+ pool("link_pool") {
+ depth = 1
+ }
+ }
+
+ toolchain("toolchain") {
+ tool("link") {
+ command = "..."
+ pool = ":link_pool($default_toolchain)")
+ }
+ }
+)*";
+
+const char kDepth[] = "depth";
+
+Value RunPool(const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Scope* scope,
+ Err* err) {
+ NonNestableBlock non_nestable(scope, function, "pool");
+ if (!non_nestable.Enter(err))
+ return Value();
+
+ if (!EnsureSingleStringArg(function, args, err) ||
+ !EnsureNotProcessingImport(function, scope, err))
+ return Value();
+
+ Label label(MakeLabelForScope(scope, function, args[0].string_value()));
+
+ if (g_scheduler->verbose_logging())
+ g_scheduler->Log("Defining pool", label.GetUserVisibleName(true));
+
+ // Get the pool depth. It is an error to define a pool without a depth,
+ // so check first for the presence of the value.
+ const Value* depth = scope->GetValue(kDepth, true);
+ if (!depth) {
+ *err = Err(function, "Can't define a pool without depth.");
+ return Value();
+ }
+
+ if (!depth->VerifyTypeIs(Value::INTEGER, err))
+ return Value();
+
+ if (depth->int_value() < 0) {
+ *err = Err(*depth, "depth must be positive or 0.");
+ return Value();
+ }
+
+ // Create the new pool.
+ std::unique_ptr<Pool> pool = std::make_unique<Pool>(
+ scope->settings(), label, scope->build_dependency_files());
+
+ if (label.name() == "console") {
+ const Settings* settings = scope->settings();
+ if (!settings->is_default()) {
+ *err = Err(
+ function,
+ "\"console\" pool must be defined only in the default toolchain.");
+ return Value();
+ }
+ if (label.dir() != settings->build_settings()->root_target_label().dir()) {
+ *err = Err(function, "\"console\" pool must be defined in the root //.");
+ return Value();
+ }
+ if (depth->int_value() != 1) {
+ *err = Err(*depth, "\"console\" pool must have depth 1.");
+ return Value();
+ }
+ }
+ pool->set_depth(depth->int_value());
+
+ // Save the generated item.
+ Scope::ItemVector* collector = scope->GetItemCollector();
+ if (!collector) {
+ *err = Err(function, "Can't define a pool in this context.");
+ return Value();
+ }
+ collector->push_back(std::move(pool));
+
+ return Value();
+}
+
+// print -----------------------------------------------------------------------
+
+const char kPrint[] = "print";
+const char kPrint_HelpShort[] = "print: Prints to the console.";
+const char kPrint_Help[] =
+ R"(print: Prints to the console.
+
+ Prints all arguments to the console separated by spaces. A newline is
+ automatically appended to the end.
+
+ This function is intended for debugging. Note that build files are run in
+ parallel so you may get interleaved prints. A buildfile may also be executed
+ more than once in parallel in the context of different toolchains so the
+ prints from one file may be duplicated or
+ interleaved with itself.
+
+Examples
+
+ print("Hello world")
+
+ print(sources, deps)
+)";
+
+Value RunPrint(Scope* scope,
+ const FunctionCallNode* function,
+ const std::vector<Value>& args,
+ Err* err) {
+ std::string output;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (i != 0)
+ output.push_back(' ');
+ output.append(args[i].ToString(false));
+ }
+ output.push_back('\n');
+
+ const BuildSettings::PrintCallback& cb =
+ scope->settings()->build_settings()->print_callback();
+ if (cb.is_null()) {
+ printf("%s", output.c_str());
+ fflush(stdout);
+ } else
+ cb.Run(output);
+
+ return Value();
+}
+
+// split_list ------------------------------------------------------------------
+
+const char kSplitList[] = "split_list";
+const char kSplitList_HelpShort[] =
+ "split_list: Splits a list into N different sub-lists.";
+const char kSplitList_Help[] =
+ R"(split_list: Splits a list into N different sub-lists.
+
+ result = split_list(input, n)
+
+ Given a list and a number N, splits the list into N sub-lists of
+ approximately equal size. The return value is a list of the sub-lists. The
+ result will always be a list of size N. If N is greater than the number of
+ elements in the input, it will be padded with empty lists.
+
+ The expected use is to divide source files into smaller uniform chunks.
+
+Example
+
+ The code:
+ mylist = [1, 2, 3, 4, 5, 6]
+ print(split_list(mylist, 3))
+
+ Will print:
+ [[1, 2], [3, 4], [5, 6]
+)";
+Value RunSplitList(Scope* scope,
+ const FunctionCallNode* function,
+ const ListNode* args_list,
+ Err* err) {
+ const auto& args_vector = args_list->contents();
+ if (args_vector.size() != 2) {
+ *err = Err(function, "Wrong number of arguments to split_list().",
+ "Expecting exactly two.");
+ return Value();
+ }
+
+ ParseNodeValueAdapter list_adapter;
+ if (!list_adapter.InitForType(scope, args_vector[0].get(), Value::LIST, err))
+ return Value();
+ const std::vector<Value>& input = list_adapter.get().list_value();
+
+ ParseNodeValueAdapter count_adapter;
+ if (!count_adapter.InitForType(scope, args_vector[1].get(), Value::INTEGER,
+ err))
+ return Value();
+ int64_t count = count_adapter.get().int_value();
+ if (count <= 0) {
+ *err = Err(function, "Requested result size is not positive.");
+ return Value();
+ }
+
+ Value result(function, Value::LIST);
+ result.list_value().resize(count);
+
+ // Every result list gets at least this many items in it.
+ int64_t min_items_per_list = static_cast<int64_t>(input.size()) / count;
+
+ // This many result lists get an extra item which is the remainder from above.
+ int64_t extra_items = static_cast<int64_t>(input.size()) % count;
+
+ // Allocate all lists that have a remainder assigned to them (max items).
+ int64_t max_items_per_list = min_items_per_list + 1;
+ auto last_item_end = input.begin();
+ for (int64_t i = 0; i < extra_items; i++) {
+ result.list_value()[i] = Value(function, Value::LIST);
+
+ auto begin_add = last_item_end;
+ last_item_end += max_items_per_list;
+ result.list_value()[i].list_value().assign(begin_add, last_item_end);
+ }
+
+ // Allocate all smaller items that don't have a remainder.
+ for (int64_t i = extra_items; i < count; i++) {
+ result.list_value()[i] = Value(function, Value::LIST);
+
+ auto begin_add = last_item_end;
+ last_item_end += min_items_per_list;
+ result.list_value()[i].list_value().assign(begin_add, last_item_end);
+ }
+
+ return result;
+}
+
+// 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(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