Skip to content

Commit

Permalink
runfiles,cc: add a method to discover runfiles
Browse files Browse the repository at this point in the history
The new method discovers the runfiles manifest and
runfiles directory using the values of the
RUNFILES_MANIFEST_FILE and RUNFILES_DIR envvars
(if specified), and if needed, also looks for them
next to argv[0].

See #4460

Change-Id: Ie9af0a92aa6879b1d9afdee2789f625de1b0d595

Closes #5140.

Change-Id: Ie9af0a92aa6879b1d9afdee2789f625de1b0d595
PiperOrigin-RevId: 195093387
  • Loading branch information
laszlocsomor authored and Copybara-Service committed May 2, 2018
1 parent ac250de commit 9f2b052
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
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
103 changes: 103 additions & 0 deletions tools/cpp/runfiles/runfiles_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 9f2b052

Please sign in to comment.