549 lines
19 KiB
C++
549 lines
19 KiB
C++
|
// 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 <iostream>
|
||
|
|
||
|
#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/item_tree.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/target_manager.h"
|
||
|
#include "tools/gn/token.h"
|
||
|
#include "tools/gn/value.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
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 \")\".");
|
||
|
}
|
||
|
|
||
|
// This is called when a template is invoked. When we see a template
|
||
|
// declaration, that funciton is RunTemplate.
|
||
|
Value RunTemplateInvocation(Scope* scope,
|
||
|
const FunctionCallNode* invocation,
|
||
|
const std::vector<Value>& args,
|
||
|
BlockNode* block,
|
||
|
const FunctionCallNode* rule,
|
||
|
Err* err) {
|
||
|
if (!EnsureNotProcessingImport(invocation, scope, err))
|
||
|
return Value();
|
||
|
Scope block_scope(scope);
|
||
|
if (!FillTargetBlockScope(scope, invocation,
|
||
|
invocation->function().value().data(),
|
||
|
block, args, &block_scope, err))
|
||
|
return Value();
|
||
|
|
||
|
// Run the block for the rule invocation.
|
||
|
block->ExecuteBlockInScope(&block_scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
// Now run the rule itself with that block as the current scope.
|
||
|
rule->block()->ExecuteBlockInScope(&block_scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
bool EnsureNotProcessingImport(const ParseNode* node,
|
||
|
const Scope* scope,
|
||
|
Err* err) {
|
||
|
if (scope->IsProcessingImport()) {
|
||
|
*err = Err(node, "Not valid from an import.",
|
||
|
"We need to talk about this thing you are doing here. Doing this\n"
|
||
|
"kind of thing from an imported file makes me feel like you are\n"
|
||
|
"abusing me. Imports are for defining defaults, variables, and rules.\n"
|
||
|
"The 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 char* 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) {
|
||
|
if (!default_scope->NonRecursiveMergeTo(block_scope, 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;
|
||
|
}
|
||
|
|
||
|
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 SourceDir& SourceDirForFunctionCall(const FunctionCallNode* function) {
|
||
|
return function->function().location().file()->dir();
|
||
|
}
|
||
|
|
||
|
const Label& ToolchainLabelForScope(const Scope* scope) {
|
||
|
return scope->settings()->toolchain()->label();
|
||
|
}
|
||
|
|
||
|
Label MakeLabelForScope(const Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const std::string& name) {
|
||
|
const SourceDir& input_dir = SourceDirForFunctionCall(function);
|
||
|
const Label& toolchain_label = ToolchainLabelForScope(scope);
|
||
|
return Label(input_dir, name, toolchain_label.dir(), toolchain_label.name());
|
||
|
}
|
||
|
|
||
|
namespace functions {
|
||
|
|
||
|
// assert ----------------------------------------------------------------------
|
||
|
|
||
|
const char kAssert[] = "assert";
|
||
|
const char kAssert_Help[] =
|
||
|
"TODO(brettw) WRITE ME";
|
||
|
|
||
|
Value RunAssert(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
Err* err) {
|
||
|
if (args.size() != 1) {
|
||
|
*err = Err(function->function(), "Wrong number of arguments.",
|
||
|
"assert() takes one argument, "
|
||
|
"were you expecting somethig else?");
|
||
|
} else if (args[0].InterpretAsInt() == 0) {
|
||
|
*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_Help[] =
|
||
|
"TODO(brettw) write this.";
|
||
|
|
||
|
Value RunConfig(const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
Scope* scope,
|
||
|
Err* err) {
|
||
|
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("Generating config", label.GetUserVisibleName(true));
|
||
|
|
||
|
// Create the empty config object.
|
||
|
ItemTree* tree = &scope->settings()->build_settings()->item_tree();
|
||
|
Config* config = Config::GetConfig(scope->settings(), function->GetRange(),
|
||
|
label, NULL, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
// Fill it.
|
||
|
const SourceDir input_dir = SourceDirForFunctionCall(function);
|
||
|
ConfigValuesGenerator gen(&config->config_values(), scope,
|
||
|
function->function(), input_dir, err);
|
||
|
gen.Run();
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
// Mark as complete.
|
||
|
{
|
||
|
base::AutoLock lock(tree->lock());
|
||
|
tree->MarkItemDefinedLocked(scope->settings()->build_settings(), label,
|
||
|
err);
|
||
|
}
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// declare_args ----------------------------------------------------------------
|
||
|
|
||
|
const char kDeclareArgs[] = "declare_args";
|
||
|
const char kDeclareArgs_Help[] =
|
||
|
"TODO(brettw) write this.";
|
||
|
|
||
|
Value RunDeclareArgs(const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
Scope* scope,
|
||
|
Err* err) {
|
||
|
// Only allow this to be called once. We use a variable in the current scope
|
||
|
// with a name the parser will reject if the user tried to type it.
|
||
|
const char did_declare_args_var[] = "@@declared_args";
|
||
|
if (scope->GetValue(did_declare_args_var)) {
|
||
|
*err = Err(function->function(), "Duplicate call to declared_args.");
|
||
|
err->AppendSubErr(
|
||
|
Err(scope->GetValue(did_declare_args_var)->origin()->GetRange(),
|
||
|
"See the original call."));
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// Find the root scope where the values will be set.
|
||
|
Scope* root = scope->mutable_containing();
|
||
|
if (!root || root->containing() || !scope->IsProcessingBuildConfig()) {
|
||
|
*err = Err(function->function(), "declare_args called incorrectly."
|
||
|
"It must be called only from the build config script and in the "
|
||
|
"root scope.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// Take all variables set in the current scope as default values and put
|
||
|
// them in the parent scope. The values in the current scope are the defaults,
|
||
|
// then we apply the external args to this list.
|
||
|
Scope::KeyValueVector values;
|
||
|
scope->GetCurrentScopeValues(&values);
|
||
|
for (size_t i = 0; i < values.size(); i++) {
|
||
|
// TODO(brettw) actually import the arguments from the command line rather
|
||
|
// than only using the defaults.
|
||
|
root->SetValue(values[i].first, values[i].second,
|
||
|
values[i].second.origin());
|
||
|
}
|
||
|
|
||
|
scope->SetValue(did_declare_args_var, Value(function, 1), NULL);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// import ----------------------------------------------------------------------
|
||
|
|
||
|
const char kImport[] = "import";
|
||
|
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"
|
||
|
" It does not do an \"include\". The imported file is executed in a\n"
|
||
|
" standalone environment from the caller of the import command. The\n"
|
||
|
" results of this execution are cached for other files that import the\n"
|
||
|
" 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)\n"
|
||
|
" a runtime error will be thrown. Therefore, it's good practice to\n"
|
||
|
" minimize the stuff that an imported file defines.\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<Value>& args,
|
||
|
Err* err) {
|
||
|
if (!EnsureSingleStringArg(function, args, err) ||
|
||
|
!EnsureNotProcessingImport(function, scope, err))
|
||
|
return Value();
|
||
|
|
||
|
const SourceDir input_dir = SourceDirForFunctionCall(function);
|
||
|
SourceFile import_file =
|
||
|
input_dir.ResolveRelativeFile(args[0].string_value());
|
||
|
scope->settings()->import_manager().DoImport(import_file, function,
|
||
|
scope, err);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// set_defaults ----------------------------------------------------------------
|
||
|
|
||
|
const char kSetDefaults[] = "set_defaults";
|
||
|
const char kSetDefaults_Help[] =
|
||
|
"TODO(brettw) write this.";
|
||
|
|
||
|
Value RunSetDefaults(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
BlockNode* block,
|
||
|
Err* err) {
|
||
|
if (!EnsureSingleStringArg(function, args, err))
|
||
|
return Value();
|
||
|
const std::string& target_type(args[0].string_value());
|
||
|
|
||
|
// Ensure there aren't defaults already set.
|
||
|
if (scope->GetTargetDefaults(target_type)) {
|
||
|
*err = Err(function->function(),
|
||
|
"This target type defaults were already set.");
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// Execute the block in a new scope that has a parent of the containing
|
||
|
// scope.
|
||
|
Scope block_scope(scope);
|
||
|
if (!FillTargetBlockScope(scope, function,
|
||
|
function->function().value().data(),
|
||
|
block, args, &block_scope, err))
|
||
|
return Value();
|
||
|
|
||
|
// Run the block for the rule invocation.
|
||
|
block->ExecuteBlockInScope(&block_scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
|
||
|
// Now copy the values set on the scope we made into the free-floating one
|
||
|
// (with no containing scope) used to hold the target defaults.
|
||
|
Scope* dest = scope->MakeTargetDefaults(target_type);
|
||
|
block_scope.NonRecursiveMergeTo(dest, function, "<SHOULD NOT FAIL>", err);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// set_sources_assignment_filter -----------------------------------------------
|
||
|
|
||
|
const char kSetSourcesAssignmentFilter[] = "set_sources_assignment_filter";
|
||
|
const char kSetSourcesAssignmentFilter_Help[] =
|
||
|
"TODO(brettw) write this.";
|
||
|
|
||
|
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 {
|
||
|
scoped_ptr<PatternList> f(new PatternList);
|
||
|
f->SetFromValue(args[0], err);
|
||
|
if (!err->has_error())
|
||
|
scope->set_sources_assignment_filter(f.Pass());
|
||
|
}
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// print -----------------------------------------------------------------------
|
||
|
|
||
|
const char kPrint[] = "print";
|
||
|
const char kPrint_Help[] =
|
||
|
"print(...)\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<Value>& args,
|
||
|
Err* err) {
|
||
|
for (size_t i = 0; i < args.size(); i++) {
|
||
|
if (i != 0)
|
||
|
std::cout << " ";
|
||
|
std::cout << args[i].ToString();
|
||
|
}
|
||
|
std::cout << std::endl;
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// -----------------------------------------------------------------------------
|
||
|
|
||
|
FunctionInfo::FunctionInfo()
|
||
|
: generic_block_runner(NULL),
|
||
|
executed_block_runner(NULL),
|
||
|
no_block_runner(NULL),
|
||
|
help(NULL) {
|
||
|
}
|
||
|
|
||
|
FunctionInfo::FunctionInfo(GenericBlockFunction gbf, const char* in_help)
|
||
|
: generic_block_runner(gbf),
|
||
|
executed_block_runner(NULL),
|
||
|
no_block_runner(NULL),
|
||
|
help(in_help) {
|
||
|
}
|
||
|
|
||
|
FunctionInfo::FunctionInfo(ExecutedBlockFunction ebf, const char* in_help)
|
||
|
: generic_block_runner(NULL),
|
||
|
executed_block_runner(ebf),
|
||
|
no_block_runner(NULL),
|
||
|
help(in_help) {
|
||
|
}
|
||
|
|
||
|
FunctionInfo::FunctionInfo(NoBlockFunction nbf, const char* in_help)
|
||
|
: generic_block_runner(NULL),
|
||
|
executed_block_runner(NULL),
|
||
|
no_block_runner(nbf),
|
||
|
help(in_help) {
|
||
|
}
|
||
|
|
||
|
// 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) \
|
||
|
map[k##command] = FunctionInfo(&Run##command, k##command##_Help);
|
||
|
|
||
|
INSERT_FUNCTION(Assert)
|
||
|
INSERT_FUNCTION(Component)
|
||
|
INSERT_FUNCTION(Config)
|
||
|
INSERT_FUNCTION(Copy)
|
||
|
INSERT_FUNCTION(Custom)
|
||
|
INSERT_FUNCTION(DeclareArgs)
|
||
|
INSERT_FUNCTION(ExecScript)
|
||
|
INSERT_FUNCTION(Executable)
|
||
|
INSERT_FUNCTION(Group)
|
||
|
INSERT_FUNCTION(Import)
|
||
|
INSERT_FUNCTION(Print)
|
||
|
INSERT_FUNCTION(ProcessFileTemplate)
|
||
|
INSERT_FUNCTION(ReadFile)
|
||
|
INSERT_FUNCTION(SetDefaults)
|
||
|
INSERT_FUNCTION(SetDefaultToolchain)
|
||
|
INSERT_FUNCTION(SetSourcesAssignmentFilter)
|
||
|
INSERT_FUNCTION(SharedLibrary)
|
||
|
INSERT_FUNCTION(StaticLibrary)
|
||
|
INSERT_FUNCTION(Template)
|
||
|
INSERT_FUNCTION(Test)
|
||
|
INSERT_FUNCTION(Tool)
|
||
|
INSERT_FUNCTION(Toolchain)
|
||
|
INSERT_FUNCTION(WriteFile)
|
||
|
|
||
|
#undef INSERT_FUNCTION
|
||
|
}
|
||
|
};
|
||
|
const FunctionInfoInitializer function_info;
|
||
|
|
||
|
const FunctionInfoMap& GetFunctions() {
|
||
|
return function_info.map;
|
||
|
}
|
||
|
|
||
|
Value RunFunction(Scope* scope,
|
||
|
const FunctionCallNode* function,
|
||
|
const std::vector<Value>& args,
|
||
|
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 build-in function matching this, check for a template.
|
||
|
const FunctionCallNode* rule =
|
||
|
scope->GetTemplate(function->function().value().as_string());
|
||
|
if (rule)
|
||
|
return RunTemplateInvocation(scope, function, args, block, rule, err);
|
||
|
|
||
|
*err = Err(name, "Unknown function.");
|
||
|
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, block, err);
|
||
|
}
|
||
|
|
||
|
if (found_function->second.executed_block_runner) {
|
||
|
if (!block) {
|
||
|
FillNeedsBlockError(function, err);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
Scope block_scope(scope);
|
||
|
block->ExecuteBlockInScope(&block_scope, err);
|
||
|
if (err->has_error())
|
||
|
return Value();
|
||
|
return found_function->second.executed_block_runner(
|
||
|
function, args, &block_scope, err);
|
||
|
}
|
||
|
|
||
|
// Otherwise it's a no-block function.
|
||
|
return found_function->second.no_block_runner(scope, function, args, err);
|
||
|
}
|
||
|
|
||
|
} // namespace functions
|