aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/runfiles/runfiles.cc
diff options
context:
space:
mode:
authorGravatar laszlocsomor <laszlocsomor@google.com>2018-03-21 02:52:51 -0700
committerGravatar Copybara-Service <copybara-piper@google.com>2018-03-21 02:54:02 -0700
commit33c08852e9145120367ff37275adaeed33809a0a (patch)
tree6def05946747bca91658feb9a84c3d3f2fdb177f /src/tools/runfiles/runfiles.cc
parent8a5a0a3ed1e19768b6ee024189307bb4ac27460d (diff)
Automated rollback of commit 8a5a0a3ed1e19768b6ee024189307bb4ac27460d.
*** Reason for rollback *** breaks building //src:bazel *** Original change description *** runfiles,C++: move to //tools/cpp/runfiles Move the C++ runfiles library to the location of the rest of the C++ tools. Also change the C++ namespace to reflect the directory hierarchy. We have not yet announced nor released the C++ runfiles library so these refactorings are fine. See https://github.com/bazelbuild/bazel/issues/4460 Closes #4873. PiperOrigin-RevId: 189883066
Diffstat (limited to 'src/tools/runfiles/runfiles.cc')
-rw-r--r--src/tools/runfiles/runfiles.cc317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/tools/runfiles/runfiles.cc b/src/tools/runfiles/runfiles.cc
new file mode 100644
index 0000000000..b1a870558b
--- /dev/null
+++ b/src/tools/runfiles/runfiles.cc
@@ -0,0 +1,317 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "tools/runfiles/runfiles.h"
+
+#ifdef COMPILER_MSVC
+#include <windows.h>
+#else // not COMPILER_MSVC
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif // COMPILER_MSVC
+
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <vector>
+
+#ifdef COMPILER_MSVC
+#include <memory>
+#endif // COMPILER_MSVC
+
+namespace bazel {
+namespace runfiles {
+
+using std::function;
+using std::map;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace {
+
+class RunfilesImpl : public Runfiles {
+ public:
+ static Runfiles* Create(const string& argv0,
+ function<string(const string&)> env_lookup,
+ string* error);
+
+ string Rlocation(const string& path) const override;
+
+ // Returns the runtime-location of a given runfile.
+ //
+ // This method assumes that the caller already validated the `path`. See
+ // Runfiles::Rlocation for requirements.
+ virtual string RlocationChecked(const string& path) const = 0;
+
+ protected:
+ RunfilesImpl() {}
+ virtual ~RunfilesImpl() {}
+};
+
+// Runfiles implementation that parses a runfiles-manifest to look up runfiles.
+class ManifestBased : public RunfilesImpl {
+ public:
+ // Returns a new `ManifestBased` instance.
+ // Reads the file at `manifest_path` to build a map of the runfiles.
+ // Returns nullptr upon failure.
+ static ManifestBased* Create(const string& manifest_path, string* error);
+
+ vector<pair<string, string> > EnvVars() const override;
+ string RlocationChecked(const string& path) const override;
+
+ private:
+ ManifestBased(const string& manifest_path, map<string, string>&& runfiles_map)
+ : manifest_path_(manifest_path), runfiles_map_(runfiles_map) {}
+
+ ManifestBased(const ManifestBased&) = delete;
+ ManifestBased(ManifestBased&&) = delete;
+ ManifestBased& operator=(const ManifestBased&) = delete;
+ ManifestBased& operator=(ManifestBased&&) = delete;
+
+ string RunfilesDir() const;
+ static bool ParseManifest(const string& path, map<string, string>* result,
+ string* error);
+
+ const string manifest_path_;
+ const map<string, string> runfiles_map_;
+};
+
+// Runfiles implementation that appends runfiles paths to the runfiles root.
+class DirectoryBased : public RunfilesImpl {
+ public:
+ DirectoryBased(string runfiles_path)
+ : runfiles_path_(std::move(runfiles_path)) {}
+ vector<pair<string, string> > EnvVars() const override;
+ string RlocationChecked(const string& path) const override;
+
+ private:
+ DirectoryBased(const DirectoryBased&) = delete;
+ DirectoryBased(DirectoryBased&&) = delete;
+ DirectoryBased& operator=(const DirectoryBased&) = delete;
+ DirectoryBased& operator=(DirectoryBased&&) = delete;
+
+ const string runfiles_path_;
+};
+
+bool IsReadableFile(const string& path) {
+ return std::ifstream(path).is_open();
+}
+
+bool IsDirectory(const string& path) {
+#ifdef COMPILER_MSVC
+ DWORD attrs = GetFileAttributesA(path.c_str());
+ return (attrs != INVALID_FILE_ATTRIBUTES) &&
+ (attrs & FILE_ATTRIBUTE_DIRECTORY);
+#else
+ struct stat buf;
+ return stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode);
+#endif
+}
+
+Runfiles* RunfilesImpl::Create(const string& argv0,
+ function<string(const string&)> env_lookup,
+ string* error) {
+ string manifest(std::move(env_lookup("RUNFILES_MANIFEST_FILE")));
+ if (!manifest.empty()) {
+ return ManifestBased::Create(manifest, error);
+ }
+
+ string directory(std::move(env_lookup("RUNFILES_DIR")));
+ if (!directory.empty()) {
+ return new DirectoryBased(directory);
+ }
+
+ manifest = argv0 + ".runfiles_manifest";
+ if (IsReadableFile(manifest)) {
+ return CreateManifestBased(manifest, error);
+ }
+
+ manifest = argv0 + ".runfiles/MANIFEST";
+ if (IsReadableFile(manifest)) {
+ return CreateManifestBased(manifest, error);
+ }
+
+ directory = argv0 + ".runfiles";
+ if (IsDirectory(directory)) {
+ return CreateDirectoryBased(std::move(directory), error);
+ }
+
+ if (error) {
+ std::ostringstream err;
+ err << "ERROR: " << __FILE__ << "(" << __LINE__
+ << "): cannot find runfiles (argv0=\"" << argv0 << "\")";
+ *error = err.str();
+ }
+ return nullptr;
+}
+
+bool IsAbsolute(const string& path) {
+ if (path.empty()) {
+ return false;
+ }
+ char c = path.front();
+ return (c == '/') || (path.size() >= 3 &&
+ ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) &&
+ path[1] == ':' && (path[2] == '\\' || path[2] == '/'));
+}
+
+string GetEnv(const string& key) {
+#ifdef COMPILER_MSVC
+ DWORD size = ::GetEnvironmentVariableA(key.c_str(), NULL, 0);
+ if (size == 0) {
+ return std::move(string()); // unset or empty envvar
+ }
+ std::unique_ptr<char[]> value(new char[size]);
+ ::GetEnvironmentVariableA(key.c_str(), value.get(), size);
+ return move(string(value.get()));
+#else
+ char* result = getenv(key.c_str());
+ return std::move((result == NULL) ? string() : string(result));
+#endif
+}
+
+string RunfilesImpl::Rlocation(const string& path) const {
+ if (path.empty() || path.find("..") != string::npos) {
+ return std::move(string());
+ }
+ if (IsAbsolute(path)) {
+ return path;
+ }
+ return RlocationChecked(path);
+}
+
+ManifestBased* ManifestBased::Create(const string& manifest_path,
+ string* error) {
+ map<string, string> runfiles;
+ return ParseManifest(manifest_path, &runfiles, error)
+ ? new ManifestBased(manifest_path, std::move(runfiles))
+ : nullptr;
+}
+
+string ManifestBased::RlocationChecked(const string& path) const {
+ const auto value = runfiles_map_.find(path);
+ return std::move(value == runfiles_map_.end() ? string() : value->second);
+}
+
+vector<pair<string, string> > ManifestBased::EnvVars() const {
+ return std::move(vector<pair<string, string> >(
+ {std::make_pair("RUNFILES_MANIFEST_FILE", manifest_path_),
+ // TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can
+ // pick up RUNFILES_DIR.
+ std::make_pair("JAVA_RUNFILES", RunfilesDir())}));
+}
+
+string ManifestBased::RunfilesDir() const {
+ const auto pos1 = manifest_path_.size() - 9; // "_MANIFEST"
+ const auto pos2 = manifest_path_.size() - 18; // ".runfiles_manifest"
+ if (manifest_path_.rfind("/MANIFEST") == pos1 ||
+ manifest_path_.rfind("\\MANIFEST") == pos1 ||
+ manifest_path_.rfind(".runfiles_manifest") == pos2) {
+ return std::move(manifest_path_.substr(0, pos1)); // remove ".MANIFEST"
+ } else {
+ return std::move(string());
+ }
+}
+
+bool ManifestBased::ParseManifest(const string& path,
+ map<string, string>* result, string* error) {
+ std::ifstream stm(path);
+ if (!stm.is_open()) {
+ if (error) {
+ std::ostringstream err;
+ err << "ERROR: " << __FILE__ << "(" << __LINE__
+ << "): cannot open runfiles manifest \"" << path << "\"";
+ *error = err.str();
+ }
+ return false;
+ }
+ string line;
+ std::getline(stm, line);
+ size_t line_count = 1;
+ while (!line.empty()) {
+ string::size_type idx = line.find_first_of(' ');
+ if (idx == string::npos) {
+ if (error) {
+ std::ostringstream err;
+ err << "ERROR: " << __FILE__ << "(" << __LINE__
+ << "): bad runfiles manifest entry in \"" << path << "\" line #"
+ << line_count << ": \"" << line << "\"";
+ *error = err.str();
+ }
+ return false;
+ }
+ (*result)[line.substr(0, idx)] = line.substr(idx + 1);
+ std::getline(stm, line);
+ ++line_count;
+ }
+ return true;
+}
+
+string DirectoryBased::RlocationChecked(const string& path) const {
+ return std::move(runfiles_path_ + "/" + path);
+}
+
+vector<pair<string, string> > DirectoryBased::EnvVars() const {
+ return std::move(vector<pair<string, string> >(
+ {std::make_pair("RUNFILES_DIR", runfiles_path_),
+ // TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can
+ // pick up RUNFILES_DIR.
+ std::make_pair("JAVA_RUNFILES", runfiles_path_)}));
+}
+
+} // namespace
+
+namespace testing {
+
+Runfiles* TestOnly_CreateRunfiles(const std::string& argv0,
+ function<string(const string&)> env_lookup,
+ string* error) {
+ return RunfilesImpl::Create(argv0, env_lookup, error);
+}
+
+bool TestOnly_IsAbsolute(const string& path) { return IsAbsolute(path); }
+
+} // namespace testing
+
+Runfiles* Runfiles::Create(const string& argv0, string* error) {
+ return RunfilesImpl::Create(
+ argv0,
+ [](const string& key) {
+ if (key == "RUNFILES_MANIFEST_FILE" || key == "RUNFILES_DIR") {
+ string val(GetEnv(key));
+ return std::move(val);
+ } else {
+ return std::move(string());
+ }
+ },
+ error);
+}
+
+Runfiles* Runfiles::CreateManifestBased(const string& manifest_path,
+ string* error) {
+ return ManifestBased::Create(manifest_path, error);
+}
+
+Runfiles* Runfiles::CreateDirectoryBased(const string& directory_path,
+ string* error) {
+ // Note: `error` is intentionally unused because we don't expect any errors
+ // here. We expect an `error` pointer so that we may use it in the future if
+ // need be, without having to change the API.
+ return new DirectoryBased(directory_path);
+}
+
+} // namespace runfiles
+} // namespace bazel