mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 08:16:09 +03:00
1098 lines
40 KiB
C++
1098 lines
40 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 <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)(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 Label& current_toolchain,
|
||
|
void (Tool::*set)(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 = tool->defined_from();
|
||
|
|
||
|
(tool->*set)(std::move(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)(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)(std::move(pattern));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ReadPatternList(Scope* scope,
|
||
|
const char* name,
|
||
|
bool (*validate)(SubstitutionType),
|
||
|
Tool* tool,
|
||
|
void (Tool::*set)(SubstitutionList),
|
||
|
Err* err) {
|
||
|
const Value* value = scope->GetValue(name, true);
|
||
|
if (!value)
|
||
|
return true; // Not present is fine.
|
||
|
if (!value->VerifyTypeIs(Value::LIST, err))
|
||
|
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;
|
||
|
|
||
|
(tool->*set)(std::move(list));
|
||
|
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 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool ValidateOutputs(const Tool* tool, Err* err) {
|
||
|
if (tool->outputs().list().empty()) {
|
||
|
*err = Err(tool->defined_from(),
|
||
|
"\"outputs\" must be specified for this tool.");
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Validates either link_output or depend_output. To generalize to either, pass
|
||
|
// the associated pattern, and the variable name that should appear in error
|
||
|
// messages.
|
||
|
bool ValidateLinkAndDependOutput(const Tool* tool,
|
||
|
Toolchain::ToolType tool_type,
|
||
|
const SubstitutionPattern& pattern,
|
||
|
const char* variable_name,
|
||
|
Err* err) {
|
||
|
if (pattern.empty())
|
||
|
return true; // Empty is always OK.
|
||
|
|
||
|
// It should only be specified for certain tool types.
|
||
|
if (tool_type != Toolchain::TYPE_SOLINK &&
|
||
|
tool_type != Toolchain::TYPE_SOLINK_MODULE) {
|
||
|
*err = Err(tool->defined_from(),
|
||
|
"This tool specifies a " + std::string(variable_name) + ".",
|
||
|
"This is only valid for solink and solink_module tools.");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!IsPatternInOutputList(tool->outputs(), pattern)) {
|
||
|
*err = Err(tool->defined_from(), "This tool's link_output is bad.",
|
||
|
"It must match one of the outputs.");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ValidateRuntimeOutputs(const Tool* tool,
|
||
|
Toolchain::ToolType tool_type,
|
||
|
Err* err) {
|
||
|
if (tool->runtime_outputs().list().empty())
|
||
|
return true; // Empty is always OK.
|
||
|
|
||
|
if (!IsLinkerTool(tool_type)) {
|
||
|
*err = Err(tool->defined_from(), "This tool specifies runtime_outputs.",
|
||
|
"This is only valid for linker tools (alink doesn't count).");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (const SubstitutionPattern& pattern : tool->runtime_outputs().list()) {
|
||
|
if (!IsPatternInOutputList(tool->outputs(), pattern)) {
|
||
|
*err = Err(tool->defined_from(), "This tool's runtime_outputs is bad.",
|
||
|
"It must be a subset of the outputs. The bad one is:\n " +
|
||
|
pattern.AsString());
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
// toolchain -------------------------------------------------------------------
|
||
|
|
||
|
const char kToolchain[] = "toolchain";
|
||
|
const char kToolchain_HelpShort[] =
|
||
|
"toolchain: Defines a toolchain.";
|
||
|
const char kToolchain_Help[] =
|
||
|
R"*(toolchain: Defines a toolchain.
|
||
|
|
||
|
A toolchain is a set of commands and build flags used to compile the source
|
||
|
code. The toolchain() function defines these commands.
|
||
|
|
||
|
Toolchain overview
|
||
|
|
||
|
You can have more than one toolchain in use at once in a build and a target
|
||
|
can exist simultaneously in multiple toolchains. A build file is executed
|
||
|
once for each toolchain it is referenced in so the GN code can vary all
|
||
|
parameters of each target (or which targets exist) on a per-toolchain basis.
|
||
|
|
||
|
When you have a simple build with only one toolchain, the build config file
|
||
|
is loaded only once at the beginning of the build. It must call
|
||
|
set_default_toolchain() (see "gn help set_default_toolchain") to tell GN the
|
||
|
label of the toolchain definition to use. The "toolchain_args" section of the
|
||
|
toolchain definition is ignored.
|
||
|
|
||
|
When a target has a dependency on a target using different toolchain (see "gn
|
||
|
help labels" for how to specify this), GN will start a build using that
|
||
|
secondary toolchain to resolve the target. GN will load the build config file
|
||
|
with the build arguements overridden as specified in the toolchain_args.
|
||
|
Because the default toolchain is already known, calls to
|
||
|
set_default_toolchain() are ignored.
|
||
|
|
||
|
To load a file in an alternate toolchain, GN does the following:
|
||
|
|
||
|
1. Loads the file with the toolchain definition in it (as determined by the
|
||
|
toolchain label).
|
||
|
2. Re-runs the master build configuration file, applying the arguments
|
||
|
specified by the toolchain_args section of the toolchain definition.
|
||
|
3. Loads the destination build file in the context of the configuration file
|
||
|
in the previous step.
|
||
|
|
||
|
The toolchain configuration is two-way. In the default toolchain (i.e. the
|
||
|
main build target) the configuration flows from the build config file to the
|
||
|
toolchain. The build config file looks at the state of the build (OS type,
|
||
|
CPU architecture, etc.) and decides which toolchain to use (via
|
||
|
set_default_toolchain()). In secondary toolchains, the configuration flows
|
||
|
from the toolchain to the build config file: the "toolchain_args" in the
|
||
|
toolchain definition specifies the arguments to re-invoke the build.
|
||
|
|
||
|
Functions and variables
|
||
|
|
||
|
tool()
|
||
|
The tool() function call specifies the commands commands to run for a given
|
||
|
step. See "gn help tool".
|
||
|
|
||
|
toolchain_args
|
||
|
Overrides for build arguments to pass to the toolchain when invoking it.
|
||
|
This is a variable of type "scope" where the variable names correspond to
|
||
|
variables in declare_args() blocks.
|
||
|
|
||
|
When you specify a target using an alternate toolchain, the master build
|
||
|
configuration file is re-interpreted in the context of that toolchain.
|
||
|
toolchain_args allows you to control the arguments passed into this
|
||
|
alternate invocation of the build.
|
||
|
|
||
|
Any default system arguments or arguments passed in via "gn args" will also
|
||
|
be passed to the alternate invocation unless explicitly overridden by
|
||
|
toolchain_args.
|
||
|
|
||
|
The toolchain_args will be ignored when the toolchain being defined is the
|
||
|
default. In this case, it's expected you want the default argument values.
|
||
|
|
||
|
See also "gn help buildargs" for an overview of these arguments.
|
||
|
|
||
|
deps
|
||
|
Dependencies of this toolchain. These dependencies will be resolved before
|
||
|
any target in the toolchain is compiled. To avoid circular dependencies
|
||
|
these must be targets defined in another toolchain.
|
||
|
|
||
|
This is expressed as a list of targets, and generally these targets will
|
||
|
always specify a toolchain:
|
||
|
deps = [ "//foo/bar:baz(//build/toolchain:bootstrap)" ]
|
||
|
|
||
|
This concept is somewhat inefficient to express in Ninja (it requires a lot
|
||
|
of duplicate of rules) so should only be used when absolutely necessary.
|
||
|
|
||
|
Example of defining a toolchain
|
||
|
|
||
|
toolchain("32") {
|
||
|
tool("cc") {
|
||
|
command = "gcc {{source}}"
|
||
|
...
|
||
|
}
|
||
|
|
||
|
toolchain_args = {
|
||
|
use_doom_melon = true # Doom melon always required for 32-bit builds.
|
||
|
current_cpu = "x86"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
toolchain("64") {
|
||
|
tool("cc") {
|
||
|
command = "gcc {{source}}"
|
||
|
...
|
||
|
}
|
||
|
|
||
|
toolchain_args = {
|
||
|
# use_doom_melon is not overridden here, it will take the default.
|
||
|
current_cpu = "x64"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Example of cross-toolchain dependencies
|
||
|
|
||
|
If a 64-bit target wants to depend on a 32-bit binary, it would specify a
|
||
|
dependency using data_deps (data deps are like deps that are only needed at
|
||
|
runtime and aren't linked, since you can't link a 32-bit and a 64-bit
|
||
|
library).
|
||
|
|
||
|
executable("my_program") {
|
||
|
...
|
||
|
if (target_cpu == "x64") {
|
||
|
# The 64-bit build needs this 32-bit helper.
|
||
|
data_deps = [ ":helper(//toolchains:32)" ]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (target_cpu == "x86") {
|
||
|
# Our helper library is only compiled in 32-bits.
|
||
|
shared_library("helper") {
|
||
|
...
|
||
|
}
|
||
|
}
|
||
|
)*";
|
||
|
|
||
|
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 toolchain args (if any).
|
||
|
const Value* toolchain_args = block_scope.GetValue("toolchain_args", true);
|
||
|
if (toolchain_args) {
|
||
|
if (!toolchain_args->VerifyTypeIs(Value::SCOPE, err))
|
||
|
return Value();
|
||
|
|
||
|
Scope::KeyValueMap values;
|
||
|
toolchain_args->scope_value()->GetCurrentScopeValues(&values);
|
||
|
toolchain->args() = values;
|
||
|
}
|
||
|
|
||
|
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(std::move(toolchain));
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// tool ------------------------------------------------------------------------
|
||
|
|
||
|
const char kTool[] = "tool";
|
||
|
const char kTool_HelpShort[] =
|
||
|
"tool: Specify arguments to a toolchain tool.";
|
||
|
const char kTool_Help[] =
|
||
|
R"(tool: Specify arguments to a toolchain tool.
|
||
|
|
||
|
Usage
|
||
|
|
||
|
tool(<tool type>) {
|
||
|
<tool variables...>
|
||
|
}
|
||
|
|
||
|
Tool types
|
||
|
|
||
|
Compiler tools:
|
||
|
"cc": C compiler
|
||
|
"cxx": C++ compiler
|
||
|
"objc": Objective C compiler
|
||
|
"objcxx": Objective C++ compiler
|
||
|
"rc": Resource compiler (Windows .rc files)
|
||
|
"asm": Assembler
|
||
|
|
||
|
Linker tools:
|
||
|
"alink": Linker for static libraries (archives)
|
||
|
"solink": Linker for shared libraries
|
||
|
"link": Linker for executables
|
||
|
|
||
|
Other tools:
|
||
|
"stamp": Tool for creating stamp files
|
||
|
"copy": Tool to copy files.
|
||
|
"action": Defaults for actions
|
||
|
|
||
|
Platform specific tools:
|
||
|
"copy_bundle_data": [iOS, macOS] Tool to copy files in a bundle.
|
||
|
"compile_xcassets": [iOS, macOS] Tool to compile asset catalogs.
|
||
|
|
||
|
Tool variables
|
||
|
|
||
|
command [string with substitutions]
|
||
|
Valid for: all tools except "action" (required)
|
||
|
|
||
|
The command to run.
|
||
|
|
||
|
default_output_dir [string with substitutions]
|
||
|
Valid for: linker tools
|
||
|
|
||
|
Default directory name for the output file relative to the
|
||
|
root_build_dir. It can contain other substitution patterns. This will
|
||
|
be the default value for the {{output_dir}} expansion (discussed below)
|
||
|
but will be overridden by the "output_dir" variable in a target, if one
|
||
|
is specified.
|
||
|
|
||
|
GN doesn't do anything with this string other than pass it along,
|
||
|
potentially with target-specific overrides. It is the tool's job to use
|
||
|
the expansion so that the files will be in the right place.
|
||
|
|
||
|
default_output_extension [string]
|
||
|
Valid for: linker tools
|
||
|
|
||
|
Extension for the main output of a linkable tool. It includes the
|
||
|
leading dot. This will be the default value for the
|
||
|
{{output_extension}} expansion (discussed below) but will be overridden
|
||
|
by by the "output extension" variable in a target, if one is specified.
|
||
|
Empty string means no extension.
|
||
|
|
||
|
GN doesn't actually do anything with this extension other than pass it
|
||
|
along, potentially with target-specific overrides. One would typically
|
||
|
use the {{output_extension}} value in the "outputs" to read this value.
|
||
|
|
||
|
Example: default_output_extension = ".exe"
|
||
|
|
||
|
depfile [string with substitutions]
|
||
|
Valid for: compiler tools (optional)
|
||
|
|
||
|
If the tool can write ".d" files, this specifies the name of the
|
||
|
resulting file. These files are used to list header file dependencies
|
||
|
(or other implicit input dependencies) that are discovered at build
|
||
|
time. See also "depsformat".
|
||
|
|
||
|
Example: depfile = "{{output}}.d"
|
||
|
|
||
|
depsformat [string]
|
||
|
Valid for: compiler tools (when depfile is specified)
|
||
|
|
||
|
Format for the deps outputs. This is either "gcc" or "msvc". See the
|
||
|
ninja documentation for "deps" for more information.
|
||
|
|
||
|
Example: depsformat = "gcc"
|
||
|
|
||
|
description [string with substitutions, optional]
|
||
|
Valid for: all tools
|
||
|
|
||
|
What to print when the command is run.
|
||
|
|
||
|
Example: description = "Compiling {{source}}"
|
||
|
|
||
|
lib_switch [string, optional, link tools only]
|
||
|
lib_dir_switch [string, optional, link tools only]
|
||
|
Valid for: Linker tools except "alink"
|
||
|
|
||
|
These strings will be prepended to the libraries and library search
|
||
|
directories, respectively, because linkers differ on how specify them.
|
||
|
If you specified:
|
||
|
lib_switch = "-l"
|
||
|
lib_dir_switch = "-L"
|
||
|
then the "{{libs}}" expansion for [ "freetype", "expat"] would be
|
||
|
"-lfreetype -lexpat".
|
||
|
|
||
|
outputs [list of strings with substitutions]
|
||
|
Valid for: Linker and compiler tools (required)
|
||
|
|
||
|
An array of names for the output files the tool produces. These are
|
||
|
relative to the build output directory. There must always be at least
|
||
|
one output file. There can be more than one output (a linker might
|
||
|
produce a library and an import library, for example).
|
||
|
|
||
|
This array just declares to GN what files the tool will produce. It is
|
||
|
your responsibility to specify the tool command that actually produces
|
||
|
these files.
|
||
|
|
||
|
If you specify more than one output for shared library links, you
|
||
|
should consider setting link_output, depend_output, and
|
||
|
runtime_outputs.
|
||
|
|
||
|
Example for a compiler tool that produces .obj files:
|
||
|
outputs = [
|
||
|
"{{source_out_dir}}/{{source_name_part}}.obj"
|
||
|
]
|
||
|
|
||
|
Example for a linker tool that produces a .dll and a .lib. The use of
|
||
|
{{target_output_name}}, {{output_extension}} and {{output_dir}} allows
|
||
|
the target to override these values.
|
||
|
outputs = [
|
||
|
"{{output_dir}}/{{target_output_name}}"
|
||
|
"{{output_extension}}",
|
||
|
"{{output_dir}}/{{target_output_name}}.lib",
|
||
|
]
|
||
|
|
||
|
pool [label, optional]
|
||
|
Valid for: all tools (optional)
|
||
|
|
||
|
Label of the pool to use for the tool. Pools are used to limit the
|
||
|
number of tasks that can execute concurrently during the build.
|
||
|
|
||
|
See also "gn help pool".
|
||
|
|
||
|
link_output [string with substitutions]
|
||
|
depend_output [string with substitutions]
|
||
|
Valid for: "solink" only (optional)
|
||
|
|
||
|
These two files specify which of the outputs from the solink tool
|
||
|
should be used for linking and dependency tracking. These should match
|
||
|
entries in the "outputs". If unspecified, the first item in the
|
||
|
"outputs" array will be used for all. See "Separate linking and
|
||
|
dependencies for shared libraries" below for more.
|
||
|
|
||
|
On Windows, where the tools produce a .dll shared library and a .lib
|
||
|
import library, you will want the first two to be the import library
|
||
|
and the third one to be the .dll file. On Linux, if you're not doing
|
||
|
the separate linking/dependency optimization, all of these should be
|
||
|
the .so output.
|
||
|
|
||
|
output_prefix [string]
|
||
|
Valid for: Linker tools (optional)
|
||
|
|
||
|
Prefix to use for the output name. Defaults to empty. This prefix will
|
||
|
be prepended to the name of the target (or the output_name if one is
|
||
|
manually specified for it) if the prefix is not already there. The
|
||
|
result will show up in the {{output_name}} substitution pattern.
|
||
|
|
||
|
Individual targets can opt-out of the output prefix by setting:
|
||
|
output_prefix_override = true
|
||
|
(see "gn help output_prefix_override").
|
||
|
|
||
|
This is typically used to prepend "lib" to libraries on
|
||
|
Posix systems:
|
||
|
output_prefix = "lib"
|
||
|
|
||
|
precompiled_header_type [string]
|
||
|
Valid for: "cc", "cxx", "objc", "objcxx"
|
||
|
|
||
|
Type of precompiled headers. If undefined or the empty string,
|
||
|
precompiled headers will not be used for this tool. Otherwise use "gcc"
|
||
|
or "msvc".
|
||
|
|
||
|
For precompiled headers to be used for a given target, the target (or a
|
||
|
config applied to it) must also specify a "precompiled_header" and, for
|
||
|
"msvc"-style headers, a "precompiled_source" value. If the type is
|
||
|
"gcc", then both "precompiled_header" and "precompiled_source" must
|
||
|
resolve to the same file, despite the different formats required for
|
||
|
each."
|
||
|
|
||
|
See "gn help precompiled_header" for more.
|
||
|
|
||
|
restat [boolean]
|
||
|
Valid for: all tools (optional, defaults to false)
|
||
|
|
||
|
Requests that Ninja check the file timestamp after this tool has run to
|
||
|
determine if anything changed. Set this if your tool has the ability to
|
||
|
skip writing output if the output file has not changed.
|
||
|
|
||
|
Normally, Ninja will assume that when a tool runs the output be new and
|
||
|
downstream dependents must be rebuild. When this is set to trye, Ninja
|
||
|
can skip rebuilding downstream dependents for input changes that don't
|
||
|
actually affect the output.
|
||
|
|
||
|
Example:
|
||
|
restat = true
|
||
|
|
||
|
rspfile [string with substitutions]
|
||
|
Valid for: all tools except "action" (optional)
|
||
|
|
||
|
Name of the response file. If empty, no response file will be
|
||
|
used. See "rspfile_content".
|
||
|
|
||
|
rspfile_content [string with substitutions]
|
||
|
Valid for: all tools except "action" (required when "rspfile" is used)
|
||
|
|
||
|
The contents to be written to the response file. This may include all
|
||
|
or part of the command to send to the tool which allows you to get
|
||
|
around OS command-line length limits.
|
||
|
|
||
|
This example adds the inputs and libraries to a response file, but
|
||
|
passes the linker flags directly on the command line:
|
||
|
tool("link") {
|
||
|
command = "link -o {{output}} {{ldflags}} @{{output}}.rsp"
|
||
|
rspfile = "{{output}}.rsp"
|
||
|
rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
|
||
|
}
|
||
|
|
||
|
runtime_outputs [string list with substitutions]
|
||
|
Valid for: linker tools
|
||
|
|
||
|
If specified, this list is the subset of the outputs that should be
|
||
|
added to runtime deps (see "gn help runtime_deps"). By default (if
|
||
|
runtime_outputs is empty or unspecified), it will be the link_output.
|
||
|
|
||
|
Expansions for tool variables
|
||
|
|
||
|
All paths are relative to the root build directory, which is the current
|
||
|
directory for running all tools. These expansions are available to all tools:
|
||
|
|
||
|
{{label}}
|
||
|
The label of the current target. This is typically used in the
|
||
|
"description" field for link tools. The toolchain will be omitted from
|
||
|
the label for targets in the default toolchain, and will be included
|
||
|
for targets in other toolchains.
|
||
|
|
||
|
{{label_name}}
|
||
|
The short name of the label of the target. This is the part after the
|
||
|
colon. For "//foo/bar:baz" this will be "baz". Unlike
|
||
|
{{target_output_name}}, this is not affected by the "output_prefix" in
|
||
|
the tool or the "output_name" set on the target.
|
||
|
|
||
|
{{output}}
|
||
|
The relative path and name of the output(s) of the current build step.
|
||
|
If there is more than one output, this will expand to a list of all of
|
||
|
them. Example: "out/base/my_file.o"
|
||
|
|
||
|
{{target_gen_dir}}
|
||
|
{{target_out_dir}}
|
||
|
The directory of the generated file and output directories,
|
||
|
respectively, for the current target. There is no trailing slash. See
|
||
|
also {{output_dir}} for linker tools. Example: "out/base/test"
|
||
|
|
||
|
{{target_output_name}}
|
||
|
The short name of the current target with no path information, or the
|
||
|
value of the "output_name" variable if one is specified in the target.
|
||
|
This will include the "output_prefix" if any. See also {{label_name}}.
|
||
|
|
||
|
Example: "libfoo" for the target named "foo" and an output prefix for
|
||
|
the linker tool of "lib".
|
||
|
|
||
|
)" // String break to prevent overflowing the 16K max VC string length.
|
||
|
R"( Compiler tools have the notion of a single input and a single output, along
|
||
|
with a set of compiler-specific flags. The following expansions are
|
||
|
available:
|
||
|
|
||
|
{{asmflags}}
|
||
|
{{cflags}}
|
||
|
{{cflags_c}}
|
||
|
{{cflags_cc}}
|
||
|
{{cflags_objc}}
|
||
|
{{cflags_objcc}}
|
||
|
{{defines}}
|
||
|
{{include_dirs}}
|
||
|
Strings correspond that to the processed flags/defines/include
|
||
|
directories specified for the target.
|
||
|
Example: "--enable-foo --enable-bar"
|
||
|
|
||
|
Defines will be prefixed by "-D" and include directories will be
|
||
|
prefixed by "-I" (these work with Posix tools as well as Microsoft
|
||
|
ones).
|
||
|
|
||
|
{{source}}
|
||
|
The relative path and name of the current input file.
|
||
|
Example: "../../base/my_file.cc"
|
||
|
|
||
|
{{source_file_part}}
|
||
|
The file part of the source including the extension (with no directory
|
||
|
information).
|
||
|
Example: "foo.cc"
|
||
|
|
||
|
{{source_name_part}}
|
||
|
The filename part of the source file with no directory or extension.
|
||
|
Example: "foo"
|
||
|
|
||
|
{{source_gen_dir}}
|
||
|
{{source_out_dir}}
|
||
|
The directory in the generated file and output directories,
|
||
|
respectively, for the current input file. If the source file is in the
|
||
|
same directory as the target is declared in, they will will be the same
|
||
|
as the "target" versions above. Example: "gen/base/test"
|
||
|
|
||
|
Linker tools have multiple inputs and (potentially) multiple outputs The
|
||
|
static library tool ("alink") is not considered a linker tool. The following
|
||
|
expansions are available:
|
||
|
|
||
|
{{inputs}}
|
||
|
{{inputs_newline}}
|
||
|
Expands to the inputs to the link step. This will be a list of object
|
||
|
files and static libraries.
|
||
|
Example: "obj/foo.o obj/bar.o obj/somelibrary.a"
|
||
|
|
||
|
The "_newline" version will separate the input files with newlines
|
||
|
instead of spaces. This is useful in response files: some linkers can
|
||
|
take a "-filelist" flag which expects newline separated files, and some
|
||
|
Microsoft tools have a fixed-sized buffer for parsing each line of a
|
||
|
response file.
|
||
|
|
||
|
{{ldflags}}
|
||
|
Expands to the processed set of ldflags and library search paths
|
||
|
specified for the target.
|
||
|
Example: "-m64 -fPIC -pthread -L/usr/local/mylib"
|
||
|
|
||
|
{{libs}}
|
||
|
Expands to the list of system libraries to link to. Each will be
|
||
|
prefixed by the "lib_prefix".
|
||
|
|
||
|
As a special case to support Mac, libraries with names ending in
|
||
|
".framework" will be added to the {{libs}} with "-framework" preceeding
|
||
|
it, and the lib prefix will be ignored.
|
||
|
|
||
|
Example: "-lfoo -lbar"
|
||
|
|
||
|
{{output_dir}}
|
||
|
The value of the "output_dir" variable in the target, or the the value
|
||
|
of the "default_output_dir" value in the tool if the target does not
|
||
|
override the output directory. This will be relative to the
|
||
|
root_build_dir and will not end in a slash. Will be "." for output to
|
||
|
the root_build_dir.
|
||
|
|
||
|
This is subtly different than {{target_out_dir}} which is defined by GN
|
||
|
based on the target's path and not overridable. {{output_dir}} is for
|
||
|
the final output, {{target_out_dir}} is generally for object files and
|
||
|
other outputs.
|
||
|
|
||
|
Usually {{output_dir}} would be defined in terms of either
|
||
|
{{target_out_dir}} or {{root_out_dir}}
|
||
|
|
||
|
{{output_extension}}
|
||
|
The value of the "output_extension" variable in the target, or the
|
||
|
value of the "default_output_extension" value in the tool if the target
|
||
|
does not specify an output extension.
|
||
|
Example: ".so"
|
||
|
|
||
|
{{solibs}}
|
||
|
Extra libraries from shared library dependencide not specified in the
|
||
|
{{inputs}}. This is the list of link_output files from shared libraries
|
||
|
(if the solink tool specifies a "link_output" variable separate from
|
||
|
the "depend_output").
|
||
|
|
||
|
These should generally be treated the same as libs by your tool.
|
||
|
|
||
|
Example: "libfoo.so libbar.so"
|
||
|
|
||
|
)" // String break to prevent overflowing the 16K max VC string length.
|
||
|
R"( The static library ("alink") tool allows {{arflags}} plus the common tool
|
||
|
substitutions.
|
||
|
|
||
|
The copy tool allows the common compiler/linker substitutions, plus
|
||
|
{{source}} which is the source of the copy. The stamp tool allows only the
|
||
|
common tool substitutions.
|
||
|
|
||
|
The copy_bundle_data and compile_xcassets tools only allows the common tool
|
||
|
substitutions. Both tools are required to create iOS/macOS bundles and need
|
||
|
only be defined on those platforms.
|
||
|
|
||
|
The copy_bundle_data tool will be called with one source and needs to copy
|
||
|
(optionally optimizing the data representation) to its output. It may be
|
||
|
called with a directory as input and it needs to be recursively copied.
|
||
|
|
||
|
The compile_xcassets tool will be called with one or more source (each an
|
||
|
asset catalog) that needs to be compiled to a single output. The following
|
||
|
substitutions are avaiable:
|
||
|
|
||
|
{{inputs}}
|
||
|
Expands to the list of .xcassets to use as input to compile the asset
|
||
|
catalog.
|
||
|
|
||
|
{{bundle_product_type}}
|
||
|
Expands to the product_type of the bundle that will contain the
|
||
|
compiled asset catalog. Usually corresponds to the product_type
|
||
|
property of the corresponding create_bundle target.
|
||
|
|
||
|
{{bundle_partial_info_plist}}
|
||
|
Expands to the path to the partial Info.plist generated by the
|
||
|
assets catalog compiler. Usually based on the target_name of
|
||
|
the create_bundle target.
|
||
|
|
||
|
Separate linking and dependencies for shared libraries
|
||
|
|
||
|
Shared libraries are special in that not all changes to them require that
|
||
|
dependent targets be re-linked. If the shared library is changed but no
|
||
|
imports or exports are different, dependent code needn't be relinked, which
|
||
|
can speed up the build.
|
||
|
|
||
|
If your link step can output a list of exports from a shared library and
|
||
|
writes the file only if the new one is different, the timestamp of this file
|
||
|
can be used for triggering re-links, while the actual shared library would be
|
||
|
used for linking.
|
||
|
|
||
|
You will need to specify
|
||
|
restat = true
|
||
|
in the linker tool to make this work, so Ninja will detect if the timestamp
|
||
|
of the dependency file has changed after linking (otherwise it will always
|
||
|
assume that running a command updates the output):
|
||
|
|
||
|
tool("solink") {
|
||
|
command = "..."
|
||
|
outputs = [
|
||
|
"{{output_dir}}/{{target_output_name}}{{output_extension}}",
|
||
|
"{{output_dir}}/{{target_output_name}}"
|
||
|
"{{output_extension}}.TOC",
|
||
|
]
|
||
|
link_output =
|
||
|
"{{output_dir}}/{{target_output_name}}{{output_extension}}"
|
||
|
depend_output =
|
||
|
"{{output_dir}}/{{target_output_name}}"
|
||
|
"{{output_extension}}.TOC"
|
||
|
restat = true
|
||
|
}
|
||
|
|
||
|
Example
|
||
|
|
||
|
toolchain("my_toolchain") {
|
||
|
# Put these at the top to apply to all tools below.
|
||
|
lib_prefix = "-l"
|
||
|
lib_dir_prefix = "-L"
|
||
|
|
||
|
tool("cc") {
|
||
|
command = "gcc {{source}} -o {{output}}"
|
||
|
outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
|
||
|
description = "GCC {{source}}"
|
||
|
}
|
||
|
tool("cxx") {
|
||
|
command = "g++ {{source}} -o {{output}}"
|
||
|
outputs = [ "{{source_out_dir}}/{{source_name_part}}.o" ]
|
||
|
description = "G++ {{source}}"
|
||
|
}
|
||
|
};
|
||
|
)";
|
||
|
|
||
|
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);
|
||
|
tool->set_defined_from(function);
|
||
|
|
||
|
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) ||
|
||
|
!ReadPatternList(&block_scope, "runtime_outputs", subst_validator,
|
||
|
tool.get(), &Tool::set_runtime_outputs, 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(), 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 &&
|
||
|
tool_type != Toolchain::TYPE_ACTION) {
|
||
|
// All tools should have outputs, except the copy, stamp, copy_bundle_data
|
||
|
// compile_xcassets and action tools that generate their outputs internally.
|
||
|
if (!ReadPatternList(&block_scope, "outputs", subst_output_validator,
|
||
|
tool.get(), &Tool::set_outputs, err) ||
|
||
|
!ValidateOutputs(tool.get(), err))
|
||
|
return Value();
|
||
|
}
|
||
|
if (!ValidateRuntimeOutputs(tool.get(), tool_type, err))
|
||
|
return Value();
|
||
|
|
||
|
// Validate link_output and depend_output.
|
||
|
if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->link_output(),
|
||
|
"link_output", err))
|
||
|
return Value();
|
||
|
if (!ValidateLinkAndDependOutput(tool.get(), tool_type, tool->depend_output(),
|
||
|
"depend_output", err))
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
// 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();
|
||
|
}
|
||
|
|
||
|
} // namespace functions
|