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);