blob: 60ee7b2ce33d385f58957c9f01858536a3c4d147 [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 "tools/gn/args.h"
#include "base/sys_info.h"
#include "build/build_config.h"
#include "tools/gn/variables.h"
const char kBuildArgs_Help[] =
"Build Arguments Overview\n"
"\n"
" Build arguments are variables passed in from outside of the build\n"
" that build files can query to determine how the build works.\n"
"\n"
"How build arguments are set\n"
"\n"
" First, system default arguments are set based on the current system.\n"
" The built-in arguments are:\n"
" - host_cpu\n"
" - host_os\n"
" - current_cpu\n"
" - current_os\n"
" - target_cpu\n"
" - target_os\n"
"\n"
" If specified, arguments from the --args command line flag are used. If\n"
" that flag is not specified, args from previous builds in the build\n"
" directory will be used (this is in the file args.gn in the build\n"
" directory).\n"
"\n"
" Last, for targets being compiled with a non-default toolchain, the\n"
" toolchain overrides are applied. These are specified in the\n"
" toolchain_args section of a toolchain definition. The use-case for\n"
" this is that a toolchain may be building code for a different\n"
" platform, and that it may want to always specify Posix, for example.\n"
" See \"gn help toolchain_args\" for more.\n"
"\n"
" If you specify an override for a build argument that never appears in\n"
" a \"declare_args\" call, a nonfatal error will be displayed.\n"
"\n"
"Examples\n"
"\n"
" gn args out/FooBar\n"
" Create the directory out/FooBar and open an editor. You would type\n"
" something like this into that file:\n"
" enable_doom_melon=false\n"
" os=\"android\"\n"
"\n"
" gn gen out/FooBar --args=\"enable_doom_melon=true os=\\\"android\\\"\"\n"
" This will overwrite the build directory with the given arguments.\n"
" (Note that the quotes inside the args command will usually need to\n"
" be escaped for your shell to pass through strings values.)\n"
"\n"
"How build arguments are used\n"
"\n"
" If you want to use an argument, you use declare_args() and specify\n"
" default values. These default values will apply if none of the steps\n"
" listed in the \"How build arguments are set\" section above apply to\n"
" the given argument, but the defaults will not override any of these.\n"
"\n"
" Often, the root build config file will declare global arguments that\n"
" will be passed to all buildfiles. Individual build files can also\n"
" specify arguments that apply only to those files. It is also useful\n"
" to specify build args in an \"import\"-ed file if you want such\n"
" arguments to apply to multiple buildfiles.\n";
namespace {
// Removes all entries in |overrides| that are in |declared_overrides|.
void RemoveDeclaredOverrides(const Scope::KeyValueMap& declared_arguments,
Scope::KeyValueMap* overrides) {
for (Scope::KeyValueMap::iterator override = overrides->begin();
override != overrides->end();) {
if (declared_arguments.find(override->first) == declared_arguments.end())
++override;
else
overrides->erase(override++);
}
}
} // namespace
Args::Args() {
}
Args::Args(const Args& other)
: overrides_(other.overrides_),
all_overrides_(other.all_overrides_),
declared_arguments_per_toolchain_(
other.declared_arguments_per_toolchain_) {
}
Args::~Args() {
}
void Args::AddArgOverride(const char* name, const Value& value) {
base::AutoLock lock(lock_);
overrides_[base::StringPiece(name)] = value;
all_overrides_[base::StringPiece(name)] = value;
}
void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) {
base::AutoLock lock(lock_);
for (const auto& cur_override : overrides) {
overrides_[cur_override.first] = cur_override.second;
all_overrides_[cur_override.first] = cur_override.second;
}
}
const Value* Args::GetArgOverride(const char* name) const {
base::AutoLock lock(lock_);
Scope::KeyValueMap::const_iterator found =
all_overrides_.find(base::StringPiece(name));
if (found == all_overrides_.end())
return nullptr;
return &found->second;
}
Scope::KeyValueMap Args::GetAllOverrides() const {
base::AutoLock lock(lock_);
return all_overrides_;
}
void Args::SetupRootScope(Scope* dest,
const Scope::KeyValueMap& toolchain_overrides) const {
base::AutoLock lock(lock_);
SetSystemVarsLocked(dest);
ApplyOverridesLocked(overrides_, dest);
ApplyOverridesLocked(toolchain_overrides, dest);
SaveOverrideRecordLocked(toolchain_overrides);
}
bool Args::DeclareArgs(const Scope::KeyValueMap& args,
Scope* scope_to_set,
Err* err) const {
base::AutoLock lock(lock_);
Scope::KeyValueMap& declared_arguments(
DeclaredArgumentsForToolchainLocked(scope_to_set));
for (const auto& arg : args) {
// Verify that the value hasn't already been declared. We want each value
// to be declared only once.
//
// The tricky part is that a buildfile can be interpreted multiple times
// when used from different toolchains, so we can't just check that we've
// seen it before. Instead, we check that the location matches.
Scope::KeyValueMap::iterator previously_declared =
declared_arguments.find(arg.first);
if (previously_declared != declared_arguments.end()) {
if (previously_declared->second.origin() != arg.second.origin()) {
// Declaration location mismatch.
*err = Err(arg.second.origin(),
"Duplicate build argument declaration.",
"Here you're declaring an argument that was already declared "
"elsewhere.\nYou can only declare each argument once in the entire "
"build so there is one\ncanonical place for documentation and the "
"default value. Either move this\nargument to the build config "
"file (for visibility everywhere) or to a .gni file\nthat you "
"\"import\" from the files where you need it (preferred).");
err->AppendSubErr(Err(previously_declared->second.origin(),
"Previous declaration.",
"See also \"gn help buildargs\" for more on how "
"build arguments work."));
return false;
}
} else {
declared_arguments.insert(arg);
}
// Only set on the current scope to the new value if it hasn't been already
// set. Mark the variable used so the build script can override it in
// certain cases without getting unused value errors.
if (!scope_to_set->GetValue(arg.first)) {
scope_to_set->SetValue(arg.first, arg.second, arg.second.origin());
scope_to_set->MarkUsed(arg.first);
}
}
return true;
}
bool Args::VerifyAllOverridesUsed(Err* err) const {
base::AutoLock lock(lock_);
Scope::KeyValueMap all_overrides(all_overrides_);
for (const auto& map_pair : declared_arguments_per_toolchain_)
RemoveDeclaredOverrides(map_pair.second, &all_overrides);
if (all_overrides.empty())
return true;
// Get a list of all possible overrides for help with error finding.
//
// It might be nice to do edit distance checks to see if we can find one close
// to what you typed.
std::string all_declared_str;
for (const auto& map_pair : declared_arguments_per_toolchain_) {
for (const auto& cur_str : map_pair.second) {
if (!all_declared_str.empty())
all_declared_str += ", ";
all_declared_str += cur_str.first.as_string();
}
}
*err = Err(
all_overrides.begin()->second.origin(), "Build argument has no effect.",
"The variable \"" + all_overrides.begin()->first.as_string() +
"\" was set as a build argument\nbut never appeared in a " +
"declare_args() block in any buildfile.\n\nPossible arguments: " +
all_declared_str);
return false;
}
void Args::MergeDeclaredArguments(Scope::KeyValueMap* dest) const {
base::AutoLock lock(lock_);
for (const auto& map_pair : declared_arguments_per_toolchain_) {
for (const auto& arg : map_pair.second)
(*dest)[arg.first] = arg.second;
}
}
void Args::SetSystemVarsLocked(Scope* dest) const {
lock_.AssertAcquired();
// Host OS.
const char* os = nullptr;
#if defined(OS_WIN)
os = "win";
#elif defined(OS_MACOSX)
os = "mac";
#elif defined(OS_LINUX)
os = "linux";
#elif defined(OS_ANDROID)
os = "android";
#else
#error Unknown OS type.
#endif
// Host architecture.
static const char kX86[] = "x86";
static const char kX64[] = "x64";
static const char kArm[] = "arm";
const char* arch = nullptr;
// Set the host CPU architecture based on the underlying OS, not
// whatever the current bit-tedness of the GN binary is.
std::string os_arch = base::SysInfo::OperatingSystemArchitecture();
if (os_arch == "x86")
arch = kX86;
else if (os_arch == "x86_64")
arch = kX64;
else if (os_arch.substr(3) == "arm")
arch = kArm;
else
CHECK(false) << "OS architecture not handled.";
// Save the OS and architecture as build arguments that are implicitly
// declared. This is so they can be overridden in a toolchain build args
// override, and so that they will appear in the "gn args" output.
Value empty_string(nullptr, std::string());
Value os_val(nullptr, std::string(os));
dest->SetValue(variables::kHostOs, os_val, nullptr);
dest->SetValue(variables::kTargetOs, empty_string, nullptr);
dest->SetValue(variables::kCurrentOs, empty_string, nullptr);
Value arch_val(nullptr, std::string(arch));
dest->SetValue(variables::kHostCpu, arch_val, nullptr);
dest->SetValue(variables::kTargetCpu, empty_string, nullptr);
dest->SetValue(variables::kCurrentCpu, empty_string, nullptr);
Scope::KeyValueMap& declared_arguments(
DeclaredArgumentsForToolchainLocked(dest));
declared_arguments[variables::kHostOs] = os_val;
declared_arguments[variables::kCurrentOs] = empty_string;
declared_arguments[variables::kTargetOs] = empty_string;
declared_arguments[variables::kHostCpu] = arch_val;
declared_arguments[variables::kCurrentCpu] = empty_string;
declared_arguments[variables::kTargetCpu] = empty_string;
// Mark these variables used so the build config file can override them
// without geting a warning about overwriting an unused variable.
dest->MarkUsed(variables::kHostCpu);
dest->MarkUsed(variables::kCurrentCpu);
dest->MarkUsed(variables::kTargetCpu);
dest->MarkUsed(variables::kHostOs);
dest->MarkUsed(variables::kCurrentOs);
dest->MarkUsed(variables::kTargetOs);
}
void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values,
Scope* scope) const {
lock_.AssertAcquired();
for (const auto& val : values)
scope->SetValue(val.first, val.second, val.second.origin());
}
void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const {
lock_.AssertAcquired();
for (const auto& val : values)
all_overrides_[val.first] = val.second;
}
Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked(
Scope* scope) const {
lock_.AssertAcquired();
return declared_arguments_per_toolchain_[scope->settings()];
}