Skip to content

Commit

Permalink
Make C++ runfiles library repo mapping aware
Browse files Browse the repository at this point in the history
Also removes a comment mentioning a `Create` overload that does not
exist.
  • Loading branch information
fmeum committed Nov 1, 2022
1 parent 0015ac0 commit be7373d
Show file tree
Hide file tree
Showing 3 changed files with 602 additions and 60 deletions.
186 changes: 166 additions & 20 deletions tools/cpp/runfiles/runfiles_src.cc
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,32 @@ bool IsDirectory(const string& path) {

bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file,
std::string runfiles_dir, std::string* out_manifest,
std::string* out_directory);
std::string* out_directory, std::string* out_repo_mapping);

bool PathsFrom(const std::string& argv0, std::string runfiles_manifest_file,
std::string runfiles_dir,
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);
std::function<bool(const std::string&)> is_repo_mapping,
std::string* out_manifest, std::string* out_directory,
std::string* out_repo_mapping);

bool ParseManifest(const string& path, map<string, string>* result,
string* error);
bool ParseRepoMapping(const string& path,
map<pair<string, string>, string>* result,
string* error);

} // namespace

Runfiles* Runfiles::Create(const string& argv0,
const string& runfiles_manifest_file,
const string& runfiles_dir, string* error) {
string manifest, directory;
const string& runfiles_dir,
const string& source_repository,
string* error) {
string manifest, directory, repo_mapping;
if (!PathsFrom(argv0, runfiles_manifest_file, runfiles_dir, &manifest,
&directory)) {
&directory, &repo_mapping)) {
if (error) {
std::ostringstream err;
err << "ERROR: " << __FILE__ << "(" << __LINE__
Expand All @@ -124,7 +131,7 @@ Runfiles* Runfiles::Create(const string& argv0,
return nullptr;
}

const vector<pair<string, string> > envvars = {
vector<pair<string, string> > envvars = {
{"RUNFILES_MANIFEST_FILE", manifest},
{"RUNFILES_DIR", directory},
// TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can
Expand All @@ -138,8 +145,16 @@ Runfiles* Runfiles::Create(const string& argv0,
}
}

map<pair<string, string>, string> mapping;
if (!repo_mapping.empty()) {
if (!ParseRepoMapping(repo_mapping, &mapping, error)) {
return nullptr;
}
}

return new Runfiles(std::move(runfiles), std::move(directory),
std::move(envvars));
std::move(mapping), std::move(envvars),
string(source_repository));
}

bool IsAbsolute(const string& path) {
Expand All @@ -148,7 +163,7 @@ bool IsAbsolute(const string& path) {
}
char c = path.front();
return (c == '/' && (path.size() < 2 || path[1] != '/')) ||
(path.size() >= 3 &&
(path.size() >= 3 &&
((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
path[1] == ':' && (path[2] == '\\' || path[2] == '/'));
}
Expand All @@ -169,6 +184,11 @@ string GetEnv(const string& key) {
}

string Runfiles::Rlocation(const string& path) const {
return Rlocation(path, source_repository_);
}

string Runfiles::Rlocation(const string& path,
const string& source_repo) const {
if (path.empty() || starts_with(path, "../") || contains(path, "/..") ||
starts_with(path, "./") || contains(path, "/./") ||
ends_with(path, "/.") || contains(path, "//")) {
Expand All @@ -177,6 +197,24 @@ string Runfiles::Rlocation(const string& path) const {
if (IsAbsolute(path)) {
return path;
}

if (repo_mapping_.empty()) {
return RlocationUnchecked(path);
}
string::size_type first_slash = path.find_first_of('/');
if (first_slash == string::npos) {
return RlocationUnchecked(path);
}
string target_apparent = path.substr(0, first_slash);
auto
target = repo_mapping_.find(std::make_pair(source_repo, target_apparent));
if (target == repo_mapping_.cend()) {
return RlocationUnchecked(path);
}
return RlocationUnchecked(target->second + path.substr(first_slash));
}

string Runfiles::RlocationUnchecked(const string& path) const {
const auto exact_match = runfiles_map_.find(path);
if (exact_match != runfiles_map_.end()) {
return exact_match->second;
Expand All @@ -188,7 +226,7 @@ string Runfiles::Rlocation(const string& path) const {
// prefix to the looked up path.
std::size_t prefix_end = path.size();
while ((prefix_end = path.find_last_of('/', prefix_end - 1)) !=
string::npos) {
string::npos) {
const string prefix = path.substr(0, prefix_end);
const auto prefix_match = runfiles_map_.find(prefix);
if (prefix_match != runfiles_map_.end()) {
Expand Down Expand Up @@ -238,48 +276,138 @@ bool ParseManifest(const string& path, map<string, string>* result,
return true;
}

bool ParseRepoMapping(const string& path,
map<pair<string, string>, string>* result,
string* error) {
std::ifstream stm(path);
if (!stm.is_open()) {
if (error) {
std::ostringstream err;
err << "ERROR: " << __FILE__ << "(" << __LINE__
<< "): cannot open repository mapping \"" << path << "\"";
*error = err.str();
}
return false;
}
string line;
std::getline(stm, line);
size_t line_count = 1;
while (!line.empty()) {
string::size_type first_comma = line.find_first_of(',');
if (first_comma == string::npos) {
if (error) {
std::ostringstream err;
err << "ERROR: " << __FILE__ << "(" << __LINE__
<< "): bad repository mapping entry in \"" << path << "\" line #"
<< line_count << ": \"" << line << "\"";
*error = err.str();
}
return false;
}
string::size_type second_comma = line.find_first_of(',', first_comma + 1);
if (second_comma == string::npos) {
if (error) {
std::ostringstream err;
err << "ERROR: " << __FILE__ << "(" << __LINE__
<< "): bad repository mapping entry in \"" << path << "\" line #"
<< line_count << ": \"" << line << "\"";
*error = err.str();
}
return false;
}

string source = line.substr(0, first_comma);
string target_apparent =
line.substr(first_comma + 1, second_comma - (first_comma + 1));
string target = line.substr(second_comma + 1);

(*result)[std::make_pair(source, target_apparent)] = target;
std::getline(stm, line);
++line_count;
}
return true;
}

} // namespace

namespace testing {

bool TestOnly_PathsFrom(const string& argv0, string mf, string dir,
bool TestOnly_PathsFrom(const string& argv0,
string mf,
string dir,
function<bool(const string&)> is_runfiles_manifest,
function<bool(const string&)> is_runfiles_directory,
string* out_manifest, string* out_directory) {
return PathsFrom(argv0, mf, dir, is_runfiles_manifest, is_runfiles_directory,
out_manifest, out_directory);
function<bool(const string&)> is_repo_mapping,
string* out_manifest,
string* out_directory,
string* out_repo_mapping) {
return PathsFrom(argv0,
mf,
dir,
is_runfiles_manifest,
is_runfiles_directory,
is_repo_mapping,
out_manifest,
out_directory,
out_repo_mapping);
}

bool TestOnly_IsAbsolute(const string& path) { return IsAbsolute(path); }

} // namespace testing

Runfiles* Runfiles::Create(const string& argv0, string* error) {
Runfiles* Runfiles::Create(const std::string& argv0,
const std::string& runfiles_manifest_file,
const std::string& runfiles_dir,
std::string* error) {
return Runfiles::Create(argv0,
runfiles_manifest_file,
runfiles_dir,
"",
error);
}

Runfiles* Runfiles::Create(const string& argv0,
const string& source_repository,
string* error) {
return Runfiles::Create(argv0, GetEnv("RUNFILES_MANIFEST_FILE"),
GetEnv("RUNFILES_DIR"), error);
GetEnv("RUNFILES_DIR"), source_repository, error);
}

Runfiles* Runfiles::CreateForTest(std::string* error) {
Runfiles* Runfiles::Create(const string& argv0, string* error) {
return Runfiles::Create(argv0, "", error);
}

Runfiles* Runfiles::CreateForTest(const string& source_repository,
std::string* error) {
return Runfiles::Create(std::string(), GetEnv("RUNFILES_MANIFEST_FILE"),
GetEnv("TEST_SRCDIR"), error);
GetEnv("TEST_SRCDIR"), source_repository, error);
}

Runfiles* Runfiles::CreateForTest(std::string* error) {
return Runfiles::CreateForTest("", error);
}

namespace {

bool PathsFrom(const string& argv0, string mf, string dir, string* out_manifest,
string* out_directory) {
string* out_directory, string* out_repo_mapping) {
return PathsFrom(argv0, mf, dir,
[](const string& path) { return IsReadableFile(path); },
[](const string& path) { return IsDirectory(path); },
out_manifest, out_directory);
[](const string& path) { return IsReadableFile(path); },
out_manifest, out_directory, out_repo_mapping);
}

bool PathsFrom(const string& argv0, string mf, string dir,
function<bool(const string&)> is_runfiles_manifest,
function<bool(const string&)> is_runfiles_directory,
string* out_manifest, string* out_directory) {
function<bool(const string&)> is_repo_mapping,
string* out_manifest, string* out_directory,
string* out_repo_mapping) {
out_manifest->clear();
out_directory->clear();
out_repo_mapping->clear();

bool mfValid = is_runfiles_manifest(mf);
bool dirValid = is_runfiles_directory(dir);
Expand Down Expand Up @@ -315,6 +443,20 @@ bool PathsFrom(const string& argv0, string mf, string dir,
dirValid = is_runfiles_directory(dir);
}

string rm;
bool rmValid = false;

if (dirValid && ends_with(dir, ".runfiles")) {
rm = dir.substr(0, dir.size() - 9) + ".repo_mapping";
rmValid = is_repo_mapping(rm);
}

if (!rmValid && mfValid && (ends_with(mf, ".runfiles_manifest") ||
ends_with(mf, ".runfiles/MANIFEST"))) {
rm = mf.substr(0, mf.size() - 18) + ".repo_mapping";
rmValid = is_repo_mapping(rm);
}

if (mfValid) {
*out_manifest = mf;
}
Expand All @@ -323,6 +465,10 @@ bool PathsFrom(const string& argv0, string mf, string dir,
*out_directory = dir;
}

if (rmValid) {
*out_repo_mapping = rm;
}

return true;
}

Expand Down
Loading

0 comments on commit be7373d

Please sign in to comment.