// 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 #include #include #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_tree.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; } } // namespace 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& 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("target_name"); 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& 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(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[] = "assert: Assert an expression is true at generation time.\n" "\n" " assert( [, ])\n" "\n" " If the condition is false, the build will fail with an error. If the\n" " optional second argument is provided, that string will be printed\n" " with the error message.\n" "\n" "Examples:\n" " assert(is_win)\n" " assert(defined(sources), \"Sources must be defined\")\n"; Value RunAssert(Scope* scope, const FunctionCallNode* function, const std::vector& 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.", "<<>>"); } 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[] = "config: Defines a configuration object.\n" "\n" " Configuration objects can be applied to targets and specify sets of\n" " compiler flags, includes, defines, etc. They provide a way to\n" " conveniently group sets of this configuration information.\n" "\n" " A config is referenced by its label just like a target.\n" "\n" " The values in a config are additive only. If you want to remove a flag\n" " you need to remove the corresponding config that sets it. The final\n" " set of flags, defines, etc. for a target is generated in this order:\n" "\n" " 1. The values specified directly on the target (rather than using a\n" " config.\n" " 2. The configs specified in the target's \"configs\" list, in order.\n" " 3. Public_configs from a breadth-first traversal of the dependency\n" " tree in the order that the targets appear in \"deps\".\n" " 4. All dependent configs from a breadth-first traversal of the\n" " dependency tree in the order that the targets appear in \"deps\".\n" "\n" "Variables valid in a config definition\n" "\n" CONFIG_VALUES_VARS_HELP " Nested configs: configs\n" "\n" "Variables on a target used to apply configs\n" "\n" " all_dependent_configs, configs, public_configs\n" "\n" "Example\n" "\n" " config(\"myconfig\") {\n" " includes = [ \"include/common\" ]\n" " defines = [ \"ENABLE_DOOM_MELON\" ]\n" " }\n" "\n" " executable(\"mything\") {\n" " configs = [ \":myconfig\" ]\n" " }\n"; Value RunConfig(const FunctionCallNode* function, const std::vector& 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. scoped_ptr config(new Config(scope->settings(), label)); 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(config.release()); return Value(); } // declare_args ---------------------------------------------------------------- const char kDeclareArgs[] = "declare_args"; const char kDeclareArgs_HelpShort[] = "declare_args: Declare build arguments."; const char kDeclareArgs_Help[] = "declare_args: Declare build arguments.\n" "\n" " Introduces the given arguments into the current scope. If they are\n" " not specified on the command line or in a toolchain's arguments,\n" " the default values given in the declare_args block will be used.\n" " However, these defaults will not override command-line values.\n" "\n" " See also \"gn help buildargs\" for an overview.\n" "\n" " The precise behavior of declare args is:\n" "\n" " 1. The declare_arg block executes. Any variables in the enclosing\n" " scope are available for reading.\n" "\n" " 2. At the end of executing the block, any variables set within that\n" " scope are saved globally as build arguments, with their current\n" " values being saved as the \"default value\" for that argument.\n" "\n" " 3. User-defined overrides are applied. Anything set in \"gn args\"\n" " now overrides any default values. The resulting set of variables\n" " is promoted to be readable from the following code in the file.\n" "\n" " This has some ramifications that may not be obvious:\n" "\n" " - You should not perform difficult work inside a declare_args block\n" " since this only sets a default value that may be discarded. In\n" " particular, don't use the result of exec_script() to set the\n" " default value. If you want to have a script-defined default, set\n" " some default \"undefined\" value like [], \"\", or -1, and after\n" " the declare_args block, call exec_script if the value is unset by\n" " the user.\n" "\n" " - Any code inside of the declare_args block will see the default\n" " values of previous variables defined in the block rather than\n" " the user-overridden value. This can be surprising because you will\n" " be used to seeing the overridden value. If you need to make the\n" " default value of one arg dependent on the possibly-overridden\n" " value of another, write two separate declare_args blocks:\n" "\n" " declare_args() {\n" " enable_foo = true\n" " }\n" " declare_args() {\n" " # Bar defaults to same user-overridden state as foo.\n" " enable_bar = enable_foo\n" " }\n" "\n" "Example\n" "\n" " declare_args() {\n" " enable_teleporter = true\n" " enable_doom_melon = false\n" " }\n" "\n" " If you want to override the (default disabled) Doom Melon:\n" " gn --args=\"enable_doom_melon=true enable_teleporter=false\"\n" " This also sets the teleporter, but it's already defaulted to on so\n" " it will have no effect.\n"; Value RunDeclareArgs(Scope* scope, const FunctionCallNode* function, const std::vector& args, BlockNode* block, Err* err) { NonNestableBlock non_nestable(scope, function, "declare_args"); if (!non_nestable.Enter(err)) return Value(); Scope block_scope(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[] = "defined: Returns whether an identifier is defined.\n" "\n" " Returns true if the given argument is defined. This is most useful in\n" " templates to assert that the caller set things up properly.\n" "\n" " You can pass an identifier:\n" " defined(foo)\n" " which will return true or false depending on whether foo is defined in\n" " the current scope.\n" "\n" " You can also check a named scope:\n" " defined(foo.bar)\n" " which will return true or false depending on whether bar is defined in\n" " the named scope foo. It will throw an error if foo is not defined or\n" " is not a scope.\n" "\n" "Example:\n" "\n" " template(\"mytemplate\") {\n" " # To help users call this template properly...\n" " assert(defined(invoker.sources), \"Sources must be defined\")\n" "\n" " # If we want to accept an optional \"values\" argument, we don't\n" " # want to dereference something that may not be defined.\n" " if (defined(invoker.values)) {\n" " values = invoker.values\n" " } else {\n" " values = \"some default value\"\n" " }\n" " }\n"; Value RunDefined(Scope* scope, const FunctionCallNode* function, const ListNode* args_list, Err* err) { const std::vector& 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[] = "getenv: Get an environment variable.\n" "\n" " value = getenv(env_var_name)\n" "\n" " Returns the value of the given enironment variable. If the value is\n" " not found, it will try to look up the variable with the \"opposite\"\n" " case (based on the case of the first letter of the variable), but\n" " is otherwise case-sensitive.\n" "\n" " If the environment variable is not found, the empty string will be\n" " returned. Note: it might be nice to extend this if we had the concept\n" " of \"none\" in the language to indicate lookup failure.\n" "\n" "Example:\n" "\n" " home_dir = getenv(\"HOME\")\n"; Value RunGetEnv(Scope* scope, const FunctionCallNode* function, const std::vector& args, Err* err) { if (!EnsureSingleStringArg(function, args, err)) return Value(); scoped_ptr 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[] = "import: Import a file into the current scope.\n" "\n" " The import command loads the rules and variables resulting from\n" " executing the given file into the current scope.\n" "\n" " By convention, imported files are named with a .gni extension.\n" "\n" " An import is different than a C++ \"include\". The imported file is\n" " executed in a standalone environment from the caller of the import\n" " command. The results of this execution are cached for other files that\n" " import the same .gni file.\n" "\n" " Note that you can not import a BUILD.gn file that's otherwise used\n" " in the build. Files must either be imported or implicitly loaded as\n" " a result of deps rules, but not both.\n" "\n" " The imported file's scope will be merged with the scope at the point\n" " import was called. If there is a conflict (both the current scope and\n" " the imported file define some variable or rule with the same name but\n" " different value), a runtime error will be thrown. Therefore, it's good\n" " practice to minimize the stuff that an imported file defines.\n" "\n" " Variables and templates beginning with an underscore '_' are\n" " considered private and will not be imported. Imported files can use\n" " such variables for internal computation without affecting other files.\n" "\n" "Examples:\n" "\n" " import(\"//build/rules/idl_compilation_rule.gni\")\n" "\n" " # Looks in the current directory.\n" " import(\"my_vars.gni\")\n"; Value RunImport(Scope* scope, const FunctionCallNode* function, const std::vector& 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()); if (!err->has_error()) { scope->settings()->import_manager().DoImport(import_file, function, scope, err); } 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[] = "set_sources_assignment_filter: Set a pattern to filter source files.\n" "\n" " The sources assignment filter is a list of patterns that remove files\n" " from the list implicitly whenever the \"sources\" variable is\n" " assigned to. This is intended to be used to globally filter out files\n" " with platform-specific naming schemes when they don't apply, for\n" " example, you may want to filter out all \"*_win.cc\" files on non-\n" " Windows platforms.\n" "\n" " Typically this will be called once in the master build config script\n" " to set up the filter for the current platform. Subsequent calls will\n" " overwrite the previous values.\n" "\n" " If you want to bypass the filter and add a file even if it might\n" " be filtered out, call set_sources_assignment_filter([]) to clear the\n" " list of filters. This will apply until the current scope exits\n" "\n" "How to use patterns\n" "\n" " File patterns are VERY limited regular expressions. They must match\n" " the entire input string to be counted as a match. In regular\n" " expression parlance, there is an implicit \"^...$\" surrounding your\n" " input. If you want to match a substring, you need to use wildcards at\n" " the beginning and end.\n" "\n" " There are only two special tokens understood by the pattern matcher.\n" " Everything else is a literal.\n" "\n" " * Matches zero or more of any character. It does not depend on the\n" " preceding character (in regular expression parlance it is\n" " equivalent to \".*\").\n" "\n" " \\b Matches a path boundary. This will match the beginning or end of\n" " a string, or a slash.\n" "\n" "Pattern examples\n" "\n" " \"*asdf*\"\n" " Matches a string containing \"asdf\" anywhere.\n" "\n" " \"asdf\"\n" " Matches only the exact string \"asdf\".\n" "\n" " \"*.cc\"\n" " Matches strings ending in the literal \".cc\".\n" "\n" " \"\\bwin/*\"\n" " Matches \"win/foo\" and \"foo/win/bar.cc\" but not \"iwin/foo\".\n" "\n" "Sources assignment example\n" "\n" " # Filter out all _win files.\n" " set_sources_assignment_filter([ \"*_win.cc\", \"*_win.h\" ])\n" " sources = [ \"a.cc\", \"b_win.cc\" ]\n" " print(sources)\n" " # Will print [ \"a.cc\" ]. b_win one was filtered out.\n"; Value RunSetSourcesAssignmentFilter(Scope* scope, const FunctionCallNode* function, const std::vector& args, Err* err) { if (args.size() != 1) { *err = Err(function, "set_sources_assignment_filter takes one argument."); } else { scoped_ptr f(new PatternList); f->SetFromValue(args[0], err); if (!err->has_error()) scope->set_sources_assignment_filter(std::move(f)); } return Value(); } // print ----------------------------------------------------------------------- const char kPrint[] = "print"; const char kPrint_HelpShort[] = "print: Prints to the console."; const char kPrint_Help[] = "print: Prints to the console.\n" "\n" " Prints all arguments to the console separated by spaces. A newline is\n" " automatically appended to the end.\n" "\n" " This function is intended for debugging. Note that build files are run\n" " in parallel so you may get interleaved prints. A buildfile may also\n" " be executed more than once in parallel in the context of different\n" " toolchains so the prints from one file may be duplicated or\n" " interleaved with itself.\n" "\n" "Examples:\n" " print(\"Hello world\")\n" "\n" " print(sources, deps)\n"; Value RunPrint(Scope* scope, const FunctionCallNode* function, const std::vector& 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()); else cb.Run(output); return Value(); } // ----------------------------------------------------------------------------- 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(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(Template, false) INSERT_FUNCTION(Tool, false) INSERT_FUNCTION(Toolchain, false) INSERT_FUNCTION(ToolchainArgs, 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(); const FunctionInfoMap& function_map = GetFunctions(); FunctionInfoMap::const_iterator found_function = function_map.find(name.value()); if (found_function == function_map.end()) { // No built-in function matching this, check for a template. const Template* templ = scope->GetTemplate(function->function().value().as_string()); if (templ) { Value args = args_list->Execute(scope, err); if (err->has_error()) return Value(); return templ->Invoke(scope, function, args.list_value(), block, err); } *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