Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runfiles,cc: add a method to discover runfiles #5140

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tools/cpp/runfiles/runfiles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions tools/cpp/runfiles/runfiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

Expand Down
116 changes: 116 additions & 0 deletions tools/cpp/runfiles/runfiles_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,122 @@ 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
Expand Down