Support source-relative paths for script_executable This is derived from https://crrev.com/c/8160801/comment/bfc1cbdb_33949900/ Support source-relative paths starting with "//" for script_executable in the dotfile (.gn) and via the --script-executable command line flag. When specified with "//", the path is resolved relative to the source root and subsequently converted into a build-dir-relative path in BuildSettings::SetPythonPath. This avoids hardcoding relative paths from the build directory. Bug: 537680330 Change-Id: I64392fd11f021ec093d6aa3f9a3eb63d0c7223fd Reviewed-on: https://gn-review.googlesource.com/c/gn/+/24500 Reviewed-by: Sylvain Defresne <sdefresne@chromium.org> Commit-Queue: Takuto Ikuta <tikuta@google.com>
diff --git a/docs/reference.md b/docs/reference.md index e4c9013..62eb514 100644 --- a/docs/reference.md +++ b/docs/reference.md
@@ -7583,6 +7583,10 @@ calls using the Python interpreter found in PATH. This value specifies the Python executable or other interpreter to use instead. + If set to a path starting with "//", it will be interpreted as relative + to the source root directory and converted to a build-directory-relative + path in the generated Ninja files. + If set to the empty string, the scripts will be executed directly. The command-line switch --script-executable will override this value (see
diff --git a/src/gn/setup.cc b/src/gn/setup.cc index 132d711..95e4c19 100644 --- a/src/gn/setup.cc +++ b/src/gn/setup.cc
@@ -191,6 +191,10 @@ calls using the Python interpreter found in PATH. This value specifies the Python executable or other interpreter to use instead. + If set to a path starting with "//", it will be interpreted as relative + to the source root directory and converted to a build-directory-relative + path in the generated Ninja files. + If set to the empty string, the scripts will be executed directly. The command-line switch --script-executable will override this value (see @@ -868,7 +872,15 @@ if (cmdline.HasSwitch(switches::kScriptExecutable)) { auto script_executable = cmdline.GetSwitchValuePath(switches::kScriptExecutable); - build_settings_.SetPythonPath(ProcessFileExtensions(script_executable)); + std::string str_val = FilePathToUTF8(script_executable); + base::FilePath python_path; + if (str_val.starts_with("//")) { + SourceFile source_file(std::move(str_val)); + python_path = build_settings_.GetFullPath(source_file); + } else { + python_path = std::move(script_executable); + } + build_settings_.SetPythonPath(ProcessFileExtensions(python_path)); } else if (value) { if (!value->VerifyTypeIs(Value::STRING, err)) { return false; @@ -877,8 +889,14 @@ // invoked by actions will be run directly. base::FilePath python_path; if (!value->string_value().empty()) { - python_path = - ProcessFileExtensions(UTF8ToFilePath(value->string_value())); + const std::string& str_val = value->string_value(); + if (str_val.starts_with("//")) { + SourceFile source_file(str_val); + python_path = build_settings_.GetFullPath(source_file); + } else { + python_path = UTF8ToFilePath(str_val); + } + python_path = ProcessFileExtensions(python_path); if (python_path.empty()) { *err = Err(Location(), "Could not find \"" + value->string_value() + "\" from dotfile in PATH.");
diff --git a/src/gn/setup_unittest.cc b/src/gn/setup_unittest.cc index 3140e52..0fe9388 100644 --- a/src/gn/setup_unittest.cc +++ b/src/gn/setup_unittest.cc
@@ -552,3 +552,34 @@ EXPECT_EQ(setup.build_settings().python_path(), script_executable); #endif } + +TEST_F(SetupTest, SourceRelativeScriptExecutable) { + base::CommandLine cmdline(base::CommandLine::NO_PROGRAM); + + const char kDotfileContents[] = R"( +buildconfig = "//BUILDCONFIG.gn" +script_executable = "//third_party/python/bin/python3" +)"; + + 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_TRUE(setup.build_settings().python_path_is_relative_to_build_dir()); + EXPECT_EQ( + setup.build_settings().python_path(), + base::FilePath(FILE_PATH_LITERAL("../../third_party/python/bin/python3")) + .NormalizePathSeparatorsTo('/')); +}
diff --git a/src/gn/switches.cc b/src/gn/switches.cc index 61b3425..27f30fd 100644 --- a/src/gn/switches.cc +++ b/src/gn/switches.cc
@@ -153,6 +153,10 @@ action targets and exec_script calls. By default GN searches the PATH for Python to execute these scripts. + If set to a path starting with "//", it will be interpreted as relative + to the source root directory and converted to a build-directory-relative + path in the generated Ninja files. + If set to the empty string, the path of scripts specified in action targets and exec_script calls will be executed directly. )";