blob: 210cc2c84b45c092f1c57732d9df70d6b2b66689 [file] [log] [blame]
// 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 <algorithm>
#include <limits>
#include <utility>
#include "tools/gn/err.h"
#include "tools/gn/functions.h"
#include "tools/gn/label.h"
#include "tools/gn/label_ptr.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/tool.h"
#include "tools/gn/toolchain.h"
#include "tools/gn/value_extractors.h"
#include "tools/gn/variables.h"
namespace functions {
namespace {
// This is just a unique value to take the address of to use as the key for
// the toolchain property on a scope.
const int kToolchainPropertyKey = 0;
bool ReadBool(Scope* scope,
const char* var,
Tool* tool,
void (Tool::*set)(bool),
Err* err) {
const Value* v = scope->GetValue(var, true);
if (!v)
return true; // Not present is fine.
if (!v->VerifyTypeIs(Value::BOOLEAN, err))
return false;
(tool->*set)(v->boolean_value());
return true;
}
// Reads the given string from the scope (if present) and puts the result into
// dest. If the value is not a string, sets the error and returns false.
bool ReadString(Scope* scope,
const char* var,
Tool* tool,
void (Tool::*set)(const std::string&),
Err* err) {
const Value* v = scope->GetValue(var, true);
if (!v)
return true; // Not present is fine.
if (!v->VerifyTypeIs(Value::STRING, err))
return false;
(tool->*set)(v->string_value());
return true;
}
// Reads the given label from the scope (if present) and puts the result into
// dest. If the value is not a label, sets the error and returns false.
bool ReadLabel(Scope* scope,
const char* var,
Tool* tool,
const ParseNode* origin,
const Label& current_toolchain,
void (Tool::*set)(const LabelPtrPair<Pool>&),
Err* err) {
const Value* v = scope->GetValue(var, true);
if (!v)
return true; // Not present is fine.
Label label =
Label::Resolve(scope->GetSourceDir(), current_toolchain, *v, err);
if (err->has_error())
return false;
LabelPtrPair<Pool> pair(label);
pair.origin = origin;
(tool->*set)(pair);
return true;
}
// Calls the given validate function on each type in the list. On failure,
// sets the error, blame the value, and return false.
bool ValidateSubstitutionList(const std::vector<SubstitutionType>& list,
bool (*validate)(SubstitutionType),
const Value* origin,
Err* err) {
for (const auto& cur_type : list) {
if (!validate(cur_type)) {
*err = Err(*origin, "Pattern not valid here.",
"You used the pattern " + std::string(kSubstitutionNames[cur_type]) +
" which is not valid\nfor this variable.");
return false;
}
}
return true;
}
bool ReadPattern(Scope* scope,
const char* name,
bool (*validate)(SubstitutionType),
Tool* tool,
void (Tool::*set)(const SubstitutionPattern&),
Err* err) {
const Value* value = scope->GetValue(name, true);
if (!value)
return true; // Not present is fine.
if (!value->VerifyTypeIs(Value::STRING, err))
return false;
SubstitutionPattern pattern;
if (!pattern.Parse(*value, err))
return false;
if (!ValidateSubstitutionList(pattern.required_types(), validate, value, err))
return false;
(tool->*set)(pattern);
return true;
}
bool ReadOutputExtension(Scope* scope, Tool* tool, Err* err) {
const Value* value = scope->GetValue("default_output_extension", true);
if (!value)
return true; // Not present is fine.
if (!value->VerifyTypeIs(Value::STRING, err))
return false;
if (value->string_value().empty())
return true; // Accept empty string.
if (value->string_value()[0] != '.') {
*err = Err(*value, "default_output_extension must begin with a '.'");
return false;
}
tool->set_default_output_extension(value->string_value());
return true;
}
bool ReadPrecompiledHeaderType(Scope* scope, Tool* tool, Err* err) {
const Value* value = scope->GetValue("precompiled_header_type", true);
if (!value)
return true; // Not present is fine.
if (!value->VerifyTypeIs(Value::STRING, err))
return false;
if (value->string_value().empty())
return true; // Accept empty string, do nothing (default is "no PCH").
if (value->string_value() == "gcc") {
tool->set_precompiled_header_type(Tool::PCH_GCC);
return true;
} else if (value->string_value() == "msvc") {
tool->set_precompiled_header_type(Tool::PCH_MSVC);
return true;
}
*err = Err(*value, "Invalid precompiled_header_type",
"Must either be empty, \"gcc\", or \"msvc\".");
return false;
}
bool ReadDepsFormat(Scope* scope, Tool* tool, Err* err) {
const Value* value = scope->GetValue("depsformat", true);
if (!value)
return true; // Not present is fine.
if (!value->VerifyTypeIs(Value::STRING, err))
return false;
if (value->string_value() == "gcc") {
tool->set_depsformat(Tool::DEPS_GCC);
} else if (value->string_value() == "msvc") {
tool->set_depsformat(Tool::DEPS_MSVC);
} else {
*err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
return false;
}
return true;
}
bool ReadOutputs(Scope* scope,
const FunctionCallNode* tool_function,
bool (*validate)(SubstitutionType),
Tool* tool,
Err* err) {
const Value* value = scope->GetValue("outputs", true);
if (!value) {
*err = Err(tool_function, "\"outputs\" must be specified for this tool.");
return false;
}
SubstitutionList list;
if (!list.Parse(*value, err))
return false;
// Validate the right kinds of patterns are used.
if (!ValidateSubstitutionList(list.required_types(), validate, value, err))
return false;
// There should always be at least one output.
if (list.list().empty()) {
*err = Err(*value, "Outputs list is empty.", "I need some outputs.");
return false;
}
tool->set_outputs(list);
return true;
}
bool IsCompilerTool(Toolchain::ToolType type) {
return type == Toolchain::TYPE_CC ||
type == Toolchain::TYPE_CXX ||
type == Toolchain::TYPE_OBJC ||
type == Toolchain::TYPE_OBJCXX ||
type == Toolchain::TYPE_RC ||
type == Toolchain::TYPE_ASM;
}
bool IsLinkerTool(Toolchain::ToolType type) {
// "alink" is not counted as in the generic "linker" tool list.
return type == Toolchain::TYPE_SOLINK ||
type == Toolchain::TYPE_SOLINK_MODULE ||
type == Toolchain::TYPE_LINK;
}
bool IsPatternInOutputList(const SubstitutionList& output_list,
const SubstitutionPattern& pattern) {
for (const auto& cur : output_list.list()) {
if (pattern.ranges().size() == cur.ranges().size() &&
std::equal(pattern.ranges().begin(), pattern.ranges().end(),
cur.ranges().begin()))
return true;
}
return false;
}
} // namespace
// toolchain -------------------------------------------------------------------
const char kToolchain[] = "toolchain";
const char kToolchain_HelpShort[] =
"toolchain: Defines a toolchain.";
const char kToolchain_Help[] =
"toolchain: Defines a toolchain.\n"
"\n"
" A toolchain is a set of commands and build flags used to compile the\n"
" source code. You can have more than one toolchain in use at once in\n"
" a build.\n"
"\n"
"Functions and variables\n"
"\n"
" tool()\n"
" The tool() function call specifies the commands commands to run for\n"
" a given step. See \"gn help tool\".\n"
"\n"
" toolchain_args()\n"
" List of arguments to pass to the toolchain when invoking this\n"
" toolchain. This applies only to non-default toolchains. See\n"
" \"gn help toolchain_args\" for more.\n"
"\n"
" deps\n"
" Dependencies of this toolchain. These dependencies will be resolved\n"
" before any target in the toolchain is compiled. To avoid circular\n"
" dependencies these must be targets defined in another toolchain.\n"
"\n"
" This is expressed as a list of targets, and generally these targets\n"
" will always specify a toolchain:\n"
" deps = [ \"//foo/bar:baz(//build/toolchain:bootstrap)\" ]\n"
"\n"
" This concept is somewhat inefficient to express in Ninja (it\n"
" requires a lot of duplicate of rules) so should only be used when\n"
" absolutely necessary.\n"
"\n"
" concurrent_links\n"
" In integer expressing the number of links that Ninja will perform in\n"
" parallel. GN will create a pool for shared library and executable\n"
" link steps with this many processes. Since linking is memory- and\n"
" I/O-intensive, projects with many large targets may want to limit\n"
" the number of parallel steps to avoid overloading the computer.\n"
" Since creating static libraries is generally not as intensive\n"
" there is no limit to \"alink\" steps.\n"
"\n"
" Defaults to 0 which Ninja interprets as \"no limit\".\n"
"\n"
" The value used will be the one from the default toolchain of the\n"
" current build.\n"
"\n"
"Invoking targets in toolchains:\n"
"\n"
" By default, when a target depends on another, there is an implicit\n"
" toolchain label that is inherited, so the dependee has the same one\n"
" as the dependent.\n"
"\n"
" You can override this and refer to any other toolchain by explicitly\n"
" labeling the toolchain to use. For example:\n"
" data_deps = [ \"//plugins:mine(//toolchains:plugin_toolchain)\" ]\n"
" The string \"//build/toolchains:plugin_toolchain\" is a label that\n"
" identifies the toolchain declaration for compiling the sources.\n"
"\n"
" To load a file in an alternate toolchain, GN does the following:\n"
"\n"
" 1. Loads the file with the toolchain definition in it (as determined\n"
" by the toolchain label).\n"
" 2. Re-runs the master build configuration file, applying the\n"
" arguments specified by the toolchain_args section of the toolchain\n"
" definition (see \"gn help toolchain_args\").\n"
" 3. Loads the destination build file in the context of the\n"
" configuration file in the previous step.\n"
"\n"
"Example:\n"
" toolchain(\"plugin_toolchain\") {\n"
" concurrent_links = 8\n"
"\n"
" tool(\"cc\") {\n"
" command = \"gcc {{source}}\"\n"
" ...\n"
" }\n"
"\n"
" toolchain_args() {\n"
" is_plugin = true\n"
" is_32bit = true\n"
" is_64bit = false\n"
" }\n"
" }\n";
Value RunToolchain(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
NonNestableBlock non_nestable(scope, function, "toolchain");
if (!non_nestable.Enter(err))
return Value();
if (!EnsureNotProcessingImport(function, scope, err) ||
!EnsureNotProcessingBuildConfig(function, scope, err))
return Value();
// Note that we don't want to use MakeLabelForScope since that will include
// the toolchain name in the label, and toolchain labels don't themselves
// have toolchain names.
const SourceDir& input_dir = scope->GetSourceDir();
Label label(input_dir, args[0].string_value());
if (g_scheduler->verbose_logging())
g_scheduler->Log("Defining toolchain", label.GetUserVisibleName(false));
// This object will actually be copied into the one owned by the toolchain
// manager, but that has to be done in the lock.
std::unique_ptr<Toolchain> toolchain(new Toolchain(scope->settings(), label));
toolchain->set_defined_from(function);
toolchain->visibility().SetPublic();
Scope block_scope(scope);
block_scope.SetProperty(&kToolchainPropertyKey, toolchain.get());
block->Execute(&block_scope, err);
block_scope.SetProperty(&kToolchainPropertyKey, nullptr);
if (err->has_error())
return Value();
// Read deps (if any).
const Value* deps_value = block_scope.GetValue(variables::kDeps, true);
if (deps_value) {
ExtractListOfLabels(
*deps_value, block_scope.GetSourceDir(),
ToolchainLabelForScope(&block_scope), &toolchain->deps(), err);
if (err->has_error())
return Value();
}
// Read concurrent_links (if any).
const Value* concurrent_links_value =
block_scope.GetValue("concurrent_links", true);
if (concurrent_links_value) {
if (!concurrent_links_value->VerifyTypeIs(Value::INTEGER, err))
return Value();
if (concurrent_links_value->int_value() < 0 ||
concurrent_links_value->int_value() > std::numeric_limits<int>::max()) {
*err = Err(*concurrent_links_value, "Value out of range.");
return Value();
}
toolchain->set_concurrent_links(
static_cast<int>(concurrent_links_value->int_value()));
}
if (!block_scope.CheckForUnusedVars(err))
return Value();
// Save this toolchain.
toolchain->ToolchainSetupComplete();
Scope::ItemVector* collector = scope->GetItemCollector();
if (!collector) {
*err = Err(function, "Can't define a toolchain in this context.");
return Value();
}
collector->push_back(toolchain.release());
return Value();
}
// tool ------------------------------------------------------------------------
const char kTool[] = "tool";
const char kTool_HelpShort[] =
"tool: Specify arguments to a toolchain tool.";
const char kTool_Help[] =
"tool: Specify arguments to a toolchain tool.\n"
"\n"
"Usage:\n"
"\n"
" tool(<tool type>) {\n"
" <tool variables...>\n"
" }\n"
"\n"
"Tool types\n"
"\n"
" Compiler tools:\n"
" \"cc\": C compiler\n"
" \"cxx\": C++ compiler\n"
" \"objc\": Objective C compiler\n"
" \"objcxx\": Objective C++ compiler\n"
" \"rc\": Resource compiler (Windows .rc files)\n"
" \"asm\": Assembler\n"
"\n"
" Linker tools:\n"
" \"alink\": Linker for static libraries (archives)\n"
" \"solink\": Linker for shared libraries\n"
" \"link\": Linker for executables\n"
"\n"
" Other tools:\n"
" \"stamp\": Tool for creating stamp files\n"
" \"copy\": Tool to copy files.\n"
"\n"
" Platform specific tools:\n"
" \"copy_bundle_data\": [iOS, OS X] Tool to copy files in a bundle.\n"
" \"compile_xcassets\": [iOS, OS X] Tool to compile asset catalogs.\n"
"\n"
"Tool variables\n"
"\n"
" command [string with substitutions]\n"
" Valid for: all tools (required)\n"
"\n"
" The command to run.\n"
"\n"
" default_output_dir [string with substitutions]\n"
" Valid for: linker tools\n"
"\n"
" Default directory name for the output file relative to the\n"
" root_build_dir. It can contain other substitution patterns.\n"
" This will be the default value for the {{output_dir}} expansion\n"
" (discussed below) but will be overridden by the \"output_dir\"\n"
" variable in a target, if one is specified.\n"
"\n"
" GN doesn't do anything with this string other than pass it\n"
" along, potentially with target-specific overrides. It is the\n"
" tool's job to use the expansion so that the files will be in\n"
" the right place.\n"
"\n"
" default_output_extension [string]\n"
" Valid for: linker tools\n"
"\n"
" Extension for the main output of a linkable tool. It includes\n"
" the leading dot. This will be the default value for the\n"
" {{output_extension}} expansion (discussed below) but will be\n"
" overridden by by the \"output extension\" variable in a target,\n"
" if one is specified. Empty string means no extension.\n"
"\n"
" GN doesn't actually do anything with this extension other than\n"
" pass it along, potentially with target-specific overrides. One\n"
" would typically use the {{output_extension}} value in the\n"
" \"outputs\" to read this value.\n"
"\n"
" Example: default_output_extension = \".exe\"\n"
"\n"
" depfile [string with substitutions]\n"
" Valid for: compiler tools (optional)\n"
"\n"
" If the tool can write \".d\" files, this specifies the name of\n"
" the resulting file. These files are used to list header file\n"
" dependencies (or other implicit input dependencies) that are\n"
" discovered at build time. See also \"depsformat\".\n"
"\n"
" Example: depfile = \"{{output}}.d\"\n"
"\n"
" depsformat [string]\n"
" Valid for: compiler tools (when depfile is specified)\n"
"\n"
" Format for the deps outputs. This is either \"gcc\" or \"msvc\".\n"
" See the ninja documentation for \"deps\" for more information.\n"
"\n"
" Example: depsformat = \"gcc\"\n"
"\n"
" description [string with substitutions, optional]\n"
" Valid for: all tools\n"
"\n"
" What to print when the command is run.\n"
"\n"
" Example: description = \"Compiling {{source}}\"\n"
"\n"
" lib_switch [string, optional, link tools only]\n"
" lib_dir_switch [string, optional, link tools only]\n"
" Valid for: Linker tools except \"alink\"\n"
"\n"
" These strings will be prepended to the libraries and library\n"
" search directories, respectively, because linkers differ on how\n"
" specify them. If you specified:\n"
" lib_switch = \"-l\"\n"
" lib_dir_switch = \"-L\"\n"
" then the \"{{libs}}\" expansion for [ \"freetype\", \"expat\"]\n"
" would be \"-lfreetype -lexpat\".\n"
"\n"
" outputs [list of strings with substitutions]\n"
" Valid for: Linker and compiler tools (required)\n"
"\n"
" An array of names for the output files the tool produces. These\n"
" are relative to the build output directory. There must always be\n"
" at least one output file. There can be more than one output (a\n"
" linker might produce a library and an import library, for\n"
" example).\n"
"\n"
" This array just declares to GN what files the tool will\n"
" produce. It is your responsibility to specify the tool command\n"
" that actually produces these files.\n"
"\n"
" If you specify more than one output for shared library links,\n"
" you should consider setting link_output, depend_output, and\n"
" runtime_link_output. Otherwise, the first entry in the\n"
" outputs list should always be the main output which will be\n"
" linked to.\n"
"\n"
" Example for a compiler tool that produces .obj files:\n"
" outputs = [\n"
" \"{{source_out_dir}}/{{source_name_part}}.obj\"\n"
" ]\n"
"\n"
" Example for a linker tool that produces a .dll and a .lib. The\n"
" use of {{target_output_name}}, {{output_extension}} and\n"
" {{output_dir}} allows the target to override these values.\n"
" outputs = [\n"
" \"{{output_dir}}/{{target_output_name}}"
"{{output_extension}}\",\n"
" \"{{output_dir}}/{{target_output_name}}.lib\",\n"
" ]\n"
"\n"
" pool [label, optional]\n"
"\n"
" Label of the pool to use for the tool. Pools are used to limit\n"
" the number of tasks that can execute concurrently during the\n"
" build.\n"
"\n"
" See also \"gn help pool\".\n"
"\n"
" link_output [string with substitutions]\n"
" depend_output [string with substitutions]\n"
" runtime_link_output [string with substitutions]\n"
" Valid for: \"solink\" only (optional)\n"
"\n"
" These three files specify which of the outputs from the solink\n"
" tool should be used for linking and dependency tracking. These\n"
" should match entries in the \"outputs\". If unspecified, the\n"
" first item in the \"outputs\" array will be used for all. See\n"
" \"Separate linking and dependencies for shared libraries\"\n"
" below for more. If link_output is set but runtime_link_output\n"
" is not set, runtime_link_output defaults to link_output.\n"
"\n"
" On Windows, where the tools produce a .dll shared library and\n"
" a .lib import library, you will want the first two to be the\n"
" import library and the third one to be the .dll file.\n"
" On Linux, if you're not doing the separate linking/dependency\n"
" optimization, all of these should be the .so output.\n"
"\n"
" output_prefix [string]\n"
" Valid for: Linker tools (optional)\n"
"\n"
" Prefix to use for the output name. Defaults to empty. This\n"
" prefix will be prepended to the name of the target (or the\n"
" output_name if one is manually specified for it) if the prefix\n"
" is not already there. The result will show up in the\n"
" {{output_name}} substitution pattern.\n"
"\n"
" Individual targets can opt-out of the output prefix by setting:\n"
" output_prefix_override = true\n"
" (see \"gn help output_prefix_override\").\n"
"\n"
" This is typically used to prepend \"lib\" to libraries on\n"
" Posix systems:\n"
" output_prefix = \"lib\"\n"
"\n"
" precompiled_header_type [string]\n"
" Valid for: \"cc\", \"cxx\", \"objc\", \"objcxx\"\n"
"\n"
" Type of precompiled headers. If undefined or the empty string,\n"
" precompiled headers will not be used for this tool. Otherwise\n"
" use \"gcc\" or \"msvc\".\n"
"\n"
" For precompiled headers to be used for a given target, the\n"
" target (or a config applied to it) must also specify a\n"
" \"precompiled_header\" and, for \"msvc\"-style headers, a\n"
" \"precompiled_source\" value. If the type is \"gcc\", then both\n"
" \"precompiled_header\" and \"precompiled_source\" must resolve\n"
" to the same file, despite the different formats required for each."
"\n"
" See \"gn help precompiled_header\" for more.\n"
"\n"
" restat [boolean]\n"
" Valid for: all tools (optional, defaults to false)\n"
"\n"
" Requests that Ninja check the file timestamp after this tool has\n"
" run to determine if anything changed. Set this if your tool has\n"
" the ability to skip writing output if the output file has not\n"
" changed.\n"
"\n"
" Normally, Ninja will assume that when a tool runs the output\n"
" be new and downstream dependents must be rebuild. When this is\n"
" set to trye, Ninja can skip rebuilding downstream dependents for\n"
" input changes that don't actually affect the output.\n"
"\n"
" Example:\n"
" restat = true\n"
"\n"
" rspfile [string with substitutions]\n"
" Valid for: all tools (optional)\n"
"\n"
" Name of the response file. If empty, no response file will be\n"
" used. See \"rspfile_content\".\n"
"\n"
" rspfile_content [string with substitutions]\n"
" Valid for: all tools (required when \"rspfile\" is specified)\n"
"\n"
" The contents to be written to the response file. This may\n"
" include all or part of the command to send to the tool which\n"
" allows you to get around OS command-line length limits.\n"
"\n"
" This example adds the inputs and libraries to a response file,\n"
" but passes the linker flags directly on the command line:\n"
" tool(\"link\") {\n"
" command = \"link -o {{output}} {{ldflags}} @{{output}}.rsp\"\n"
" rspfile = \"{{output}}.rsp\"\n"
" rspfile_content = \"{{inputs}} {{solibs}} {{libs}}\"\n"
" }\n"
"\n"
"Expansions for tool variables\n"
"\n"
" All paths are relative to the root build directory, which is the\n"
" current directory for running all tools. These expansions are\n"
" available to all tools:\n"
"\n"
" {{label}}\n"
" The label of the current target. This is typically used in the\n"
" \"description\" field for link tools. The toolchain will be\n"
" omitted from the label for targets in the default toolchain, and\n"
" will be included for targets in other toolchains.\n"
"\n"
" {{label_name}}\n"
" The short name of the label of the target. This is the part\n"
" after the colon. For \"//foo/bar:baz\" this will be \"baz\".\n"
" Unlike {{target_output_name}}, this is not affected by the\n"
" \"output_prefix\" in the tool or the \"output_name\" set\n"
" on the target.\n"
"\n"
" {{output}}\n"
" The relative path and name of the output(s) of the current\n"
" build step. If there is more than one output, this will expand\n"
" to a list of all of them.\n"
" Example: \"out/base/my_file.o\"\n"
"\n"
" {{target_gen_dir}}\n"
" {{target_out_dir}}\n"
" The directory of the generated file and output directories,\n"
" respectively, for the current target. There is no trailing\n"
" slash. See also {{output_dir}} for linker tools.\n"
" Example: \"out/base/test\"\n"
"\n"
" {{target_output_name}}\n"
" The short name of the current target with no path information,\n"
" or the value of the \"output_name\" variable if one is specified\n"
" in the target. This will include the \"output_prefix\" if any.\n"
" See also {{label_name}}.\n"
" Example: \"libfoo\" for the target named \"foo\" and an\n"
" output prefix for the linker tool of \"lib\".\n"
"\n"
" Compiler tools have the notion of a single input and a single output,\n"
" along with a set of compiler-specific flags. The following expansions\n"
" are available:\n"
"\n"
" {{asmflags}}\n"
" {{cflags}}\n"
" {{cflags_c}}\n"
" {{cflags_cc}}\n"
" {{cflags_objc}}\n"
" {{cflags_objcc}}\n"
" {{defines}}\n"
" {{include_dirs}}\n"
" Strings correspond that to the processed flags/defines/include\n"
" directories specified for the target.\n"
" Example: \"--enable-foo --enable-bar\"\n"
"\n"
" Defines will be prefixed by \"-D\" and include directories will\n"
" be prefixed by \"-I\" (these work with Posix tools as well as\n"
" Microsoft ones).\n"
"\n"
" {{source}}\n"
" The relative path and name of the current input file.\n"
" Example: \"../../base/my_file.cc\"\n"
"\n"
" {{source_file_part}}\n"
" The file part of the source including the extension (with no\n"
" directory information).\n"
" Example: \"foo.cc\"\n"
"\n"
" {{source_name_part}}\n"
" The filename part of the source file with no directory or\n"
" extension.\n"
" Example: \"foo\"\n"
"\n"
" {{source_gen_dir}}\n"
" {{source_out_dir}}\n"
" The directory in the generated file and output directories,\n"
" respectively, for the current input file. If the source file\n"
" is in the same directory as the target is declared in, they will\n"
" will be the same as the \"target\" versions above.\n"
" Example: \"gen/base/test\"\n"
"\n"
" Linker tools have multiple inputs and (potentially) multiple outputs\n"
" The static library tool (\"alink\") is not considered a linker tool.\n"
" The following expansions are available:\n"
"\n"
" {{inputs}}\n"
" {{inputs_newline}}\n"
" Expands to the inputs to the link step. This will be a list of\n"
" object files and static libraries.\n"
" Example: \"obj/foo.o obj/bar.o obj/somelibrary.a\"\n"
"\n"
" The \"_newline\" version will separate the input files with\n"
" newlines instead of spaces. This is useful in response files:\n"
" some linkers can take a \"-filelist\" flag which expects newline\n"
" separated files, and some Microsoft tools have a fixed-sized\n"
" buffer for parsing each line of a response file.\n"
"\n"
" {{ldflags}}\n"
" Expands to the processed set of ldflags and library search paths\n"
" specified for the target.\n"
" Example: \"-m64 -fPIC -pthread -L/usr/local/mylib\"\n"
"\n"
" {{libs}}\n"
" Expands to the list of system libraries to link to. Each will\n"
" be prefixed by the \"lib_prefix\".\n"
"\n"
" As a special case to support Mac, libraries with names ending in\n"
" \".framework\" will be added to the {{libs}} with \"-framework\"\n"
" preceeding it, and the lib prefix will be ignored.\n"
"\n"
" Example: \"-lfoo -lbar\"\n"
"\n"
" {{output_dir}}\n"
" The value of the \"output_dir\" variable in the target, or the\n"
" the value of the \"default_output_dir\" value in the tool if the\n"
" target does not override the output directory. This will be\n"
" relative to the root_build_dir and will not end in a slash.\n"
" Will be \".\" for output to the root_build_dir.\n"
"\n"
" This is subtly different than {{target_out_dir}} which is\n"
" defined by GN based on the target's path and not overridable.\n"
" {{output_dir}} is for the final output, {{target_out_dir}} is\n"
" generally for object files and other outputs.\n"
"\n"
" Usually {{output_dir}} would be defined in terms of either\n"
" {{target_out_dir}} or {{root_out_dir}}\n"
"\n"
" {{output_extension}}\n"
" The value of the \"output_extension\" variable in the target,\n"
" or the value of the \"default_output_extension\" value in the\n"
" tool if the target does not specify an output extension.\n"
" Example: \".so\"\n"
"\n"
" {{solibs}}\n"
" Extra libraries from shared library dependencide not specified\n"
" in the {{inputs}}. This is the list of link_output files from\n"
" shared libraries (if the solink tool specifies a \"link_output\"\n"
" variable separate from the \"depend_output\").\n"
"\n"
" These should generally be treated the same as libs by your tool.\n"
" Example: \"libfoo.so libbar.so\"\n"
"\n"
" The static library (\"alink\") tool allows {{arflags}} plus the common\n"
" tool substitutions.\n"
"\n"
" The copy tool allows the common compiler/linker substitutions, plus\n"
" {{source}} which is the source of the copy. The stamp tool allows\n"
" only the common tool substitutions.\n"
"\n"
" The copy_bundle_data and compile_xcassets tools only allows the common\n"
" tool substitutions. Both tools are required to create iOS/OS X bundles\n"
" and need only be defined on those platforms.\n"
"\n"
" The copy_bundle_data tool will be called with one source and needs to\n"
" copy (optionally optimizing the data representation) to its output. It\n"
" may be called with a directory as input and it needs to be recursively\n"
" copied.\n"
"\n"
" The compile_xcassets tool will be called with one or more source (each\n"
" an asset catalog) that needs to be compiled to a single output.\n"
"\n"
"Separate linking and dependencies for shared libraries\n"
"\n"
" Shared libraries are special in that not all changes to them require\n"
" that dependent targets be re-linked. If the shared library is changed\n"
" but no imports or exports are different, dependent code needn't be\n"
" relinked, which can speed up the build.\n"
"\n"
" If your link step can output a list of exports from a shared library\n"
" and writes the file only if the new one is different, the timestamp of\n"
" this file can be used for triggering re-links, while the actual shared\n"
" library would be used for linking.\n"
"\n"
" You will need to specify\n"
" restat = true\n"
" in the linker tool to make this work, so Ninja will detect if the\n"
" timestamp of the dependency file has changed after linking (otherwise\n"
" it will always assume that running a command updates the output):\n"
"\n"
" tool(\"solink\") {\n"
" command = \"...\"\n"
" outputs = [\n"
" \"{{output_dir}}/{{target_output_name}}{{output_extension}}\",\n"
" \"{{output_dir}}/{{target_output_name}}"
"{{output_extension}}.TOC\",\n"
" ]\n"
" link_output =\n"
" \"{{output_dir}}/{{target_output_name}}{{output_extension}}\"\n"
" depend_output =\n"
" \"{{output_dir}}/{{target_output_name}}"
"{{output_extension}}.TOC\"\n"
" restat = true\n"
" }\n"
"\n"
"Example\n"
"\n"
" toolchain(\"my_toolchain\") {\n"
" # Put these at the top to apply to all tools below.\n"
" lib_prefix = \"-l\"\n"
" lib_dir_prefix = \"-L\"\n"
"\n"
" tool(\"cc\") {\n"
" command = \"gcc {{source}} -o {{output}}\"\n"
" outputs = [ \"{{source_out_dir}}/{{source_name_part}}.o\" ]\n"
" description = \"GCC {{source}}\"\n"
" }\n"
" tool(\"cxx\") {\n"
" command = \"g++ {{source}} -o {{output}}\"\n"
" outputs = [ \"{{source_out_dir}}/{{source_name_part}}.o\" ]\n"
" description = \"G++ {{source}}\"\n"
" }\n"
" }\n";
Value RunTool(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
// Find the toolchain definition we're executing inside of. The toolchain
// function will set a property pointing to it that we'll pick up.
Toolchain* toolchain = reinterpret_cast<Toolchain*>(
scope->GetProperty(&kToolchainPropertyKey, nullptr));
if (!toolchain) {
*err = Err(function->function(), "tool() called outside of toolchain().",
"The tool() function can only be used inside a toolchain() "
"definition.");
return Value();
}
if (!EnsureSingleStringArg(function, args, err))
return Value();
const std::string& tool_name = args[0].string_value();
Toolchain::ToolType tool_type = Toolchain::ToolNameToType(tool_name);
if (tool_type == Toolchain::TYPE_NONE) {
*err = Err(args[0], "Unknown tool type");
return Value();
}
// Run the tool block.
Scope block_scope(scope);
block->Execute(&block_scope, err);
if (err->has_error())
return Value();
// Figure out which validator to use for the substitution pattern for this
// tool type. There are different validators for the "outputs" than for the
// rest of the strings.
bool (*subst_validator)(SubstitutionType) = nullptr;
bool (*subst_output_validator)(SubstitutionType) = nullptr;
if (IsCompilerTool(tool_type)) {
subst_validator = &IsValidCompilerSubstitution;
subst_output_validator = &IsValidCompilerOutputsSubstitution;
} else if (IsLinkerTool(tool_type)) {
subst_validator = &IsValidLinkerSubstitution;
subst_output_validator = &IsValidLinkerOutputsSubstitution;
} else if (tool_type == Toolchain::TYPE_ALINK) {
subst_validator = &IsValidALinkSubstitution;
// ALink uses the standard output file patterns as other linker tools.
subst_output_validator = &IsValidLinkerOutputsSubstitution;
} else if (tool_type == Toolchain::TYPE_COPY ||
tool_type == Toolchain::TYPE_COPY_BUNDLE_DATA) {
subst_validator = &IsValidCopySubstitution;
subst_output_validator = &IsValidCopySubstitution;
} else if (tool_type == Toolchain::TYPE_COMPILE_XCASSETS) {
subst_validator = &IsValidCompileXCassetsSubstitution;
subst_output_validator = &IsValidCompileXCassetsSubstitution;
} else {
subst_validator = &IsValidToolSubstitution;
subst_output_validator = &IsValidToolSubstitution;
}
std::unique_ptr<Tool> tool(new Tool);
if (!ReadPattern(&block_scope, "command", subst_validator, tool.get(),
&Tool::set_command, err) ||
!ReadOutputExtension(&block_scope, tool.get(), err) ||
!ReadPattern(&block_scope, "depfile", subst_validator, tool.get(),
&Tool::set_depfile, err) ||
!ReadDepsFormat(&block_scope, tool.get(), err) ||
!ReadPattern(&block_scope, "description", subst_validator, tool.get(),
&Tool::set_description, err) ||
!ReadString(&block_scope, "lib_switch", tool.get(), &Tool::set_lib_switch,
err) ||
!ReadString(&block_scope, "lib_dir_switch", tool.get(),
&Tool::set_lib_dir_switch, err) ||
!ReadPattern(&block_scope, "link_output", subst_validator, tool.get(),
&Tool::set_link_output, err) ||
!ReadPattern(&block_scope, "depend_output", subst_validator, tool.get(),
&Tool::set_depend_output, err) ||
!ReadPattern(&block_scope, "runtime_link_output", subst_validator,
tool.get(), &Tool::set_runtime_link_output, err) ||
!ReadString(&block_scope, "output_prefix", tool.get(),
&Tool::set_output_prefix, err) ||
!ReadPattern(&block_scope, "default_output_dir", subst_validator,
tool.get(), &Tool::set_default_output_dir, err) ||
!ReadPrecompiledHeaderType(&block_scope, tool.get(), err) ||
!ReadBool(&block_scope, "restat", tool.get(), &Tool::set_restat, err) ||
!ReadPattern(&block_scope, "rspfile", subst_validator, tool.get(),
&Tool::set_rspfile, err) ||
!ReadPattern(&block_scope, "rspfile_content", subst_validator, tool.get(),
&Tool::set_rspfile_content, err) ||
!ReadLabel(&block_scope, "pool", tool.get(), function, toolchain->label(),
&Tool::set_pool, err)) {
return Value();
}
if (tool_type != Toolchain::TYPE_COPY &&
tool_type != Toolchain::TYPE_STAMP &&
tool_type != Toolchain::TYPE_COPY_BUNDLE_DATA &&
tool_type != Toolchain::TYPE_COMPILE_XCASSETS) {
// All tools should have outputs, except the copy, stamp, copy_bundle_data
// and compile_xcassets tools that generate their outputs internally.
if (!ReadOutputs(&block_scope, function, subst_output_validator,
tool.get(), err))
return Value();
}
// Validate that the link_output, depend_output, and runtime_link_output
// refer to items in the outputs and aren't defined for irrelevant tool
// types.
if (!tool->link_output().empty()) {
if (tool_type != Toolchain::TYPE_SOLINK &&
tool_type != Toolchain::TYPE_SOLINK_MODULE) {
*err = Err(function, "This tool specifies a link_output.",
"This is only valid for solink and solink_module tools.");
return Value();
}
if (!IsPatternInOutputList(tool->outputs(), tool->link_output())) {
*err = Err(function, "This tool's link_output is bad.",
"It must match one of the outputs.");
return Value();
}
}
if (!tool->depend_output().empty()) {
if (tool_type != Toolchain::TYPE_SOLINK &&
tool_type != Toolchain::TYPE_SOLINK_MODULE) {
*err = Err(function, "This tool specifies a depend_output.",
"This is only valid for solink and solink_module tools.");
return Value();
}
if (!IsPatternInOutputList(tool->outputs(), tool->depend_output())) {
*err = Err(function, "This tool's depend_output is bad.",
"It must match one of the outputs.");
return Value();
}
}
if ((!tool->link_output().empty() && tool->depend_output().empty()) ||
(tool->link_output().empty() && !tool->depend_output().empty())) {
*err = Err(function, "Both link_output and depend_output should either "
"be specified or they should both be empty.");
return Value();
}
if (!tool->runtime_link_output().empty()) {
if (tool_type != Toolchain::TYPE_SOLINK &&
tool_type != Toolchain::TYPE_SOLINK_MODULE) {
*err = Err(function, "This tool specifies a runtime_link_output.",
"This is only valid for solink and solink_module tools.");
return Value();
}
if (!IsPatternInOutputList(tool->outputs(), tool->runtime_link_output())) {
*err = Err(function, "This tool's runtime_link_output is bad.",
"It must match one of the outputs.");
return Value();
}
}
// Make sure there weren't any vars set in this tool that were unused.
if (!block_scope.CheckForUnusedVars(err))
return Value();
toolchain->SetTool(tool_type, std::move(tool));
return Value();
}
// toolchain_args --------------------------------------------------------------
extern const char kToolchainArgs[] = "toolchain_args";
extern const char kToolchainArgs_HelpShort[] =
"toolchain_args: Set build arguments for toolchain build setup.";
extern const char kToolchainArgs_Help[] =
"toolchain_args: Set build arguments for toolchain build setup.\n"
"\n"
" Used inside a toolchain definition to pass arguments to an alternate\n"
" toolchain's invocation of the build.\n"
"\n"
" When you specify a target using an alternate toolchain, the master\n"
" build configuration file is re-interpreted in the context of that\n"
" toolchain (see \"gn help toolchain\"). The toolchain_args function\n"
" allows you to control the arguments passed into this alternate\n"
" invocation of the build.\n"
"\n"
" Any default system arguments or arguments passed in on the command-\n"
" line will also be passed to the alternate invocation unless explicitly\n"
" overridden by toolchain_args.\n"
"\n"
" The toolchain_args will be ignored when the toolchain being defined\n"
" is the default. In this case, it's expected you want the default\n"
" argument values.\n"
"\n"
" See also \"gn help buildargs\" for an overview of these arguments.\n"
"\n"
"Example:\n"
" toolchain(\"my_weird_toolchain\") {\n"
" ...\n"
" toolchain_args() {\n"
" # Override the system values for a generic Posix system.\n"
" is_win = false\n"
" is_posix = true\n"
"\n"
" # Pass this new value for specific setup for my toolchain.\n"
" is_my_weird_system = true\n"
" }\n"
" }\n";
Value RunToolchainArgs(Scope* scope,
const FunctionCallNode* function,
const std::vector<Value>& args,
BlockNode* block,
Err* err) {
// Find the toolchain definition we're executing inside of. The toolchain
// function will set a property pointing to it that we'll pick up.
Toolchain* toolchain = reinterpret_cast<Toolchain*>(
scope->GetProperty(&kToolchainPropertyKey, nullptr));
if (!toolchain) {
*err = Err(function->function(),
"toolchain_args() called outside of toolchain().",
"The toolchain_args() function can only be used inside a "
"toolchain() definition.");
return Value();
}
if (!args.empty()) {
*err = Err(function->function(), "This function takes no arguments.");
return Value();
}
// This function makes a new scope with various variable sets on it, which
// we then save on the toolchain to use when re-invoking the build.
Scope block_scope(scope);
block->Execute(&block_scope, err);
if (err->has_error())
return Value();
Scope::KeyValueMap values;
block_scope.GetCurrentScopeValues(&values);
toolchain->args() = values;
return Value();
}
} // namespace functions