text: // Copyright (c) 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "tools/gn/functions.h"
  
  #include <stddef.h>
  #include <iostream>
  #include <memory>
  #include <regex>
  #include <utility>
  
  #include "base/environment.h"
  #include "base/strings/string_util.h"
  #include "tools/gn/config.h"
  #include "tools/gn/config_values_generator.h"
  #include "tools/gn/err.h"
  #include "tools/gn/input_file.h"
  #include "tools/gn/parse_node_value_adapter.h"
  #include "tools/gn/parse_tree.h"
  #include "tools/gn/pool.h"
  #include "tools/gn/scheduler.h"
  #include "tools/gn/scope.h"
  #include "tools/gn/settings.h"
  #include "tools/gn/template.h"
  #include "tools/gn/token.h"
  #include "tools/gn/value.h"
  #include "tools/gn/value_extractors.h"
  #include "tools/gn/variables.h"
  
  namespace {
  
  // Some functions take a {} following them, and some don't. For the ones that
  // don't, this is used to verify that the given block node is null and will
  // set the error accordingly if it's not. Returns true if the block is null.
  bool VerifyNoBlockForFunctionCall(const FunctionCallNode* function,
                                    const BlockNode* block,
                                    Err* err) {
    if (!block)
      return true;
  
    *err =
        Err(block, "Unexpected '{'.",
            "This function call doesn't take a {} block following it, and you\n"
            "can't have a {} block that's not connected to something like an if\n"
            "statement or a target declaration.");
    err->AppendRange(function->function().range());
    return false;
  }
  
  // This key is set as a scope property on the scope of a declare_args() block,
  // in order to prevent reading a variable defined earlier in the same call
  // (see `gn help declare_args` for more).
  const void* kInDeclareArgsKey = nullptr;
  
  }  // namespace
  
  bool EnsureNotReadingFromSameDeclareArgs(const ParseNode* node,
                                           const Scope* cur_scope,
                                           const Scope* val_scope,
                                           Err* err) {
    // If the value didn't come from a scope at all, we're safe.
    if (!val_scope)
      return true;
  
    const Scope* val_args_scope = nullptr;
    val_scope->GetProperty(&kInDeclareArgsKey, &val_args_scope);
  
    const Scope* cur_args_scope = nullptr;
    cur_scope->GetProperty(&kInDeclareArgsKey, &cur_args_scope);
    if (!val_args_scope || !cur_args_scope || (val_args_scope != cur_args_scope))
      return true;
  
    *err =
        Err(node,
            "Reading a variable defined in the same declare_args() call.\n"
            "\n"
            "If you need to set the value of one arg based on another, put\n"
            "them in two separate declare_args() calls, one after the other.\n");
    return false;
  }
  
  bool EnsureNotProcessingImport(const ParseNode* node,
                                 const Scope* scope,
                                 Err* err) {
    if (scope->IsProcessingImport()) {
      *err =
          Err(node, "Not valid from an import.",
              "Imports are for defining defaults, variables, and rules. The\n"
              "appropriate place for this kind of thing is really in a normal\n"
              "BUILD file.");
      return false;
    }
    return true;
  }
  
  bool EnsureNotProcessingBuildConfig(const ParseNode* node,
                                      const Scope* scope,
                                      Err* err) {
    if (scope->IsProcessingBuildConfig()) {
      *err = Err(node, "Not valid from the build config.",
                 "You can't do this kind of thing from the build config script, "
                 "silly!\nPut it in a regular BUILD file.");
      return false;
    }
    return true;
  }
  
  bool FillTargetBlockScope(const Scope* scope,
                            const FunctionCallNode* function,
                            const std::string& target_type,
                            const BlockNode* block,
                            const std::vector<Value>& args,
                            Scope* block_scope,
                            Err* err) {
    if (!block) {
      FillNeedsBlockError(function, err);
      return false;
    }
  
    // Copy the target defaults, if any, into the scope we're going to execute
    // the block in.
    const Scope* default_scope = scope->GetTargetDefaults(target_type);
    if (default_scope) {
      Scope::MergeOptions merge_options;
      merge_options.skip_private_vars = true;
      if (!default_scope->NonRecursiveMergeTo(block_scope, merge_options,
                                              function, "target defaults", err))
        return false;
    }
  
    // The name is the single argument to the target function.
    if (!EnsureSingleStringArg(function, args, err))
      return false;
  
    // Set the target name variable to the current target, and mark it used
    // because we don't want to issue an error if the script ignores it.
    const std::string_view target_name(variables::kTargetName);
    block_scope->SetValue(target_name, Value(function, args[0].string_value()),
                          function);
    block_scope->MarkUsed(target_name);
    return true;
  }
  
  void FillNeedsBlockError(const FunctionCallNode* function, Err* err) {
    *err = Err(function->function(), "This function call requires a block.",
               "The block's \"{\" must be on the same line as the function "
               "call's \")\".");
  }
  
  bool EnsureSingleStringArg(const FunctionCallNode* function,
                             const std::vector<Value>& args,
                             Err* err) {
    if (args.size() != 1) {
      *err = Err(function->function(), "Incorrect arguments.",
                 "This function requires a single string argument.");
      return false;
    }
    return args[0].VerifyTypeIs(Value::STRING, err);
  }
  
  const Label& ToolchainLabelForScope(const Scope* scope) {
    return scope->settings()->toolchain_label();
  }
  
  Label MakeLabelForScope(const Scope* scope,
                          const FunctionCallNode* function,
                          const std::string& name) {
    const Label& toolchain_label = ToolchainLabelForScope(scope);
    return Label(scope->GetSourceDir(), name, toolchain_label.dir(),
                 toolchain_label.name());
  }
  
  // static
  const int NonNestableBlock::kKey = 0;
  
  NonNestableBlock::NonNestableBlock(Scope* scope,
                                     const FunctionCallNode* function,
                                     const char* type_description)
      : scope_(scope),
        function_(function),
        type_description_(type_description),
        key_added_(false) {}
  
  NonNestableBlock::~NonNestableBlock() {
    if (key_added_)
      scope_->SetProperty(&kKey, nullptr);
  }
  
  bool NonNestableBlock::Enter(Err* err) {
    void* scope_value = scope_->GetProperty(&kKey, nullptr);
    if (scope_value) {
      // Existing block.
      const NonNestableBlock* existing =
          reinterpret_cast<const NonNestableBlock*>(scope_value);
      *err = Err(function_, "Can't nest these things.",
                 std::string("You are trying to nest a ") + type_description_ +
                     " inside a " + existing->type_description_ + ".");
      err->AppendSubErr(Err(existing->function_, "The enclosing block."));
      return false;
    }
  
    scope_->SetProperty(&kKey, this);
    key_added_ = true;
    return true;
  }
  
  namespace functions {
  
  // assert ----------------------------------------------------------------------
  
  const char kAssert[] = "assert";
  const char kAssert_HelpShort[] =
      "assert: Assert an expression is true at generation time.";
  const char kAssert_Help[] =
      R"(assert: Assert an expression is true at generation time.
  
    assert(<condition> [, <error string>])
  
    If the condition is false, the build will fail with an error. If the
    optional second argument is provided, that string will be printed
    with the error message.
  
  Examples
  
    assert(is_win)
    assert(defined(sources), "Sources must be defined");
  )";
  
  Value RunAssert(Scope* scope,
                  const FunctionCallNode* function,
                  const std::vector<Value>& args,
                  Err* err) {
    if (args.size() != 1 && args.size() != 2) {
      *err = Err(function->function(), "Wrong number of arguments.",
                 "assert() takes one or two argument, "
                 "were you expecting somethig else?");
    } else if (args[0].type() != Value::BOOLEAN) {
      *err = Err(function->function(), "Assertion value not a bool.");
    } else if (!args[0].boolean_value()) {
      if (args.size() == 2) {
        // Optional string message.
        if (args[1].type() != Value::STRING) {
          *err = Err(function->function(), "Assertion failed.",
                     "<<<ERROR MESSAGE IS NOT A STRING>>>");
        } else {
          *err = Err(function->function(), "Assertion failed.",
                     args[1].string_value());
        }
      } else {
        *err = Err(function->function(), "Assertion failed.");
      }
  
      if (args[0].origin()) {
        // If you do "assert(foo)" we'd ideally like to show you where foo was
        // set, and in this case the origin of the args will tell us that.
        // However, if you do "assert(foo && bar)" the source of the value will
        // be the assert like, which isn't so helpful.
        //
        // So we try to see if the args are from the same line or not. This will
        // break if you do "assert(\nfoo && bar)" and we may show the second line
        // as the source, oh well. The way around this is to check to see if the
        // origin node is inside our function call block.
        Location origin_location = args[0].origin()->GetRange().begin();
        if (origin_location.file() != function->function().location().file() ||
            origin_location.line_number() !=
                function->function().location().line_number()) {
          err->AppendSubErr(
              Err(args[0].origin()->GetRange(), "", "This is where it was set."));
        }
      }
    }
    return Value();
  }
  
  // config ----------------------------------------------------------------------
  
  const char kConfig[] = "config";
  const char kConfig_HelpShort[] = "config: Defines a configuration object.";
  const char kConfig_Help[] =
      R"(config: Defines a configuration object.
  
    Configuration objects can be applied to targets and specify sets of compiler
    flags, includes, defines, etc. They provide a way to conveniently group sets
    of this configuration information.
  
    A config is referenced by its label just like a target.
  
    The values in a config are additive only. If you want to remove a flag you
    need to remove the corresponding config that sets it. The final set of flags,
    defines, etc. for a target is generated in this order:
  
     1. The values specified directly on the target (rather than using a config.
     2. The configs specified in the target's "configs" list, in order.
     3. Public_configs from a breadth-first traversal of the dependency tree in
        the order that the targets appear in "deps".
     4. All dependent configs from a breadth-first traversal of the dependency
        tree in the order that the targets appear in "deps".
  
  More background
  
    Configs solve a problem where the build system needs to have a higher-level
    understanding of various compiler settings. For example, some compiler flags
    have to appear in a certain order relative to each other, some settings like
    defines and flags logically go together, and the build system needs to
    de-duplicate flags even though raw command-line parameters can't always be
    operated on in that way.
  
    The config gives a name to a group of settings that can then be reasoned
    about by GN. GN can know that configs with the same label are the same thing
    so can be de-duplicated. It allows related settings to be grouped so they
    are added or removed as a unit. And it allows targets to refer to settings
    with conceptual names ("no_rtti", "enable_exceptions", etc.) rather than
    having to hard-coding every compiler's flags each time they are referred to.
  
  Variables valid in a config definition
  
  )"
  
      CONFIG_VALUES_VARS_HELP
  
      R"(  Nested configs: configs
  
  Variables on a target used to apply configs
  
    all_dependent_configs, configs, public_configs
  
  Example
  
    config("myconfig") {
      include_dirs = [ "include/common" ]
      defines = [ "ENABLE_DOOM_MELON" ]
    }
  
    executable("mything") {
      configs = [ ":myconfig" ]
    }
  )";
  
  Value RunConfig(const FunctionCallNode* function,
                  const std::vector<Value>& args,
                  Scope* scope,
                  Err* err) {
    NonNestableBlock non_nestable(scope, function, "config");
    if (!non_nestable.Enter(err))
      return Value();
  
    if (!EnsureSingleStringArg(function, args, err) ||
        !EnsureNotProcessingImport(function, scope, err))
      return Value();
  
    Label label(MakeLabelForScope(scope, function, args[0].string_value()));
  
    if (g_scheduler->verbose_logging())
      g_scheduler->Log("Defining config", label.GetUserVisibleName(true));
  
    // Create the new config.
    std::unique_ptr<Config> config = std::make_unique<Config>(
        scope->settings(), label, scope->build_dependency_files());
    config->set_defined_from(function);
    if (!Visibility::FillItemVisibility(config.get(), scope, err))
      return Value();
  
    // Fill the flags and such.
    const SourceDir& input_dir = scope->GetSourceDir();
    ConfigValuesGenerator gen(&config->own_values(), scope, input_dir, err);
    gen.Run();
    if (err->has_error())
      return Value();
  
    // Read sub-configs.
    const Value* configs_value = scope->GetValue(variables::kConfigs, true);
    if (configs_value) {
      ExtractListOfUniqueLabels(*configs_value, scope->GetSourceDir(),
                                ToolchainLabelForScope(scope), &config->configs(),
                                err);
    }
    if (err->has_error())
      return Value();
  
    // Save the generated item.
    Scope::ItemVector* collector = scope->GetItemCollector();
    if (!collector) {
      *err = Err(function, "Can't define a config in this context.");
      return Value();
    }
    collector->push_back(std::move(config));
  
    return Value();
  }
  
  // declare_args ----------------------------------------------------------------
  
  const char kDeclareArgs[] = "declare_args";
  const char kDeclareArgs_HelpShort[] = "declare_args: Declare build arguments.";
  const char kDeclareArgs_Help[] =
      R"(declare_args: Declare build arguments.
  
    Introduces the given arguments into the current scope. If they are not
    specified on the command line or in a toolchain's arguments, the default
    values given in the declare_args block will be used. However, these defaults
    will not override command-line values.
  
    See also "gn help buildargs" for an overview.
  
    The precise behavior of declare args is:
  
     1. The declare_args() block executes. Any variable defined in the enclosing
        scope is available for reading, but any variable defined earlier in
        the current scope is not (since the overrides haven't been applied yet).
  
     2. At the end of executing the block, any variables set within that scope
        are saved globally as build arguments, with their current values being
        saved as the "default value" for that argument.
  
     3. User-defined overrides are applied. Anything set in "gn args" now
        overrides any default values. The resulting set of variables is promoted
        to be readable from the following code in the file.
  
    This has some ramifications that may not be obvious:
  
      - You should not perform difficult work inside a declare_args block since
        this only sets a default value that may be discarded. In particular,
        don't use the result of exec_script() to set the default value. If you
        want to have a script-defined default, set some default "undefined" value
        like [], "", or -1, and after the declare_args block, call exec_script if
        the value is unset by the user.
  
      - Because you cannot read the value of a variable defined in the same
        block, if you need to make the default value of one arg depend
        on the possibly-overridden value of another, write two separate
        declare_args() blocks:
  
          declare_args() {
            enable_foo = true
          }
          declare_args() {
            # Bar defaults to same user-overridden state as foo.
            enable_bar = enable_foo
          }
  
  Example
  
    declare_args() {
      enable_teleporter = true
      enable_doom_melon = false
    }
  
    If you want to override the (default disabled) Doom Melon:
      gn --args="enable_doom_melon=true enable_teleporter=true"
    This also sets the teleporter, but it's already defaulted to on so it will
    have no effect.
  )";
  
  Value RunDeclareArgs(Scope* scope,
                       const FunctionCallNode* function,
                       const std::vector<Value>& args,
                       BlockNode* block,
                       Err* err) {
    NonNestableBlock non_nestable(scope, function, "declare_args");
    if (!non_nestable.Enter(err))
      return Value();
  
    Scope block_scope(scope);
    block_scope.SetProperty(&kInDeclareArgsKey, &block_scope);
    block->Execute(&block_scope, err);
    if (err->has_error())
      return Value();
  
    // Pass the values from our scope into the Args object for adding to the
    // scope with the proper values (taking into account the defaults given in
    // the block_scope, and arguments passed into the build).
    Scope::KeyValueMap values;
    block_scope.GetCurrentScopeValues(&values);
    scope->settings()->build_settings()->build_args().DeclareArgs(values, scope,
                                                                  err);
    return Value();
  }
  
  // defined ---------------------------------------------------------------------
  
  const char kDefined[] = "defined";
  const char kDefined_HelpShort[] =
      "defined: Returns whether an identifier is defined.";
  const char kDefined_Help[] =
      R"(defined: Returns whether an identifier is defined.
  
    Returns true if the given argument is defined. This is most useful in
    templates to assert that the caller set things up properly.
  
    You can pass an identifier:
      defined(foo)
    which will return true or false depending on whether foo is defined in the
    current scope.
  
    You can also check a named scope:
      defined(foo.bar)
    which will return true or false depending on whether bar is defined in the
    named scope foo. It will throw an error if foo is not defined or is not a
    scope.
  
  Example
  
    template("mytemplate") {
      # To help users call this template properly...
      assert(defined(invoker.sources), "Sources must be defined")
  
      # If we want to accept an optional "values" argument, we don't
      # want to dereference something that may not be defined.
      if (defined(invoker.values)) {
        values = invoker.values
      } else {
        values = "some default value"
      }
    }
  )";
  
  Value RunDefined(Scope* scope,
                   const FunctionCallNode* function,
                   const ListNode* args_list,
                   Err* err) {
    const auto& args_vector = args_list->contents();
    if (args_vector.size() != 1) {
      *err = Err(function, "Wrong number of arguments to defined().",
                 "Expecting exactly one.");
      return Value();
    }
  
    const IdentifierNode* identifier = args_vector[0]->AsIdentifier();
    if (identifier) {
      // Passed an identifier "defined(foo)".
      if (scope->GetValue(identifier->value().value()))
        return Value(function, true);
      return Value(function, false);
    }
  
    const AccessorNode* accessor = args_vector[0]->AsAccessor();
    if (accessor) {
      // Passed an accessor "defined(foo.bar)".
      if (accessor->member()) {
        // The base of the accessor must be a scope if it's defined.
        const Value* base = scope->GetValue(accessor->base().value());
        if (!base) {
          *err = Err(accessor, "Undefined identifier");
          return Value();
        }
        if (!base->VerifyTypeIs(Value::SCOPE, err))
          return Value();
  
        // Check the member inside the scope to see if its defined.
        if (base->scope_value()->GetValue(accessor->member()->value().value()))
          return Value(function, true);
        return Value(function, false);
      }
    }
  
    // Argument is invalid.
    *err = Err(function, "Bad thing passed to defined().",
               "It should be of the form defined(foo) or defined(foo.bar).");
    return Value();
  }
  
  // getenv ----------------------------------------------------------------------
  
  const char kGetEnv[] = "getenv";
  const char kGetEnv_HelpShort[] = "getenv: Get an environment variable.";
  const char kGetEnv_Help[] =
      R"(getenv: Get an environment variable.
  
    value = getenv(env_var_name)
  
    Returns the value of the given environment variable. If the value is not
    found, it will try to look up the variable with the "opposite" case (based on
    the case of the first letter of the variable), but is otherwise
    case-sensitive.
  
    If the environment variable is not found, the empty string will be returned.
    Note: it might be nice to extend this if we had the concept of "none" in the
    language to indicate lookup failure.
  
  Example
  
    home_dir = getenv("HOME")
  )";
  
  Value RunGetEnv(Scope* scope,
                  const FunctionCallNode* function,
                  const std::vector<Value>& args,
                  Err* err) {
    if (!EnsureSingleStringArg(function, args, err))
      return Value();
  
    std::unique_ptr<base::Environment> env(base::Environment::Create());
  
    std::string result;
    if (!env->GetVar(args[0].string_value().c_str(), &result))
      return Value(function, "");  // Not found, return empty string.
    return Value(function, result);
  }
  
  // import ----------------------------------------------------------------------
  
  const char kImport[] = "import";
  const char kImport_HelpShort[] =
      "import: Import a file into the current scope.";
  const char kImport_Help[] =
      R"(import: Import a file into the current scope.
  
    The import command loads the rules and variables resulting from executing the
    given file into the current scope.
  
    By convention, imported files are named with a .gni extension.
  
    An import is different than a C++ "include". The imported file is executed in
    a standalone environment from the caller of the import command. The results
    of this execution are cached for other files that import the same .gni file.
  
    Note that you can not import a BUILD.gn file that's otherwise used in the
    build. Files must either be imported or implicitly loaded as a result of deps
    rules, but not both.
  
    The imported file's scope will be merged with the scope at the point import
    was called. If there is a conflict (both the current scope and the imported
    file define some variable or rule with the same name but different value), a
    runtime error will be thrown. Therefore, it's good practice to minimize the
    stuff that an imported file defines.
  
    Variables and templates beginning with an underscore '_' are considered
    private and will not be imported. Imported files can use such variables for
    internal computation without affecting other files.
  
  Examples
  
    import("//build/rules/idl_compilation_rule.gni")
  
    # Looks in the current directory.
    import("my_vars.gni")
  )";
  
  Value RunImport(Scope* scope,
                  const FunctionCallNode* function,
                  const std::vector<Value>& args,
                  Err* err) {
    if (!EnsureSingleStringArg(function, args, err))
      return Value();
  
    const SourceDir& input_dir = scope->GetSourceDir();
    SourceFile import_file = input_dir.ResolveRelativeFile(
        args[0], err, scope->settings()->build_settings()->root_path_utf8());
    scope->AddBuildDependencyFile(import_file);
    if (!err->has_error()) {
      scope->settings()->import_manager().DoImport(import_file, function, scope,
                                                   err);
    }
    return Value();
  }
  
  // not_needed -----------------------------------------------------------------
  
  const char kNotNeeded[] = "not_needed";
  const char kNotNeeded_HelpShort[] =
      "not_needed: Mark variables from scope as not needed.";
  const char kNotNeeded_Help[] =
      R"(not_needed: Mark variables from scope as not needed.
  
    not_needed(variable_list_or_star, variable_to_ignore_list = [])
    not_needed(from_scope, variable_list_or_star,
               variable_to_ignore_list = [])
  
    Mark the variables in the current or given scope as not needed, which means
    you will not get an error about unused variables for these. The
    variable_to_ignore_list allows excluding variables from "all matches" if
    variable_list_or_star is "*".
  
  Example
  
    not_needed("*", [ "config" ])
    not_needed([ "data_deps", "deps" ])
    not_needed(invoker, "*", [ "config" ])
    not_needed(invoker, [ "data_deps", "deps" ])
  )";
  
  Value RunNotNeeded(Scope* scope,
                     const FunctionCallNode* function,
                     const ListNode* args_list,
                     Err* err) {
    const auto& args_vector = args_list->contents();
    if (args_vector.size() < 1 || args_vector.size() > 3) {
      *err = Err(function, "Wrong number of arguments.",
                 "Expecting one, two or three arguments.");
      return Value();
    }
    auto args_cur = args_vector.begin();
  
    Value* value = nullptr;  // Value to use, may point to result_value.
    Value result_value;      // Storage for the "evaluate" case.
    Value scope_value;       // Storage for an evaluated scope.
    const IdentifierNode* identifier = (*args_cur)->AsIdentifier();
    if (identifier) {
      // Optimize the common case where the input scope is an identifier. This
      // prevents a copy of a potentially large Scope object.
      value = scope->GetMutableValue(identifier->value().value(),
                                     Scope::SEARCH_NESTED, true);
      if (!value) {
        *err = Err(identifier, "Undefined identifier.");
        return Value();
      }
    } else {
      // Non-optimized case, just evaluate the argument.
      result_value = (*args_cur)->Execute(scope, err);
      if (err->has_error())
        return Value();
      value = &result_value;
    }
    args_cur++;
  
    // Extract the source scope if different from current one.
    Scope* source = scope;
    if (value->type() == Value::SCOPE) {
      if (args_cur == args_vector.end()) {
        *err = Err(
            function, "Wrong number of arguments.",
            "The first argument is a scope, expecting two or three arguments.");
        return Value();
      }
      // Copy the scope value if it will be overridden.
      if (value == &result_value) {
        scope_value = Value(nullptr, value->scope_value()->MakeClosure());
        source = scope_value.scope_value();
      } else {
        source = value->scope_value();
      }
      result_value = (*args_cur)->Execute(scope, err);
      if (err->has_error())
        return Value();
      value = &result_value;
      args_cur++;
    } else if (args_vector.size() > 2) {
      *err = Err(
          function, "Wrong number of arguments.",
          "The first argument is not a scope, expecting one or two arguments.");
      return Value();
    }
  
    // Extract the exclusion list if defined.
    Value exclusion_value;
    std::set<std::string> exclusion_set;
    if (args_cur != args_vector.end()) {
      exclusion_value = (*args_cur)->Execute(source, err);
      if (err->has_error())
        return Value();
  
      if (exclusion_value.type() != Value::LIST) {
        *err = Err(exclusion_value, "Not a valid list of variables to exclude.",
                   "Expecting a list of strings.");
        return Value();
      }
  
      for (const Value& cur : exclusion_value.list_value()) {
        if (!cur.VerifyTypeIs(Value::STRING, err))
          return Value();
  
        exclusion_set.insert(cur.string_value());
      }
    }
  
    if (value->type() == Value::STRING) {
      if (value->string_value() == "*") {
        source->MarkAllUsed(exclusion_set);
        return Value();
      }
    } else if (value->type() == Value::LIST) {
      if (exclusion_value.type() != Value::NONE) {
        *err = Err(exclusion_value, "Not supported with a variable list.",
                   "Exclusion list can only be used with the string \"*\".");
        return Value();
      }
      for (const Value& cur : value->list_value()) {
        if (!cur.VerifyTypeIs(Value::STRING, err))
          return Value();
        if (!source->GetValue(cur.string_value(), true)) {
          *err = Err(cur, "Undefined identifier");
          return Value();
        }
      }
      return Value();
    }
  
    // Not the right type of argument.
    *err = Err(*value, "Not a valid list of variables.",
               "Expecting either the string \"*\" or a list of strings.");
    return Value();
  }
  
  // set_sources_assignment_filter -----------------------------------------------
  
  const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
  const char kSetSourcesAssignmentFilter_HelpShort[] =
      "set_sources_assignment_filter: Set a pattern to filter source files.";
  const char kSetSourcesAssignmentFilter_Help[] =
      R"(set_sources_assignment_filter: Set a pattern to filter source files.
  
    The sources assignment filter is a list of patterns that remove files from
    the list implicitly whenever the "sources" variable is assigned to. This will
    do nothing for non-lists.
  
    This is intended to be used to globally filter out files with
    platform-specific naming schemes when they don't apply, for example you may
    want to filter out all "*_win.cc" files on non-Windows platforms.
  
    Typically this will be called once in the master build config script to set
    up the filter for the current platform. Subsequent calls will overwrite the
    previous values.
  
    If you want to bypass the filter and add a file even if it might be filtered
    out, call set_sources_assignment_filter([]) to clear the list of filters.
    This will apply until the current scope exits
  
  How to use patterns
  
    File patterns are VERY limited regular expressions. They must match the
    entire input string to be counted as a match. In regular expression parlance,
    there is an implicit "^...$" surrounding your input. If you want to match a
    substring, you need to use wildcards at the beginning and end.
  
    There are only two special tokens understood by the pattern matcher.
    Everything else is a literal.
  
     - "*" Matches zero or more of any character. It does not depend on the
       preceding character (in regular expression parlance it is equivalent to
       ".*").
  
     - "\b" Matches a path boundary. This will match the beginning or end of a
       string, or a slash.
  
  Pattern examples
  
    "*asdf*"
        Matches a string containing "asdf" anywhere.
  
    "asdf"
        Matches only the exact string "asdf".
  
    "*.cc"
        Matches strings ending in the literal ".cc".
  
    "\bwin/*"
        Matches "win/foo" and "foo/win/bar.cc" but not "iwin/foo".
  
  Sources assignment example
  
    # Filter out all _win files.
    set_sources_assignment_filter([ "*_win.cc", "*_win.h" ])
    sources = [ "a.cc", "b_win.cc" ]
    print(sources)
    # Will print [ "a.cc" ]. b_win one was filtered out.
  )";
  
  Value RunSetSourcesAssignmentFilter(Scope* scope,
                                      const FunctionCallNode* function,
                                      const std::vector<Value>& args,
                                      Err* err) {
    if (args.size() != 1) {
      *err = Err(function, "set_sources_assignment_filter takes one argument.");
    } else {
      std::unique_ptr<PatternList> f = std::make_unique<PatternList>();
      f->SetFromValue(args[0], err);
      if (!err->has_error())
        scope->set_sources_assignment_filter(std::move(f));
    }
    return Value();
  }
  
  // pool ------------------------------------------------------------------------
  
  const char kPool[] = "pool";
  const char kPool_HelpShort[] = "pool: Defines a pool object.";
  const char kPool_Help[] =
      R"*(pool: Defines a pool object.
  
    Pool objects can be applied to a tool to limit the parallelism of the
    build. This object has a single property "depth" corresponding to
    the number of tasks that may run simultaneously.
  
    As the file containing the pool definition may be executed in the
    context of more than one toolchain it is recommended to specify an
    explicit toolchain when defining and referencing a pool.
  
    A pool named "console" defined in the root build file represents Ninja's
    console pool. Targets using this pool will have access to the console's
    stdin and stdout, and output will not be buffered. This special pool must
    have a depth of 1. Pools not defined in the root must not be named "console".
    The console pool can only be defined for the default toolchain.
    Refer to the Ninja documentation on the console pool for more info.
  
    A pool is referenced by its label just like a target.
  
  Variables
  
    depth*
    * = required
  
  Example
  
    if (current_toolchain == default_toolchain) {
      pool("link_pool") {
        depth = 1
      }
    }
  
    toolchain("toolchain") {
      tool("link") {
        command = "..."
        pool = ":link_pool($default_toolchain)"
      }
    }
  )*";
  
  const char kDepth[] = "depth";
  
  Value RunPool(const FunctionCallNode* function,
                const std::vector<Value>& args,
                Scope* scope,
                Err* err) {
    NonNestableBlock non_nestable(scope, function, "pool");
    if (!non_nestable.Enter(err))
      return Value();
  
    if (!EnsureSingleStringArg(function, args, err) ||
        !EnsureNotProcessingImport(function, scope, err))
      return Value();
  
    Label label(MakeLabelForScope(scope, function, args[0].string_value()));
  
    if (g_scheduler->verbose_logging())
      g_scheduler->Log("Defining pool", label.GetUserVisibleName(true));
  
    // Get the pool depth. It is an error to define a pool without a depth,
    // so check first for the presence of the value.
    const Value* depth = scope->GetValue(kDepth, true);
    if (!depth) {
      *err = Err(function, "Can't define a pool without depth.");
      return Value();
    }
  
    if (!depth->VerifyTypeIs(Value::INTEGER, err))
      return Value();
  
    if (depth->int_value() < 0) {
      *err = Err(*depth, "depth must be positive or 0.");
      return Value();
    }
  
    // Create the new pool.
    std::unique_ptr<Pool> pool = std::make_unique<Pool>(
        scope->settings(), label, scope->build_dependency_files());
  
    if (label.name() == "console") {
      const Settings* settings = scope->settings();
      if (!settings->is_default()) {
        *err = Err(
            function,
            "\"console\" pool must be defined only in the default toolchain.");
        return Value();
      }
      if (label.dir() != settings->build_settings()->root_target_label().dir()) {
        *err = Err(function, "\"console\" pool must be defined in the root //.");
        return Value();
      }
      if (depth->int_value() != 1) {
        *err = Err(*depth, "\"console\" pool must have depth 1.");
        return Value();
      }
    }
    pool->set_depth(depth->int_value());
  
    // Save the generated item.
    Scope::ItemVector* collector = scope->GetItemCollector();
    if (!collector) {
      *err = Err(function, "Can't define a pool in this context.");
      return Value();
    }
    collector->push_back(std::move(pool));
  
    return Value();
  }
  
  // print -----------------------------------------------------------------------
  
  const char kPrint[] = "print";
  const char kPrint_HelpShort[] = "print: Prints to the console.";
  const char kPrint_Help[] =
      R"(print: Prints to the console.
  
    Prints all arguments to the console separated by spaces. A newline is
    automatically appended to the end.
  
    This function is intended for debugging. Note that build files are run in
    parallel so you may get interleaved prints. A buildfile may also be executed
    more than once in parallel in the context of different toolchains so the
    prints from one file may be duplicated or
    interleaved with itself.
  
  Examples
  
    print("Hello world")
  
    print(sources, deps)
  )";
  
  Value RunPrint(Scope* scope,
                 const FunctionCallNode* function,
                 const std::vector<Value>& args,
                 Err* err) {
    std::string output;
    for (size_t i = 0; i < args.size(); i++) {
      if (i != 0)
        output.push_back(' ');
      output.append(args[i].ToString(false));
    }
    output.push_back('\n');
  
    const BuildSettings::PrintCallback& cb =
        scope->settings()->build_settings()->print_callback();
    if (cb) {
      cb(output);
    } else {
      printf("%s", output.c_str());
      fflush(stdout);
    }
  
    return Value();
  }
  
  // split_list ------------------------------------------------------------------
  
  const char kSplitList[] = "split_list";
  const char kSplitList_HelpShort[] =
      "split_list: Splits a list into N different sub-lists.";
  const char kSplitList_Help[] =
      R"(split_list: Splits a list into N different sub-lists.
  
    result = split_list(input, n)
  
    Given a list and a number N, splits the list into N sub-lists of
    approximately equal size. The return value is a list of the sub-lists. The
    result will always be a list of size N. If N is greater than the number of
    elements in the input, it will be padded with empty lists.
  
    The expected use is to divide source files into smaller uniform chunks.
  
  Example
  
    The code:
      mylist = [1, 2, 3, 4, 5, 6]
      print(split_list(mylist, 3))
  
    Will print:
      [[1, 2], [3, 4], [5, 6]
  )";
  Value RunSplitList(Scope* scope,
                     const FunctionCallNode* function,
                     const ListNode* args_list,
                     Err* err) {
    const auto& args_vector = args_list->contents();
    if (args_vector.size() != 2) {
      *err = Err(function, "Wrong number of arguments to split_list().",
                 "Expecting exactly two.");
      return Value();
    }
  
    ParseNodeValueAdapter list_adapter;
    if (!list_adapter.InitForType(scope, args_vector[0].get(), Value::LIST, err))
      return Value();
    const std::vector<Value>& input = list_adapter.get().list_value();
  
    ParseNodeValueAdapter count_adapter;
    if (!count_adapter.InitForType(scope, args_vector[1].get(), Value::INTEGER,
                                   err))
      return Value();
    int64_t count = count_adapter.get().int_value();
    if (count <= 0) {
      *err = Err(function, "Requested result size is not positive.");
      return Value();
    }
  
    Value result(function, Value::LIST);
    result.list_value().resize(count);
  
    // Every result list gets at least this many items in it.
    int64_t min_items_per_list = static_cast<int64_t>(input.size()) / count;
  
    // This many result lists get an extra item which is the remainder from above.
    int64_t extra_items = static_cast<int64_t>(input.size()) % count;
  
    // Allocate all lists that have a remainder assigned to them (max items).
    int64_t max_items_per_list = min_items_per_list + 1;
    auto last_item_end = input.begin();
    for (int64_t i = 0; i < extra_items; i++) {
      result.list_value()[i] = Value(function, Value::LIST);
  
      auto begin_add = last_item_end;
      last_item_end += max_items_per_list;
      result.list_value()[i].list_value().assign(begin_add, last_item_end);
    }
  
    // Allocate all smaller items that don't have a remainder.
    for (int64_t i = extra_items; i < count; i++) {
      result.list_value()[i] = Value(function, Value::LIST);
  
      auto begin_add = last_item_end;
      last_item_end += min_items_per_list;
      result.list_value()[i].list_value().assign(begin_add, last_item_end);
    }
  
    return result;
  }
  
  // string_replace --------------------------------------------------------------
  
  const char kStringReplace[] = "string_replace";
  const char kStringReplace_HelpShort[] =
      "string_replace: Replaces substring in the given string.";
  const char kStringReplace_Help[] =
      R"(string_replace: Replaces substring in the given string.
  
    result = string_replace(str, old, new[, max])
  
    Returns a copy of the string str in which the occurrences of old have been
    replaced with new, optionally restricting the number of replacements. The
    replacement is performed sequentially, so if new contains old, it won't be
    replaced.
  
  Example
  
    The code:
      mystr = "Hello, world!"
      print(string_replace(mystr, "world", "GN"))
  
    Will print:
      Hello, GN!
  )";
  
  Value RunStringReplace(Scope* scope,
                         const FunctionCallNode* function,
                         const std::vector<Value>& args,
                         Err* err) {
    if (args.size() < 3 || args.size() > 4) {
      *err = Err(function, "Wrong number of arguments to string_replace().");
      return Value();
    }
  
    if (!args[0].VerifyTypeIs(Value::STRING, err))
      return Value();
    const std::string str = args[0].string_value();
  
    if (!args[1].VerifyTypeIs(Value::STRING, err))
      return Value();
    const std::string& old = args[1].string_value();
  
    if (!args[2].VerifyTypeIs(Value::STRING, err))
      return Value();
    const std::string& new_ = args[2].string_value();
  
    int64_t max = INT64_MAX;
    if (args.size() > 3) {
      if (!args[3].VerifyTypeIs(Value::INTEGER, err))
        return Value();
      max = args[3].int_value();
      if (max <= 0) {
        *err = Err(function, "Requested number of replacements is not positive.");
        return Value();
      }
    }
  
    int64_t n = 0;
    std::string val(str);
    size_t start_pos = 0;
    while ((start_pos = val.find(old, start_pos)) != std::string::npos) {
      val.replace(start_pos, old.length(), new_);
      start_pos += new_.length();
      if (++n >= max)
        break;
    }
    return Value(function, std::move(val));
  }
  
  // -----------------------------------------------------------------------------
  
  FunctionInfo::FunctionInfo()
      : self_evaluating_args_runner(nullptr),
        generic_block_runner(nullptr),
        executed_block_runner(nullptr),
        no_block_runner(nullptr),
        help_short(nullptr),
        help(nullptr),
        is_target(false) {}
  
  FunctionInfo::FunctionInfo(SelfEvaluatingArgsFunction seaf,
                             const char* in_help_short,
                             const char* in_help,
                             bool in_is_target)
      : self_evaluating_args_runner(seaf),
        generic_block_runner(nullptr),
        executed_block_runner(nullptr),
        no_block_runner(nullptr),
        help_short(in_help_short),
        help(in_help),
        is_target(in_is_target) {}
  
  FunctionInfo::FunctionInfo(GenericBlockFunction gbf,
                             const char* in_help_short,
                             const char* in_help,
                             bool in_is_target)
      : self_evaluating_args_runner(nullptr),
        generic_block_runner(gbf),
        executed_block_runner(nullptr),
        no_block_runner(nullptr),
        help_short(in_help_short),
        help(in_help),
        is_target(in_is_target) {}
  
  FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf,
                             const char* in_help_short,
                             const char* in_help,
                             bool in_is_target)
      : self_evaluating_args_runner(nullptr),
        generic_block_runner(nullptr),
        executed_block_runner(ebf),
        no_block_runner(nullptr),
        help_short(in_help_short),
        help(in_help),
        is_target(in_is_target) {}
  
  FunctionInfo::FunctionInfo(NoBlockFunction nbf,
                             const char* in_help_short,
                             const char* in_help,
                             bool in_is_target)
      : self_evaluating_args_runner(nullptr),
        generic_block_runner(nullptr),
        executed_block_runner(nullptr),
        no_block_runner(nbf),
        help_short(in_help_short),
        help(in_help),
        is_target(in_is_target) {}
  
  // Setup the function map via a static initializer. We use this because it
  // avoids race conditions without having to do some global setup function or
  // locking-heavy singleton checks at runtime. In practice, we always need this
  // before we can do anything interesting, so it's OK to wait for the
  // initializer.
  struct FunctionInfoInitializer {
    FunctionInfoMap map;
  
    FunctionInfoInitializer() {
  #define INSERT_FUNCTION(command, is_target)                             \
    map[k##command] = FunctionInfo(&Run##command, k##command##_HelpShort, \
                                   k##command##_Help, is_target);
  
      INSERT_FUNCTION(Action, true)
      INSERT_FUNCTION(ActionForEach, true)
      INSERT_FUNCTION(BundleData, true)
      INSERT_FUNCTION(CreateBundle, true)
      INSERT_FUNCTION(Copy, true)
      INSERT_FUNCTION(Executable, true)
      INSERT_FUNCTION(Group, true)
      INSERT_FUNCTION(LoadableModule, true)
      INSERT_FUNCTION(SharedLibrary, true)
      INSERT_FUNCTION(SourceSet, true)
      INSERT_FUNCTION(StaticLibrary, true)
      INSERT_FUNCTION(Target, true)
      INSERT_FUNCTION(GeneratedFile, true)
      INSERT_FUNCTION(RustLibrary, true)
  
      INSERT_FUNCTION(Assert, false)
      INSERT_FUNCTION(Config, false)
      INSERT_FUNCTION(DeclareArgs, false)
      INSERT_FUNCTION(Defined, false)
      INSERT_FUNCTION(ExecScript, false)
      INSERT_FUNCTION(ForEach, false)
      INSERT_FUNCTION(ForwardVariablesFrom, false)
      INSERT_FUNCTION(GetEnv, false)
      INSERT_FUNCTION(GetLabelInfo, false)
      INSERT_FUNCTION(GetPathInfo, false)
      INSERT_FUNCTION(GetTargetOutputs, false)
      INSERT_FUNCTION(Import, false)
      INSERT_FUNCTION(NotNeeded, false)
      INSERT_FUNCTION(Pool, false)
      INSERT_FUNCTION(Print, false)
      INSERT_FUNCTION(ProcessFileTemplate, false)
      INSERT_FUNCTION(ReadFile, false)
      INSERT_FUNCTION(RebasePath, false)
      INSERT_FUNCTION(SetDefaults, false)
      INSERT_FUNCTION(SetDefaultToolchain, false)
      INSERT_FUNCTION(SetSourcesAssignmentFilter, false)
      INSERT_FUNCTION(SplitList, false)
      INSERT_FUNCTION(StringReplace, false)
      INSERT_FUNCTION(Template, false)
      INSERT_FUNCTION(Tool, false)
      INSERT_FUNCTION(Toolchain, false)
      INSERT_FUNCTION(WriteFile, false)
  
  #undef INSERT_FUNCTION
    }
  };
  const FunctionInfoInitializer function_info;
  
  const FunctionInfoMap& GetFunctions() {
    return function_info.map;
  }
  
  Value RunFunction(Scope* scope,
                    const FunctionCallNode* function,
                    const ListNode* args_list,
                    BlockNode* block,
                    Err* err) {
    const Token& name = function->function();
  
    std::string template_name(function->function().value());
    const Template* templ = scope->GetTemplate(template_name);
    if (templ) {
      Value args = args_list->Execute(scope, err);
      if (err->has_error())
        return Value();
      return templ->Invoke(scope, function, template_name, args.list_value(),
                           block, err);
    }
  
    // No template matching this, check for a built-in function.
    const FunctionInfoMap& function_map = GetFunctions();
    FunctionInfoMap::const_iterator found_function =
        function_map.find(name.value());
    if (found_function == function_map.end()) {
      *err = Err(name, "Unknown function.");
      return Value();
    }
  
    if (found_function->second.self_evaluating_args_runner) {
      // Self evaluating args functions are special weird built-ins like foreach.
      // Rather than force them all to check that they have a block or no block
      // and risk bugs for new additions, check a whitelist here.
      if (found_function->second.self_evaluating_args_runner != &RunForEach) {
        if (!VerifyNoBlockForFunctionCall(function, block, err))
          return Value();
      }
      return found_function->second.self_evaluating_args_runner(scope, function,
                                                                args_list, err);
    }
  
    // All other function types take a pre-executed set of args.
    Value args = args_list->Execute(scope, err);
    if (err->has_error())
      return Value();
  
    if (found_function->second.generic_block_runner) {
      if (!block) {
        FillNeedsBlockError(function, err);
        return Value();
      }
      return found_function->second.generic_block_runner(
          scope, function, args.list_value(), block, err);
    }
  
    if (found_function->second.executed_block_runner) {
      if (!block) {
        FillNeedsBlockError(function, err);
        return Value();
      }
  
      Scope block_scope(scope);
      block->Execute(&block_scope, err);
      if (err->has_error())
        return Value();
  
      Value result = found_function->second.executed_block_runner(
          function, args.list_value(), &block_scope, err);
      if (err->has_error())
        return Value();
  
      if (!block_scope.CheckForUnusedVars(err))
        return Value();
      return result;
    }
  
    // Otherwise it's a no-block function.
    if (!VerifyNoBlockForFunctionCall(function, block, err))
      return Value();
    return found_function->second.no_block_runner(scope, function,
                                                  args.list_value(), err);
  }
  
  }  // namespace functions
  