Allow host_os and host_cpu to be accessed in .gn files This exposes host_os and host_cpu in the dotfile evaluation scope alongside gn_version. This is derived from https://chromium-review.git.corp.google.com/c/v8/v8/+/8285754/comment/69434b1d_b71efa64/ Bug: 551837554 Change-Id: Iaded5fbf5c03b00bfbc3f9915c73b34dc6e42b33 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25700 Reviewed-by: Andrew Grieve <agrieve@google.com> Commit-Queue: David Turner <digit@google.com> Reviewed-by: David Turner <digit@google.com>
diff --git a/docs/reference.md b/docs/reference.md index 719bc5d..41bd09f 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -4821,9 +4821,9 @@ ``` Corresponds to the number printed by `gn --version`. This variable is - only variable available in the dotfile (all the rest are missing - because the dotfile has to be parsed before args.gn or anything else - is processed). + one of the few variables available in the dotfile (along with `host_cpu` + and `host_os`, all the rest are missing because the dotfile has to be + parsed before args.gn or anything else is processed). ``` #### **Example** @@ -4835,7 +4835,7 @@ ``` This is value is exposed so that cross-compile toolchains can access the host - architecture when needed. + architecture when needed. It is also available in the dotfile. The value should generally be considered read-only, but it can be overridden in order to handle unusual cases where there might be multiple plausible @@ -4853,7 +4853,7 @@ ``` This value is exposed so that cross-compiles can access the host build - system's settings. + system's settings. It is also available in the dotfile. This value should generally be treated as read-only. It, however, is not used internally by GN for any purpose. @@ -7517,7 +7517,8 @@ Next, project-specific overrides are applied. These are specified inside the default_args variable of //.gn. See "gn help dotfile" for more. Note - that during processing of the dotfile itself, only `gn_version` is defined. + that during processing of the dotfile itself, only `gn_version`, `host_cpu`, + and `host_os` are defined. 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 @@ -7577,9 +7578,9 @@ gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn - The system variable `gn_version` is available in the dotfile, but none of - the other variables are, because the dotfile is processed before args.gn - or anything else is processed. + The system variables `gn_version`, `host_cpu`, and `host_os` are available + in the dotfile, but none of the other variables are, because the dotfile is + processed before args.gn or anything else is processed. ``` #### **Variables**
diff --git a/src/gn/args.cc b/src/gn/args.cc index a623f86..d78d862 100644 --- a/src/gn/args.cc +++ b/src/gn/args.cc
@@ -31,7 +31,8 @@ Next, project-specific overrides are applied. These are specified inside the default_args variable of //.gn. See "gn help dotfile" for more. Note - that during processing of the dotfile itself, only `gn_version` is defined. + that during processing of the dotfile itself, only `gn_version`, `host_cpu`, + and `host_os` are defined. 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 @@ -335,35 +336,37 @@ return result; } -void Args::SetSystemVarsLocked(Scope* dest) const { - // Host OS. - const char* os = nullptr; +// static +const char* Args::GetHostOs() { #if defined(OS_WIN) || defined(OS_MSYS) - os = "win"; + return "win"; #elif defined(OS_MACOSX) - os = "mac"; + return "mac"; #elif defined(OS_LINUX) - os = "linux"; + return "linux"; #elif defined(OS_FREEBSD) - os = "freebsd"; + return "freebsd"; #elif defined(OS_AIX) - os = "aix"; + return "aix"; #elif defined(OS_OPENBSD) - os = "openbsd"; + return "openbsd"; #elif defined(OS_HAIKU) - os = "haiku"; + return "haiku"; #elif defined(OS_SOLARIS) - os = "solaris"; + return "solaris"; #elif defined(OS_NETBSD) - os = "netbsd"; + return "netbsd"; #elif defined(OS_ZOS) - os = "zos"; + return "zos"; #elif defined(OS_SERENITY) - os = "serenity"; + return "serenity"; #else #error Unknown OS type. #endif +} +// static +const char* Args::GetHostCpu() { // Host architecture. static const char kX86[] = "x86"; static const char kX64[] = "x64"; @@ -378,42 +381,47 @@ static const char kRISCV64[] = "riscv64"; static const char kE2K[] = "e2k"; static const char kLOONG64[] = "loong64"; - 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" || os_arch == "BePC") - arch = kX86; - else if (os_arch == "x86_64") - arch = kX64; - else if (os_arch == "aarch64" || os_arch == "arm64") - arch = kArm64; - else if (os_arch.substr(0, 3) == "arm") - arch = kArm; - 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") + return kX86; + if (os_arch == "x86_64") + return kX64; + if (os_arch == "aarch64" || os_arch == "arm64") + return kArm64; + if (os_arch.substr(0, 3) == "arm") + return kArm; + if (os_arch == "mips") + return kMips; + if (os_arch == "mips64") + return kMips64; + if (os_arch == "s390x") + return kS390X; + 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 if (os_arch == "sparc64") - arch = kSPARC64; - else if (os_arch == "riscv32") - arch = kRISCV32; - else if (os_arch == "riscv64") - arch = kRISCV64; - else if (os_arch == "e2k") - arch = kE2K; - else if (os_arch == "loongarch64") - arch = kLOONG64; - else - CHECK(false) << "OS architecture not handled. (" << os_arch << ")"; + return kPPC64; + if (os_arch == "sparc64") + return kSPARC64; + if (os_arch == "riscv32") + return kRISCV32; + if (os_arch == "riscv64") + return kRISCV64; + if (os_arch == "e2k") + return kE2K; + if (os_arch == "loongarch64") + return kLOONG64; + + CHECK(false) << "OS architecture not handled. (" << os_arch << ")"; + return nullptr; +} + +void Args::SetSystemVarsLocked(Scope* dest) const { + const char* os = GetHostOs(); + const char* arch = GetHostCpu(); // Save the OS and architecture as build arguments that are implicitly // declared. This is so they can be overridden in a toolchain build args
diff --git a/src/gn/args.h b/src/gn/args.h index 0bc1821..07ae332 100644 --- a/src/gn/args.h +++ b/src/gn/args.h
@@ -61,6 +61,12 @@ // has an override, it returns `override_value`. std::optional<Value> GetArgFromAllArguments(const char* name) const; + // Returns the default host_os string based on the current platform. + static const char* GetHostOs(); + + // Returns the default host_cpu string based on the current platform. + static const char* GetHostCpu(); + // Sets up the root scope for a toolchain. This applies the default system // flags and saves the toolchain overrides so they can be applied to // declare_args blocks that appear when loading files in that toolchain.
diff --git a/src/gn/scope_per_file_provider.cc b/src/gn/scope_per_file_provider.cc index fa7a1d0..848c2a1 100644 --- a/src/gn/scope_per_file_provider.cc +++ b/src/gn/scope_per_file_provider.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "gn/args.h" #include "gn/filesystem_utils.h" #include "gn/settings.h" #include "gn/source_file.h" @@ -28,9 +29,17 @@ if (ident == variables::kGnVersion) return GetGnVersion(); - // In the dotfile scope, gn_version is the only thing defined. - if (dotfile_scope_) + // In the dotfile scope, only gn_version, host_cpu, and host_os are defined. + // host_cpu and host_os are only provided here for the dotfile scope because + // in normal scopes they are build arguments that can be overridden (which + // would otherwise be shadowed by programmatic values). + if (dotfile_scope_) { + if (ident == variables::kHostCpu) + return GetHostCpu(); + if (ident == variables::kHostOs) + return GetHostOs(); return nullptr; + } if (ident == variables::kCurrentToolchain) return GetCurrentToolchain(); @@ -82,6 +91,20 @@ return gn_version_.get(); } +const Value* ScopePerFileProvider::GetHostCpu() { + if (!host_cpu_) { + host_cpu_ = std::make_unique<Value>(nullptr, Args::GetHostCpu()); + } + return host_cpu_.get(); +} + +const Value* ScopePerFileProvider::GetHostOs() { + if (!host_os_) { + host_os_ = std::make_unique<Value>(nullptr, Args::GetHostOs()); + } + return host_os_.get(); +} + const Value* ScopePerFileProvider::GetPythonPath() { if (!python_path_) { python_path_ = std::make_unique<Value>(
diff --git a/src/gn/scope_per_file_provider.h b/src/gn/scope_per_file_provider.h index 9f7adb0..4c0711f 100644 --- a/src/gn/scope_per_file_provider.h +++ b/src/gn/scope_per_file_provider.h
@@ -18,7 +18,7 @@ // When allow_target_vars is unset, the target-related values will be // undefined to GN script. When dotfile_scope is set, only the values // safe to reference in a dotfile will be resolved. At the moment that - // is just gn_version. + // is gn_version, host_cpu, and host_os. ScopePerFileProvider(Scope* scope, bool allow_target_vars, bool dotfile_scope = false); @@ -31,6 +31,8 @@ const Value* GetCurrentToolchain(); const Value* GetDefaultToolchain(); const Value* GetGnVersion(); + const Value* GetHostCpu(); + const Value* GetHostOs(); const Value* GetPythonPath(); const Value* GetRootBuildDir(); const Value* GetRootGenDir(); @@ -45,6 +47,8 @@ std::unique_ptr<Value> current_toolchain_; std::unique_ptr<Value> default_toolchain_; std::unique_ptr<Value> gn_version_; + std::unique_ptr<Value> host_cpu_; + std::unique_ptr<Value> host_os_; std::unique_ptr<Value> python_path_; std::unique_ptr<Value> root_build_dir_; std::unique_ptr<Value> root_gen_dir_;
diff --git a/src/gn/scope_per_file_provider_unittest.cc b/src/gn/scope_per_file_provider_unittest.cc index d4a458f..18f610e 100644 --- a/src/gn/scope_per_file_provider_unittest.cc +++ b/src/gn/scope_per_file_provider_unittest.cc
@@ -3,6 +3,7 @@ // found in the LICENSE file. #include "gn/scope_per_file_provider.h" +#include "gn/args.h" #include "gn/build_settings.h" #include "gn/settings.h" #include "gn/test_with_scope.h" @@ -62,6 +63,14 @@ ScopePerFileProvider provider(&scope, false, true); EXPECT_GE(provider.GetProgrammaticValue(variables::kGnVersion)->int_value(), 0); + EXPECT_NE(nullptr, provider.GetProgrammaticValue(variables::kHostCpu)); + EXPECT_NE(nullptr, provider.GetProgrammaticValue(variables::kHostOs)); + EXPECT_EQ( + Args::GetHostCpu(), + provider.GetProgrammaticValue(variables::kHostCpu)->string_value()); + EXPECT_EQ( + Args::GetHostOs(), + provider.GetProgrammaticValue(variables::kHostOs)->string_value()); EXPECT_EQ(nullptr, provider.GetProgrammaticValue(variables::kRootBuildDir)); } }
diff --git a/src/gn/setup.cc b/src/gn/setup.cc index 47b0282..eb946eb 100644 --- a/src/gn/setup.cc +++ b/src/gn/setup.cc
@@ -60,9 +60,9 @@ gn gen out/Debug --root=/home/build --dotfile=/home/my_gn_file.gn - The system variable `gn_version` is available in the dotfile, but none of - the other variables are, because the dotfile is processed before args.gn - or anything else is processed. + The system variables `gn_version`, `host_cpu`, and `host_os` are available + in the dotfile, but none of the other variables are, because the dotfile is + processed before args.gn or anything else is processed. Variables
diff --git a/src/gn/setup_unittest.cc b/src/gn/setup_unittest.cc index 0fe9388..0eee1e7 100644 --- a/src/gn/setup_unittest.cc +++ b/src/gn/setup_unittest.cc
@@ -583,3 +583,36 @@ base::FilePath(FILE_PATH_LITERAL("../../third_party/python/bin/python3")) .NormalizePathSeparatorsTo('/')); } + +TEST_F(SetupTest, HostOsAndHostCpuInDotFile) { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + + const char kDotfileContents[] = R"( +buildconfig = "//BUILDCONFIG.gn" +assert(host_os != "") +assert(host_cpu != "") +if (host_os != "nonexistent_os") { + default_args = { + test_arg = host_os + } +} +)"; + + base::ScopedTempDir in_temp_dir; + ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir()); + base::FilePath in_path = base::MakeAbsoluteFilePath(in_temp_dir.GetPath()); + base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn")); + WriteFile(dot_gn_name, kDotfileContents); + + WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), ""); + cmdline.AppendSwitchPath(switches::kRoot, in_path); + + base::FilePath build_dir = in_path.Append(FILE_PATH_LITERAL("out")) + .Append(FILE_PATH_LITERAL("default")); + + Setup setup; + Err err; + EXPECT_TRUE( + setup.DoSetupWithErr(FilePathToUTF8(build_dir), true, cmdline, &err)); + EXPECT_FALSE(err.has_error()); +}
diff --git a/src/gn/variables.cc b/src/gn/variables.cc index a1ec0ba..3564369 100644 --- a/src/gn/variables.cc +++ b/src/gn/variables.cc
@@ -17,9 +17,9 @@ R"(gn_version: [number] The version of gn. Corresponds to the number printed by `gn --version`. This variable is - only variable available in the dotfile (all the rest are missing - because the dotfile has to be parsed before args.gn or anything else - is processed). + one of the few variables available in the dotfile (along with `host_cpu` + and `host_os`, all the rest are missing because the dotfile has to be + parsed before args.gn or anything else is processed). Example @@ -33,7 +33,7 @@ R"(host_cpu: The processor architecture that GN is running on. This is value is exposed so that cross-compile toolchains can access the host - architecture when needed. + architecture when needed. It is also available in the dotfile. The value should generally be considered read-only, but it can be overridden in order to handle unusual cases where there might be multiple plausible @@ -53,7 +53,7 @@ R"(host_os: [string] The operating system that GN is running on. This value is exposed so that cross-compiles can access the host build - system's settings. + system's settings. It is also available in the dotfile. This value should generally be treated as read-only. It, however, is not used internally by GN for any purpose.