| // 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; |
| |
| *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\n" |
| "To view possible args, run \"gn args --list <builddir>\""); |
| 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"; |
| static const char kMips[] = "mipsel"; |
| 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(0, 3) == "arm") |
| arch = kArm; |
| else if (os_arch == "mips") |
| arch = kMips; |
| 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()]; |
| } |