|  | // 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 "tools/gn/settings.h" | 
|  | #include "tools/gn/source_file.h" | 
|  | #include "tools/gn/string_utils.h" | 
|  | #include "tools/gn/variables.h" | 
|  | #include "util/build_config.h" | 
|  | #include "util/sys_info.h" | 
|  |  | 
|  | const char kBuildArgs_Help[] = | 
|  | R"(Build Arguments Overview | 
|  |  | 
|  | Build arguments are variables passed in from outside of the build that build | 
|  | files can query to determine how the build works. | 
|  |  | 
|  | How build arguments are set | 
|  |  | 
|  | First, system default arguments are set based on the current system. The | 
|  | built-in arguments are: | 
|  | - host_cpu | 
|  | - host_os | 
|  | - current_cpu | 
|  | - current_os | 
|  | - target_cpu | 
|  | - target_os | 
|  |  | 
|  | Next, project-specific overrides are applied. These are specified inside | 
|  | the default_args variable of //.gn. See "gn help dotfile" for more. | 
|  |  | 
|  | If specified, arguments from the --args command line flag are used. If that | 
|  | flag is not specified, args from previous builds in the build directory will | 
|  | be used (this is in the file args.gn in the build directory). | 
|  |  | 
|  | Last, for targets being compiled with a non-default toolchain, the toolchain | 
|  | overrides are applied. These are specified in the toolchain_args section of a | 
|  | toolchain definition. The use-case for this is that a toolchain may be | 
|  | building code for a different platform, and that it may want to always | 
|  | specify Posix, for example. See "gn help toolchain" for more. | 
|  |  | 
|  | If you specify an override for a build argument that never appears in a | 
|  | "declare_args" call, a nonfatal error will be displayed. | 
|  |  | 
|  | Examples | 
|  |  | 
|  | gn args out/FooBar | 
|  | Create the directory out/FooBar and open an editor. You would type | 
|  | something like this into that file: | 
|  | enable_doom_melon=false | 
|  | os="android" | 
|  |  | 
|  | gn gen out/FooBar --args="enable_doom_melon=true os=\"android\"" | 
|  | This will overwrite the build directory with the given arguments. (Note | 
|  | that the quotes inside the args command will usually need to be escaped | 
|  | for your shell to pass through strings values.) | 
|  |  | 
|  | How build arguments are used | 
|  |  | 
|  | If you want to use an argument, you use declare_args() and specify default | 
|  | values. These default values will apply if none of the steps listed in the | 
|  | "How build arguments are set" section above apply to the given argument, but | 
|  | the defaults will not override any of these. | 
|  |  | 
|  | Often, the root build config file will declare global arguments that will be | 
|  | passed to all buildfiles. Individual build files can also specify arguments | 
|  | that apply only to those files. It is also useful to specify build args in an | 
|  | "import"-ed file if you want such arguments to apply to multiple buildfiles. | 
|  | )"; | 
|  |  | 
|  | 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::ValueWithOverride::ValueWithOverride() | 
|  | : default_value(), has_override(false), override_value() {} | 
|  |  | 
|  | Args::ValueWithOverride::ValueWithOverride(const Value& def_val) | 
|  | : default_value(def_val), has_override(false), override_value() {} | 
|  |  | 
|  | Args::ValueWithOverride::~ValueWithOverride() = default; | 
|  |  | 
|  | Args::Args() = default; | 
|  |  | 
|  | Args::Args(const Args& other) | 
|  | : overrides_(other.overrides_), | 
|  | all_overrides_(other.all_overrides_), | 
|  | declared_arguments_per_toolchain_( | 
|  | other.declared_arguments_per_toolchain_), | 
|  | toolchain_overrides_(other.toolchain_overrides_) {} | 
|  |  | 
|  | Args::~Args() = default; | 
|  |  | 
|  | void Args::AddArgOverride(const char* name, const Value& value) { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | overrides_[std::string_view(name)] = value; | 
|  | all_overrides_[std::string_view(name)] = value; | 
|  | } | 
|  |  | 
|  | void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | for (const auto& cur_override : overrides) { | 
|  | overrides_[cur_override.first] = cur_override.second; | 
|  | all_overrides_[cur_override.first] = cur_override.second; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Args::AddDefaultArgOverrides(const Scope::KeyValueMap& overrides) { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  | for (const auto& cur_override : overrides) | 
|  | overrides_[cur_override.first] = cur_override.second; | 
|  | } | 
|  |  | 
|  | const Value* Args::GetArgOverride(const char* name) const { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | Scope::KeyValueMap::const_iterator found = | 
|  | all_overrides_.find(std::string_view(name)); | 
|  | if (found == all_overrides_.end()) | 
|  | return nullptr; | 
|  | return &found->second; | 
|  | } | 
|  |  | 
|  | void Args::SetupRootScope(Scope* dest, | 
|  | const Scope::KeyValueMap& toolchain_overrides) const { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | SetSystemVarsLocked(dest); | 
|  |  | 
|  | // Apply overrides for already declared args. | 
|  | // (i.e. the system vars we set above) | 
|  | ApplyOverridesLocked(overrides_, dest); | 
|  | ApplyOverridesLocked(toolchain_overrides, dest); | 
|  |  | 
|  | OverridesForToolchainLocked(dest) = toolchain_overrides; | 
|  |  | 
|  | SaveOverrideRecordLocked(toolchain_overrides); | 
|  | } | 
|  |  | 
|  | bool Args::DeclareArgs(const Scope::KeyValueMap& args, | 
|  | Scope* scope_to_set, | 
|  | Err* err) const { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | Scope::KeyValueMap& declared_arguments( | 
|  | DeclaredArgumentsForToolchainLocked(scope_to_set)); | 
|  |  | 
|  | const Scope::KeyValueMap& toolchain_overrides( | 
|  | OverridesForToolchainLocked(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); | 
|  | } | 
|  |  | 
|  | // In all the cases below, mark the variable used. If a variable is set | 
|  | // that's only used in one toolchain, we don't want to report unused | 
|  | // variable errors in other toolchains. Also, in some cases it's reasonable | 
|  | // for the build file to overwrite the value with a different value based | 
|  | // on some other condition without dereferencing the value first. | 
|  |  | 
|  | // Check whether this argument has been overridden on the toolchain level | 
|  | // and use the override instead. | 
|  | Scope::KeyValueMap::const_iterator toolchain_override = | 
|  | toolchain_overrides.find(arg.first); | 
|  | if (toolchain_override != toolchain_overrides.end()) { | 
|  | scope_to_set->SetValue(toolchain_override->first, | 
|  | toolchain_override->second, | 
|  | toolchain_override->second.origin()); | 
|  | scope_to_set->MarkUsed(arg.first); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Check whether this argument has been overridden and use the override | 
|  | // instead. | 
|  | Scope::KeyValueMap::const_iterator override = overrides_.find(arg.first); | 
|  | if (override != overrides_.end()) { | 
|  | scope_to_set->SetValue(override->first, override->second, | 
|  | override->second.origin()); | 
|  | scope_to_set->MarkUsed(override->first); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | 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 { | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  | Scope::KeyValueMap unused_overrides(all_overrides_); | 
|  | for (const auto& map_pair : declared_arguments_per_toolchain_) | 
|  | RemoveDeclaredOverrides(map_pair.second, &unused_overrides); | 
|  |  | 
|  | if (unused_overrides.empty()) | 
|  | return true; | 
|  |  | 
|  | // Some assignments in args.gn had no effect.  Show an error for the first | 
|  | // unused assignment. | 
|  | std::string_view name = unused_overrides.begin()->first; | 
|  | const Value& value = unused_overrides.begin()->second; | 
|  |  | 
|  | std::string err_help( | 
|  | "The variable \"" + name + | 
|  | "\" was set as a build argument\n" | 
|  | "but never appeared in a declare_args() block in any buildfile.\n\n" | 
|  | "To view all possible args, run \"gn args --list <out_dir>\""); | 
|  |  | 
|  | // Use all declare_args for a spelling suggestion. | 
|  | std::vector<std::string_view> candidates; | 
|  | for (const auto& map_pair : declared_arguments_per_toolchain_) { | 
|  | for (const auto& declared_arg : map_pair.second) | 
|  | candidates.push_back(declared_arg.first); | 
|  | } | 
|  | std::string_view suggestion = SpellcheckString(name, candidates); | 
|  | if (!suggestion.empty()) | 
|  | err_help = "Did you mean \"" + suggestion + "\"?\n\n" + err_help; | 
|  |  | 
|  | *err = Err(value.origin(), "Build argument has no effect.", err_help); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | Args::ValueWithOverrideMap Args::GetAllArguments() const { | 
|  | ValueWithOverrideMap result; | 
|  |  | 
|  | std::lock_guard<std::mutex> lock(lock_); | 
|  |  | 
|  | // Sort the keys from declared_arguments_per_toolchain_ so | 
|  | // the return value will be deterministic. | 
|  | std::vector<const Settings*> keys; | 
|  | keys.reserve(declared_arguments_per_toolchain_.size()); | 
|  | for (const auto& map_pair : declared_arguments_per_toolchain_) { | 
|  | keys.push_back(map_pair.first); | 
|  | } | 
|  | std::sort(keys.begin(), keys.end(), | 
|  | [](const Settings* a, const Settings* b) -> bool { | 
|  | return a->toolchain_label() < b->toolchain_label(); | 
|  | }); | 
|  |  | 
|  | // Default values. | 
|  | for (const auto& key : keys) { | 
|  | const auto& value = declared_arguments_per_toolchain_[key]; | 
|  | for (const auto& arg : value) | 
|  | result.insert(std::make_pair(arg.first, ValueWithOverride(arg.second))); | 
|  | } | 
|  |  | 
|  | // Merge in overrides. | 
|  | for (const auto& over : overrides_) { | 
|  | auto found = result.find(over.first); | 
|  | if (found != result.end()) { | 
|  | found->second.has_override = true; | 
|  | found->second.override_value = over.second; | 
|  | } | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void Args::SetSystemVarsLocked(Scope* dest) const { | 
|  | // 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_FREEBSD) | 
|  | os = "freebsd"; | 
|  | #elif defined(OS_AIX) | 
|  | os = "aix"; | 
|  | #elif defined(OS_OPENBSD) | 
|  | os = "openbsd"; | 
|  | #else | 
|  | #error Unknown OS type. | 
|  | #endif | 
|  | // NOTE: Adding a new port? Please follow | 
|  | // https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md | 
|  |  | 
|  | // Host architecture. | 
|  | static const char kX86[] = "x86"; | 
|  | static const char kX64[] = "x64"; | 
|  | static const char kArm[] = "arm"; | 
|  | static const char kArm64[] = "arm64"; | 
|  | static const char kMips[] = "mipsel"; | 
|  | static const char kMips64[] = "mips64el"; | 
|  | static const char kS390X[] = "s390x"; | 
|  | static const char kPPC64[] = "ppc64"; | 
|  | 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 = 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 == "aarch64") | 
|  | arch = kArm64; | 
|  | else if (os_arch == "mips") | 
|  | arch = kMips; | 
|  | else if (os_arch == "mips64") | 
|  | arch = kMips64; | 
|  | else if (os_arch == "s390x") | 
|  | arch = kS390X; | 
|  | else if (os_arch == "ppc64" || os_arch == "ppc64le") | 
|  | // We handle the endianness inside //build/config/host_byteorder.gni. | 
|  | // This allows us to use the same toolchain as ppc64 BE | 
|  | // and specific flags are included using the host_byteorder logic. | 
|  | arch = kPPC64; | 
|  | else | 
|  | CHECK(false) << "OS architecture not handled. (" << os_arch << ")"; | 
|  |  | 
|  | // 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 { | 
|  | const Scope::KeyValueMap& declared_arguments( | 
|  | DeclaredArgumentsForToolchainLocked(scope)); | 
|  |  | 
|  | // Only set a value if it has been declared. | 
|  | for (const auto& val : values) { | 
|  | Scope::KeyValueMap::const_iterator declared = | 
|  | declared_arguments.find(val.first); | 
|  |  | 
|  | if (declared == declared_arguments.end()) | 
|  | continue; | 
|  |  | 
|  | scope->SetValue(val.first, val.second, val.second.origin()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const { | 
|  | for (const auto& val : values) | 
|  | all_overrides_[val.first] = val.second; | 
|  | } | 
|  |  | 
|  | Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked( | 
|  | Scope* scope) const { | 
|  | return declared_arguments_per_toolchain_[scope->settings()]; | 
|  | } | 
|  |  | 
|  | Scope::KeyValueMap& Args::OverridesForToolchainLocked(Scope* scope) const { | 
|  | return toolchain_overrides_[scope->settings()]; | 
|  | } |