aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--tools/cpp/runfiles/runfiles.cc54
-rw-r--r--tools/cpp/runfiles/runfiles.h21
-rw-r--r--tools/cpp/runfiles/runfiles_test.cc103
3 files changed, 178 insertions, 0 deletions
diff --git a/tools/cpp/runfiles/runfiles.cc b/tools/cpp/runfiles/runfiles.cc
index 2f25b53ae5..1e6f0583bc 100644
--- a/tools/cpp/runfiles/runfiles.cc
+++ b/tools/cpp/runfiles/runfiles.cc
@@ -348,6 +348,60 @@ Runfiles* Runfiles::CreateDirectoryBased(const string& directory_path,
return new DirectoryBased(directory_path);
}
+bool Runfiles::PathsFrom(const string& argv0,
+ function<string(string)> env_lookup,
+ function<bool(const string&)> is_runfiles_manifest,
+ function<bool(const string&)> is_runfiles_directory,
+ string* out_manifest, string* out_directory) {
+ out_manifest->clear();
+ out_directory->clear();
+ string mf = env_lookup("RUNFILES_MANIFEST_FILE");
+ string dir = env_lookup("RUNFILES_DIR");
+
+ bool mfValid = is_runfiles_manifest(mf);
+ bool dirValid = is_runfiles_directory(dir);
+
+ if (!mfValid && !dirValid) {
+ mf = argv0 + ".runfiles/MANIFEST";
+ dir = argv0 + ".runfiles";
+ mfValid = is_runfiles_manifest(mf);
+ dirValid = is_runfiles_directory(dir);
+ if (!mfValid) {
+ mf = argv0 + ".runfiles_manifest";
+ mfValid = is_runfiles_manifest(mf);
+ }
+ }
+
+ if (!mfValid && !dirValid) {
+ return false;
+ }
+
+ if (!mfValid) {
+ mf = dir + "/MANIFEST";
+ mfValid = is_runfiles_manifest(mf);
+ if (!mfValid) {
+ mf = dir + "_manifest";
+ mfValid = is_runfiles_manifest(mf);
+ }
+ }
+
+ if (!dirValid) {
+ static const size_t kSubstrLen = 9; // "_manifest" or "/MANIFEST"
+ dir = mf.substr(0, mf.size() - kSubstrLen);
+ dirValid = is_runfiles_directory(dir);
+ }
+
+ if (mfValid) {
+ *out_manifest = mf;
+ }
+
+ if (dirValid) {
+ *out_directory = dir;
+ }
+
+ return true;
+}
+
} // namespace runfiles
} // namespace cpp
} // namespace tools
diff --git a/tools/cpp/runfiles/runfiles.h b/tools/cpp/runfiles/runfiles.h
index 2fad6df3dc..d161ef16e3 100644
--- a/tools/cpp/runfiles/runfiles.h
+++ b/tools/cpp/runfiles/runfiles.h
@@ -138,6 +138,27 @@ class Runfiles {
// need to use runfiles.
virtual std::vector<std::pair<std::string, std::string> > EnvVars() const = 0;
+ // Computes the path of the runfiles manifest and the runfiles directory.
+ //
+ // If the method finds both a valid manifest and valid directory according to
+ // `is_runfiles_manifest` and `is_runfiles_directory`, then the method sets
+ // the corresponding values to `out_manifest` and `out_directory` and returns
+ // true.
+ //
+ // If the method only finds a valid manifest or a valid directory, but not
+ // both, then it sets the corresponding output variable (`out_manifest` or
+ // `out_directory`) to the value while clearing the other output variable. The
+ // method still returns true in this case.
+ //
+ // If the method cannot find either a valid manifest or valid directory, it
+ // clears both output variables and returns false.
+ static bool PathsFrom(
+ const std::string& argv0,
+ std::function<std::string(std::string)> env_lookup,
+ std::function<bool(const std::string&)> is_runfiles_manifest,
+ std::function<bool(const std::string&)> is_runfiles_directory,
+ std::string* out_manifest, std::string* out_directory);
+
protected:
Runfiles() {}
diff --git a/tools/cpp/runfiles/runfiles_test.cc b/tools/cpp/runfiles/runfiles_test.cc
index 2177e02afc..02135abfb3 100644
--- a/tools/cpp/runfiles/runfiles_test.cc
+++ b/tools/cpp/runfiles/runfiles_test.cc
@@ -486,6 +486,109 @@ TEST_F(RunfilesTest, IsAbsolute) {
EXPECT_TRUE(TestOnly_IsAbsolute("x:\\"));
}
+TEST_F(RunfilesTest, PathsFromEnvVars) {
+ string mf, dir;
+
+ static const function<string(string)> kEnvVars = [](string key) {
+ if (key == "TEST_SRCDIR") {
+ return "always ignored";
+ } else if (key == "RUNFILES_MANIFEST_FILE") {
+ return "mock1/MANIFEST";
+ } else if (key == "RUNFILES_DIR") {
+ return "mock2";
+ } else {
+ return "";
+ }
+ };
+
+ // Both envvars have a valid value.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "mock1/MANIFEST"; },
+ [](const string& path) { return path == "mock2"; }, &mf, &dir));
+ EXPECT_EQ(mf, "mock1/MANIFEST");
+ EXPECT_EQ(dir, "mock2");
+
+ // RUNFILES_MANIFEST_FILE is invalid but RUNFILES_DIR is good and there's a
+ // runfiles manifest in the runfiles directory.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "mock2/MANIFEST"; },
+ [](const string& path) { return path == "mock2"; }, &mf, &dir));
+ EXPECT_EQ(mf, "mock2/MANIFEST");
+ EXPECT_EQ(dir, "mock2");
+
+ // RUNFILES_MANIFEST_FILE is invalid but RUNFILES_DIR is good, but there's no
+ // runfiles manifest in the runfiles directory.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars, [](const string& path) { return false; },
+ [](const string& path) { return path == "mock2"; }, &mf, &dir));
+ EXPECT_EQ(mf, "");
+ EXPECT_EQ(dir, "mock2");
+
+ // RUNFILES_DIR is invalid but RUNFILES_MANIFEST_FILE is good, and it is in
+ // a valid-looking runfiles directory.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "mock1/MANIFEST"; },
+ [](const string& path) { return path == "mock1"; }, &mf, &dir));
+ EXPECT_EQ(mf, "mock1/MANIFEST");
+ EXPECT_EQ(dir, "mock1");
+
+ // RUNFILES_DIR is invalid but RUNFILES_MANIFEST_FILE is good, but it is not
+ // in any valid-looking runfiles directory.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "mock1/MANIFEST"; },
+ [](const string& path) { return false; }, &mf, &dir));
+ EXPECT_EQ(mf, "mock1/MANIFEST");
+ EXPECT_EQ(dir, "");
+
+ // Both envvars are invalid, but there's a manifest in a runfiles directory
+ // next to argv0, however there's no other content in the runfiles directory.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "argv0.runfiles/MANIFEST"; },
+ [](const string& path) { return false; }, &mf, &dir));
+ EXPECT_EQ(mf, "argv0.runfiles/MANIFEST");
+ EXPECT_EQ(dir, "");
+
+ // Both envvars are invalid, but there's a manifest next to argv0. There's
+ // no runfiles tree anywhere.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "argv0.runfiles_manifest"; },
+ [](const string& path) { return false; }, &mf, &dir));
+ EXPECT_EQ(mf, "argv0.runfiles_manifest");
+ EXPECT_EQ(dir, "");
+
+ // Both envvars are invalid, but there's a valid manifest next to argv0, and a
+ // valid runfiles directory (without a manifest in it).
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "argv0.runfiles_manifest"; },
+ [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir));
+ EXPECT_EQ(mf, "argv0.runfiles_manifest");
+ EXPECT_EQ(dir, "argv0.runfiles");
+
+ // Both envvars are invalid, but there's a valid runfiles directory next to
+ // argv0, though no manifest in it.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars, [](const string& path) { return false; },
+ [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir));
+ EXPECT_EQ(mf, "");
+ EXPECT_EQ(dir, "argv0.runfiles");
+
+ // Both envvars are invalid, but there's a valid runfiles directory next to
+ // argv0 with a valid manifest in it.
+ EXPECT_TRUE(Runfiles::PathsFrom(
+ "argv0", kEnvVars,
+ [](const string& path) { return path == "argv0.runfiles/MANIFEST"; },
+ [](const string& path) { return path == "argv0.runfiles"; }, &mf, &dir));
+ EXPECT_EQ(mf, "argv0.runfiles/MANIFEST");
+ EXPECT_EQ(dir, "argv0.runfiles");
+}
+
} // namespace
} // namespace runfiles
} // namespace cpp