Add new build configuration variable to add an extension to build file names. Using such an extension would result in a name of the form "BUILD.$extension.gn". This is useful notably for code that is expected to get built in two different GN builds. Change-Id: I1a8fce9ded95d1300d0bf2a23e983301acb9f9d7 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/8500 Commit-Queue: Brett Wilson <brettw@chromium.org> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/src/gn/analyzer_unittest.cc b/src/gn/analyzer_unittest.cc index 4242fee..d2e89a9 100644 --- a/src/gn/analyzer_unittest.cc +++ b/src/gn/analyzer_unittest.cc
@@ -35,6 +35,9 @@ const Settings* GetToolchainSettings(const Label& label) const override { return nullptr; } + SourceFile BuildFileForLabel(const Label& label) const override { + return SourceFile(label.dir().value() + "BUILD.gn"); + } private: ~MockLoader() override = default;
diff --git a/src/gn/builder_unittest.cc b/src/gn/builder_unittest.cc index 8b844e3..b0f48f0 100644 --- a/src/gn/builder_unittest.cc +++ b/src/gn/builder_unittest.cc
@@ -27,6 +27,9 @@ const Settings* GetToolchainSettings(const Label& label) const override { return nullptr; } + SourceFile BuildFileForLabel(const Label& label) const override { + return SourceFile(label.dir().value() + "BUILD.gn"); + } bool HasLoadedNone() const { return files_.empty(); }
diff --git a/src/gn/loader.cc b/src/gn/loader.cc index d6cf5fe..547e919 100644 --- a/src/gn/loader.cc +++ b/src/gn/loader.cc
@@ -85,11 +85,6 @@ Load(BuildFileForLabel(label), origin, label.GetToolchainLabel()); } -// static -SourceFile Loader::BuildFileForLabel(const Label& label) { - return SourceFile(label.dir().value() + "BUILD.gn"); -} - // ----------------------------------------------------------------------------- LoaderImpl::LoaderImpl(const BuildSettings* build_settings) @@ -202,6 +197,11 @@ return &found_toolchain->second->settings; } +SourceFile LoaderImpl::BuildFileForLabel(const Label& label) const { + return SourceFile( + label.dir().value() + "BUILD" + build_file_extension_ + ".gn"); +} + void LoaderImpl::ScheduleLoadFile(const Settings* settings, const LocationRange& origin, const SourceFile& file) {
diff --git a/src/gn/loader.h b/src/gn/loader.h index c4a2dfe..8f95bb8 100644 --- a/src/gn/loader.h +++ b/src/gn/loader.h
@@ -50,13 +50,13 @@ // false if we haven't processed this toolchain yet. virtual const Settings* GetToolchainSettings(const Label& label) const = 0; + // Returns the build file that the given label references. + virtual SourceFile BuildFileForLabel(const Label& label) const = 0; + // Helper function that extracts the file and toolchain name from the given // label, and calls Load(). void Load(const Label& label, const LocationRange& origin); - // Returns the build file that the given label references. - static SourceFile BuildFileForLabel(const Label& label); - // When processing the default build config, we want to capture the argument // of set_default_build_config. The implementation of that function uses this // constant as a property key to get the Label* out of the scope where the @@ -87,6 +87,7 @@ void ToolchainLoaded(const Toolchain* toolchain) override; Label GetDefaultToolchain() const override; const Settings* GetToolchainSettings(const Label& label) const override; + SourceFile BuildFileForLabel(const Label& label) const override; // Sets the task runner corresponding to the main thread. By default this // class will use the thread active during construction, but there is not @@ -105,6 +106,12 @@ async_load_file_ = std::move(cb); } + // Sets the additional extension for build files in this build. + // The resulting file name will be "BUILD.<extension>.gn". + void set_build_file_extension(std::string extension) { + build_file_extension_ = "." + extension; + } + const Label& default_toolchain_label() const { return default_toolchain_label_; } @@ -173,6 +180,8 @@ // Records for the build config file loads. using ToolchainRecordMap = std::map<Label, std::unique_ptr<ToolchainRecord>>; ToolchainRecordMap toolchain_records_; + + std::string build_file_extension_; }; #endif // TOOLS_GN_LOADER_H_
diff --git a/src/gn/loader_unittest.cc b/src/gn/loader_unittest.cc index 3896b59..ce14894 100644 --- a/src/gn/loader_unittest.cc +++ b/src/gn/loader_unittest.cc
@@ -335,3 +335,59 @@ EXPECT_FALSE(scheduler().is_failed()); } + +TEST_F(LoaderTest, NonDefaultBuildFileName) { + std::string new_name = "BUILD.more.gn"; + + SourceFile build_config("//build/config/BUILDCONFIG.gn"); + build_settings_.set_build_config_file(build_config); + + scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_)); + loader->set_build_file_extension("more"); + + // The default toolchain needs to be set by the build config file. + mock_ifm_.AddCannedResponse(build_config, + "set_default_toolchain(\"//tc:tc\")"); + + loader->set_async_load_file(mock_ifm_.GetAsyncCallback()); + + // Request the root build file be loaded. This should kick off the default + // build config loading. + SourceFile root_build("//" + new_name); + loader->Load(root_build, LocationRange(), Label()); + EXPECT_TRUE(mock_ifm_.HasOnePending(build_config)); + + // Completing the build config load should kick off the root build file load. + mock_ifm_.IssueAllPending(); + MsgLoop::Current()->RunUntilIdleForTesting(); + EXPECT_TRUE(mock_ifm_.HasOnePending(root_build)); + + // Load the root build file. + mock_ifm_.IssueAllPending(); + MsgLoop::Current()->RunUntilIdleForTesting(); + + // Schedule some other file to load in another toolchain. + Label second_tc(SourceDir("//tc2/"), "tc2"); + SourceFile second_file("//foo/" + new_name); + loader->Load(second_file, LocationRange(), second_tc); + EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/" + new_name))); + + // Running the toolchain file should schedule the build config file to load + // for that toolchain. + mock_ifm_.IssueAllPending(); + MsgLoop::Current()->RunUntilIdleForTesting(); + + // We have to tell it we have a toolchain definition now (normally the + // builder would do this). + const Settings* default_settings = loader->GetToolchainSettings(Label()); + Toolchain second_tc_object(default_settings, second_tc); + loader->ToolchainLoaded(&second_tc_object); + EXPECT_TRUE(mock_ifm_.HasOnePending(build_config)); + + // Running the build config file should make our second file pending. + mock_ifm_.IssueAllPending(); + MsgLoop::Current()->RunUntilIdleForTesting(); + EXPECT_TRUE(mock_ifm_.HasOnePending(second_file)); + + EXPECT_FALSE(scheduler().is_failed()); +}
diff --git a/src/gn/qt_creator_writer.cc b/src/gn/qt_creator_writer.cc index 5300821..f5c997a 100644 --- a/src/gn/qt_creator_writer.cc +++ b/src/gn/qt_creator_writer.cc
@@ -222,7 +222,7 @@ void QtCreatorWriter::HandleTarget(const Target* target) { using namespace QtCreatorWriterUtils; - SourceFile build_file = Loader::BuildFileForLabel(target->label()); + SourceFile build_file = builder_.loader()->BuildFileForLabel(target->label()); sources_.insert(FilePathToUTF8(build_settings_->GetFullPath(build_file))); AddToSources(target->settings()->import_manager().GetImportedFiles());
diff --git a/src/gn/setup.cc b/src/gn/setup.cc index 75638cf..4026d1a 100644 --- a/src/gn/setup.cc +++ b/src/gn/setup.cc
@@ -107,7 +107,8 @@ root [optional] Label of the root build target. The GN build will start by loading the build file containing this target name. This defaults to "//:" which will - cause the file //BUILD.gn to be loaded. + cause the file //BUILD.gn to be loaded. Note that build_file_extension + applies to the default case as well. script_executable [optional] Path to specific Python executable or other interpreter to use in @@ -138,6 +139,13 @@ This is intended to be used when subprojects declare arguments with default values that need to be changed for whatever reason. + build_file_extension [optional] + If set to a non-empty string, this is added to the name of all build files + to load. + GN will look for build files named "BUILD.$build_file_extension.gn". + This is intended to be used during migrations or other situations where + there are two independent GN builds in the same directories. + Example .gn file contents buildconfig = "//build/config/BUILDCONFIG.gn" @@ -313,7 +321,6 @@ : build_settings_(), loader_(new LoaderImpl(&build_settings_)), builder_(loader_.get()), - root_build_file_("//BUILD.gn"), dotfile_settings_(&build_settings_, std::string()), dotfile_scope_(&dotfile_settings_) { dotfile_settings_.set_toolchain_label(Label()); @@ -782,6 +789,26 @@ SourceDir(secondary_value->string_value())); } + // Build file names. + const Value* build_file_extension_value = + dotfile_scope_.GetValue("build_file_extension", true); + if (build_file_extension_value) { + if (!build_file_extension_value->VerifyTypeIs(Value::STRING, &err)) { + err.PrintToStdout(); + return false; + } + + std::string extension = build_file_extension_value->string_value(); + auto normalized_extension = UTF8ToFilePath(extension).value(); + if (normalized_extension.find_first_of(base::FilePath::kSeparators) != + base::FilePath::StringType::npos) { + Err(Location(), "Build file extension '" + extension + "' cannot " + + "contain a path separator").PrintToStdout(); + return false; + } + loader_->set_build_file_extension(extension); + } + // Root build file. const Value* root_value = dotfile_scope_.GetValue("root", true); if (root_value) { @@ -796,9 +823,10 @@ err.PrintToStdout(); return false; } - - root_build_file_ = Loader::BuildFileForLabel(root_target_label); } + // Set the root build file here in order to take into account the values of + // "build_file_extension" and "root". + root_build_file_ = loader_->BuildFileForLabel(root_target_label); build_settings_.SetRootTargetLabel(root_target_label); // Build config file.
diff --git a/src/gn/setup_unittest.cc b/src/gn/setup_unittest.cc index 723306e..eda66ab 100644 --- a/src/gn/setup_unittest.cc +++ b/src/gn/setup_unittest.cc
@@ -5,6 +5,7 @@ #include "gn/setup.h" #include "base/command_line.h" +#include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "gn/filesystem_utils.h" @@ -43,3 +44,37 @@ ASSERT_EQ(1u, gen_deps.size()); EXPECT_EQ(gen_deps[0], base::MakeAbsoluteFilePath(dot_gn_name)); } + +static void RunExtensionCheckTest(std::string extension, bool success) { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + + // Create a temp directory containing a .gn file and a BUILDCONFIG.gn file, + // pass it as --root. + base::ScopedTempDir in_temp_dir; + ASSERT_TRUE(in_temp_dir.CreateUniqueTempDir()); + base::FilePath in_path = in_temp_dir.GetPath(); + base::FilePath dot_gn_name = in_path.Append(FILE_PATH_LITERAL(".gn")); + WriteFile(dot_gn_name, "buildconfig = \"//BUILDCONFIG.gn\"\n\ + build_file_extension = \"" + extension + "\""); + WriteFile(in_path.Append(FILE_PATH_LITERAL("BUILDCONFIG.gn")), ""); + cmdline.AppendSwitchASCII(switches::kRoot, FilePathToUTF8(in_path)); + + // Create another temp dir for writing the generated files to. + base::ScopedTempDir build_temp_dir; + ASSERT_TRUE(build_temp_dir.CreateUniqueTempDir()); + + // Run setup and check that its status. + Setup setup; + EXPECT_EQ(success, + setup.DoSetup(FilePathToUTF8(build_temp_dir.GetPath()), true, cmdline)); +} + +TEST_F(SetupTest, NoSeparatorInExtension) { + RunExtensionCheckTest( + "hello" + std::string(1, base::FilePath::kSeparators[0]) + "world", + false); +} + +TEST_F(SetupTest, Extension) { + RunExtensionCheckTest("yay", true); +}
diff --git a/src/gn/target.cc b/src/gn/target.cc index 1b26c06..97e3d37 100644 --- a/src/gn/target.cc +++ b/src/gn/target.cc
@@ -223,6 +223,9 @@ 6. When all targets are resolved, write out the root build.ninja file. + Note that the BUILD.gn file name may be modulated by .gn arguments such as + build_file_extension. + Executing target definitions and templates Build files are loaded in parallel. This means it is impossible to
diff --git a/src/gn/xcode_writer.cc b/src/gn/xcode_writer.cc index 2593514..34a3170 100644 --- a/src/gn/xcode_writer.cc +++ b/src/gn/xcode_writer.cc
@@ -528,7 +528,7 @@ if (!item->AsConfig() && !item->AsTarget() && !item->AsToolchain()) continue; - const SourceFile build = Loader::BuildFileForLabel(item->label()); + const SourceFile build = builder.loader()->BuildFileForLabel(item->label()); if (ShouldIncludeFileInProject(build)) sources.insert(build);