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

Make C++ runfiles library repo mapping aware #16623

Closed
wants to merge 2 commits into from
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
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