From 819bf38d97e6eef3c823bdae3ffcdb013d6d83e3 Mon Sep 17 00:00:00 2001 From: Laszlo Csomor Date: Mon, 30 Apr 2018 03:29:21 -0700 Subject: c++,runfiles: move runfiles library Move the half-done C++ runfiles library to `//tools/cpp/runfiles`. (The Python and Bash runfiles libraries are already under `//tools//runfiles`.) See https://github.com/bazelbuild/bazel/issues/4460 Change-Id: I1006f7f81462ea0e4b1de1adcdba89e386d4f9e7 Closes #5107. Change-Id: I1006f7f81462ea0e4b1de1adcdba89e386d4f9e7 PiperOrigin-RevId: 194763392 --- src/main/cpp/util/BUILD | 2 +- src/tools/runfiles/BUILD | 31 --- src/tools/runfiles/runfiles.cc | 350 -------------------------- src/tools/runfiles/runfiles.h | 184 -------------- src/tools/runfiles/runfiles_test.cc | 489 ------------------------------------ 5 files changed, 1 insertion(+), 1055 deletions(-) delete mode 100644 src/tools/runfiles/runfiles.cc delete mode 100644 src/tools/runfiles/runfiles.h delete mode 100644 src/tools/runfiles/runfiles_test.cc (limited to 'src') diff --git a/src/main/cpp/util/BUILD b/src/main/cpp/util/BUILD index be4a50f700..254b6f8f1f 100644 --- a/src/main/cpp/util/BUILD +++ b/src/main/cpp/util/BUILD @@ -47,9 +47,9 @@ cc_library( ":ijar", "//src/test/cpp/util:__pkg__", "//src/tools/launcher:__subpackages__", - "//src/tools/runfiles:__pkg__", "//src/tools/singlejar:__pkg__", "//third_party/def_parser:__pkg__", + "//tools/cpp/runfiles:__pkg__", ], deps = [ ":blaze_exit_code", diff --git a/src/tools/runfiles/BUILD b/src/tools/runfiles/BUILD index 7222654d6b..9dd99e6b04 100644 --- a/src/tools/runfiles/BUILD +++ b/src/tools/runfiles/BUILD @@ -20,42 +20,11 @@ filegroup( name = "embedded_tools", srcs = [ "BUILD.tools", - "runfiles.cc", - "runfiles.h", "//src/tools/runfiles/java/com/google/devtools/build/runfiles:embedded_tools", ], visibility = ["//src:__pkg__"], ) -cc_library( - name = "cc-runfiles", - srcs = ["runfiles.cc"], - hdrs = ["runfiles.h"], - # This library is available to clients under - # @bazel_tools//tools/runfiles:cc-runfiles, with the same source files. - # The include statement in runfiles.cc that includes runfiles.h must work - # both here in the //src/tools/runfiles package, and in the - # @bazel_tools//tools/runfiles package. - # runfiles.cc includes "tools/runfiles/runfiles.h" so we need to tell the - # compiler to prepend "src" to it so the include path is valid. - # Alternatively we could omit this "copts" attribute here and add some - # include path manipulating attributes to - # @bazel_tools//tools/runfiles:cc-runfiles instead -- that would work too, - # but I (laszlocsomor@) find this solution (i.e. the "copts" attribute on - # this rule) to be simpler. - copts = ["-Isrc"], -) - -cc_test( - name = "cc-runfiles-test", - srcs = ["runfiles_test.cc"], - deps = [ - ":cc-runfiles", - "//src/main/cpp/util:file", - "@com_google_googletest//:gtest_main", - ], -) - test_suite( name = "windows_tests", tags = [ diff --git a/src/tools/runfiles/runfiles.cc b/src/tools/runfiles/runfiles.cc deleted file mode 100644 index 801e44e374..0000000000 --- a/src/tools/runfiles/runfiles.cc +++ /dev/null @@ -1,350 +0,0 @@ -// 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 -#else // not COMPILER_MSVC -#include -#include -#include -#include -#endif // COMPILER_MSVC - -#include -#include -#include -#include - -#ifdef COMPILER_MSVC -#include -#endif // COMPILER_MSVC - -namespace bazel { -namespace runfiles { - -using std::function; -using std::map; -using std::pair; -using std::string; -using std::vector; - -namespace { - -bool starts_with(const string& s, const string& prefix) { - if (prefix.empty()) { - return true; - } - if (s.empty()) { - return false; - } - return s.find(prefix) == 0; -} - -bool contains(const string& s, const string& substr) { - if (substr.empty()) { - return true; - } - if (s.empty()) { - return false; - } - return s.find(substr) != string::npos; -} - -bool ends_with(const string& s, const string& suffix) { - if (suffix.empty()) { - return true; - } - if (s.empty()) { - return false; - } - return s.rfind(suffix) == s.size() - suffix.size(); -} - -class RunfilesImpl : public Runfiles { - public: - static Runfiles* Create(const string& argv0, - function 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 > EnvVars() const override; - string RlocationChecked(const string& path) const override; - - private: - ManifestBased(const string& manifest_path, map&& 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* result, - string* error); - - const string manifest_path_; - const map 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 > 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 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() < 2 || path[1] != '/')) || - (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 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() || starts_with(path, "../") || contains(path, "/..") || - starts_with(path, "./") || contains(path, "/./") || - ends_with(path, "/.") || contains(path, "//")) { - return std::move(string()); - } - if (IsAbsolute(path)) { - return path; - } - return RlocationChecked(path); -} - -ManifestBased* ManifestBased::Create(const string& manifest_path, - string* error) { - map 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 > ManifestBased::EnvVars() const { - return std::move(vector >( - {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* 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 > DirectoryBased::EnvVars() const { - return std::move(vector >( - {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 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 diff --git a/src/tools/runfiles/runfiles.h b/src/tools/runfiles/runfiles.h deleted file mode 100644 index 20e9dca1e1..0000000000 --- a/src/tools/runfiles/runfiles.h +++ /dev/null @@ -1,184 +0,0 @@ -// 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. - -// Runfiles lookup library for Bazel-built C++ binaries and tests. -// -// Usage: -// -// #include "tools/runfiles/runfiles.h" -// ... -// -// int main(int argc, char** argv) { -// std::string error; -// std::unique_ptr runfiles( -// bazel::runfiles::Runfiles::Create(argv[0], &error)); -// if (runfiles == nullptr) { -// ... // error handling -// } -// std::string path(runfiles->Rlocation("io_bazel/src/bazel")); -// std::ifstream data(path); -// if (data.is_open()) { -// ... // use the runfile -// -// The code above creates a manifest- or directory-based implementations -// depending on it finding a runfiles manifest or -directory near argv[0] or -// finding appropriate environment variables that tell it where to find the -// manifest or directory. See `Runfiles::Create` for more info. -// -// If you want to explicitly create a manifest- or directory-based -// implementation, you can do so as follows: -// -// std::unique_ptr runfiles1( -// bazel::runfiles::Runfiles::CreateManifestBased( -// "path/to/foo.runfiles/MANIFEST", &error)); -// -// std::unique_ptr runfiles2( -// bazel::runfiles::Runfiles::CreateDirectoryBased( -// "path/to/foo.runfiles", &error)); -// -// If you want to start child processes that also need runfiles, you need to set -// the right environment variables for them: -// -// std::unique_ptr runfiles( -// bazel::runfiles::Runfiles::Create(argv[0], &error)); -// -// for (const auto i : runfiles->EnvVars()) { -// setenv(i.first, i.second, 1); -// } -// std::string path(runfiles->Rlocation("path/to/binary")); -// if (!path.empty()) { -// pid_t child = fork(); -// ... - -#ifndef BAZEL_SRC_TOOLS_RUNFILES_RUNFILES_H_ -#define BAZEL_SRC_TOOLS_RUNFILES_RUNFILES_H_ 1 - -#include -#include -#include -#include - -namespace bazel { -namespace runfiles { - -class Runfiles { - public: - virtual ~Runfiles() {} - - // Returns a new `Runfiles` instance. - // - // The returned object is either: - // - manifest-based, meaning it looks up runfile paths from a manifest file, - // or - // - directory-based, meaning it looks up runfile paths under a given - // directory path - // - // This method: - // 1. checks the RUNFILES_MANIFEST_FILE or RUNFILES_DIR environment variables; - // if either is non-empty, returns a manifest- or directory-based Runfiles - // object; otherwise - // 2. checks if there's a runfiles manifest (argv0 + ".runfiles_manifest") or - // runfiles directory (argv0 + ".runfiles") next to this binary; if so, - // returns a manifest- or directory-based Runfiles object; otherwise - // 3. returns nullptr. - // - // The manifest-based Runfiles object eagerly reads and caches the whole - // manifest file upon instantiation; this may be relevant for performance - // consideration. - // - // Returns nullptr on error. If `error` is provided, the method prints an - // error message into it. - static Runfiles* Create(const std::string& argv0, - std::string* error = nullptr); - - // Returns a new manifest-based `Runfiles` instance. - // Returns nullptr on error. If `error` is provided, the method prints an - // error message into it. - static Runfiles* CreateManifestBased(const std::string& manifest_path, - std::string* error = nullptr); - - // Returns a new directory-based `Runfiles` instance. - // Returns nullptr on error. If `error` is provided, the method prints an - // error message into it. - static Runfiles* CreateDirectoryBased(const std::string& directory_path, - std::string* error = nullptr); - - // Returns the runtime path of a runfile. - // - // Runfiles are data-dependencies of Bazel-built binaries and tests. - // - // The returned path may not be valid. The caller should check the path's - // validity and that the path exists. - // - // The function may return an empty string. In that case the caller can be - // sure that the Runfiles object does not know about this data-dependency. - // - // Args: - // path: runfiles-root-relative path of the runfile; must not be empty and - // must not contain uplevel references. - // Returns: - // the path to the runfile, which the caller should check for existence, or - // an empty string if the method doesn't know about this runfile - virtual std::string Rlocation(const std::string& path) const = 0; - - // Returns environment variables for subprocesses. - // - // The caller should set the returned key-value pairs in the environment of - // subprocesses in case those subprocesses are also Bazel-built binaries that - // need to use runfiles. - virtual std::vector > EnvVars() const = 0; - - protected: - Runfiles() {} - - private: - Runfiles(const Runfiles&) = delete; - Runfiles(Runfiles&&) = delete; - Runfiles& operator=(const Runfiles&) = delete; - Runfiles& operator=(Runfiles&&) = delete; -}; - -// The "testing" namespace contains functions that allow unit testing the code. -// Do not use these outside of runfiles_test.cc, they are only part of the -// public API for the benefit of the tests. -// These functions and their interface may change without notice. -namespace testing { - -// For testing only. -// -// Create a new Runfiles instance, looking up environment variables using -// `env_lookup`. -// -// Args: -// argv0: name of the binary; if this string is not empty, then the function -// looks for a runfiles manifest or directory next to this -// env_lookup: a function that returns envvar values if an envvar is known, or -// empty string otherwise -Runfiles* TestOnly_CreateRunfiles( - const std::string& argv0, - std::function env_lookup, - std::string* error); - -// For testing only. -// Returns true if `path` is an absolute Unix or Windows path. -// For Windows paths, this function does not regard drive-less absolute paths -// (i.e. absolute-on-current-drive, e.g. "\foo\bar") as absolute and returns -// false for these. -bool TestOnly_IsAbsolute(const std::string& path); - -} // namespace testing -} // namespace runfiles -} // namespace bazel - -#endif // BAZEL_SRC_TOOLS_RUNFILES_RUNFILES_H_ diff --git a/src/tools/runfiles/runfiles_test.cc b/src/tools/runfiles/runfiles_test.cc deleted file mode 100644 index b15fc04a68..0000000000 --- a/src/tools/runfiles/runfiles_test.cc +++ /dev/null @@ -1,489 +0,0 @@ -// 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 "src/tools/runfiles/runfiles.h" - -#ifdef COMPILER_MSVC -#include -#endif // COMPILER_MSVC - -#include -#include -#include -#include - -#include "gtest/gtest.h" -#include "src/main/cpp/util/file.h" - -#define _T(x) #x -#define T(x) _T(x) -#define LINE() T(__LINE__) - -namespace bazel { -namespace runfiles { -namespace { - -using bazel::runfiles::testing::TestOnly_CreateRunfiles; -using bazel::runfiles::testing::TestOnly_IsAbsolute; -using std::cerr; -using std::endl; -using std::function; -using std::pair; -using std::string; -using std::unique_ptr; -using std::vector; - -class RunfilesTest : public ::testing::Test { - protected: - // Create a temporary file that is deleted with the destructor. - class MockFile { - public: - // Create an empty file with the given name under $TEST_TMPDIR. - static MockFile* Create(const string& name); - - // Create a file with the given name and contents under $TEST_TMPDIR. - // The method ensures to create all parent directories, so `name` is allowed - // to contain directory components. - static MockFile* Create(const string& name, const vector& lines); - - ~MockFile(); - const string& Path() const { return path_; } - - private: - MockFile(const string& path) : path_(path) {} - MockFile(const MockFile&) = delete; - MockFile(MockFile&&) = delete; - MockFile& operator=(const MockFile&) = delete; - MockFile& operator=(MockFile&&) = delete; - - const string path_; - }; - - static string GetTemp(); - - static function kEnvWithTestSrcdir; -}; - -function RunfilesTest::kEnvWithTestSrcdir = - [](const string& key) { - if (key == "TEST_SRCDIR") { - return string("always ignored"); - } else { - return string(); - } - }; - -string RunfilesTest::GetTemp() { -#ifdef COMPILER_MSVC - DWORD size = ::GetEnvironmentVariableA("TEST_TMPDIR", NULL, 0); - if (size == 0) { - return std::move(string()); // unset or empty envvar - } - unique_ptr value(new char[size]); - ::GetEnvironmentVariableA("TEST_TMPDIR", value.get(), size); - return std::move(string(value.get())); -#else - char* result = getenv("TEST_TMPDIR"); - return result != NULL ? std::move(string(result)) : std::move(string()); -#endif -} - -RunfilesTest::MockFile* RunfilesTest::MockFile::Create(const string& name) { - return Create(name, vector()); -} - -RunfilesTest::MockFile* RunfilesTest::MockFile::Create( - const string& name, const vector& lines) { - if (name.find("..") != string::npos || TestOnly_IsAbsolute(name)) { - cerr << "WARNING: " << __FILE__ << "(" << __LINE__ << "): bad name: \"" - << name << "\"" << endl; - return nullptr; - } - - string tmp(std::move(RunfilesTest::GetTemp())); - if (tmp.empty()) { - cerr << "WARNING: " << __FILE__ << "(" << __LINE__ - << "): $TEST_TMPDIR is empty" << endl; - return nullptr; - } - string path(tmp + "/" + name); - string dirname = blaze_util::Dirname(path); - if (!blaze_util::MakeDirectories(dirname, 0777)) { - cerr << "WARNING: " << __FILE__ << "(" << __LINE__ << "): MakeDirectories(" - << dirname << ") failed" << endl; - return nullptr; - } - - auto stm = std::ofstream(path); - for (auto i : lines) { - stm << i << std::endl; - } - return new MockFile(path); -} - -RunfilesTest::MockFile::~MockFile() { std::remove(path_.c_str()); } - -TEST_F(RunfilesTest, CreatesManifestBasedRunfilesFromManifestNextToBinary) { - unique_ptr mf( - MockFile::Create("foo" LINE() ".runfiles_manifest", {"a/b c/d"})); - EXPECT_TRUE(mf != nullptr); - string argv0(mf->Path().substr( - 0, mf->Path().size() - string(".runfiles_manifest").size())); - - string error; - unique_ptr r( - TestOnly_CreateRunfiles(argv0, kEnvWithTestSrcdir, &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - EXPECT_EQ(r->Rlocation("a/b"), "c/d"); - // We know it's manifest-based because it returns empty string for unknown - // paths. - EXPECT_EQ(r->Rlocation("unknown"), ""); -} - -TEST_F(RunfilesTest, - CreatesManifestBasedRunfilesFromManifestInRunfilesDirectory) { - unique_ptr mf( - MockFile::Create("foo" LINE() ".runfiles/MANIFEST", {"a/b c/d"})); - EXPECT_TRUE(mf != nullptr); - string argv0(mf->Path().substr( - 0, mf->Path().size() - string(".runfiles/MANIFEST").size())); - - string error; - unique_ptr r( - TestOnly_CreateRunfiles(argv0, kEnvWithTestSrcdir, &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - EXPECT_EQ(r->Rlocation("a/b"), "c/d"); - // We know it's manifest-based because it returns empty string for unknown - // paths. - EXPECT_EQ(r->Rlocation("unknown"), ""); -} - -TEST_F(RunfilesTest, CreatesManifestBasedRunfilesFromEnvvar) { - unique_ptr mf( - MockFile::Create("foo" LINE() ".runfiles_manifest", {"a/b c/d"})); - EXPECT_TRUE(mf != nullptr); - - string error; - unique_ptr r(TestOnly_CreateRunfiles( - "ignore-argv0", - [&mf](const string& key) { - if (key == "RUNFILES_MANIFEST_FILE") { - return mf->Path(); - } else if (key == "RUNFILES_DIR") { - return string("ignored when RUNFILES_MANIFEST_FILE has a value"); - } else if (key == "TEST_SRCDIR") { - return string("always ignored"); - } else { - return string(); - } - }, - &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - EXPECT_EQ(r->Rlocation("a/b"), "c/d"); - // We know it's manifest-based because it returns empty string for unknown - // paths. - EXPECT_EQ(r->Rlocation("unknown"), ""); -} - -TEST_F(RunfilesTest, CannotCreateManifestBasedRunfilesDueToBadManifest) { - unique_ptr mf( - MockFile::Create("foo" LINE() ".runfiles_manifest", {"a b", "nospace"})); - EXPECT_TRUE(mf != nullptr); - - string error; - unique_ptr r(Runfiles::CreateManifestBased(mf->Path(), &error)); - ASSERT_EQ(r, nullptr); - EXPECT_NE(error.find("bad runfiles manifest entry"), string::npos); - EXPECT_NE(error.find("line #2: \"nospace\""), string::npos); -} - -TEST_F(RunfilesTest, ManifestBasedRunfilesRlocation) { - unique_ptr mf( - MockFile::Create("foo" LINE() ".runfiles_manifest", {"a/b c/d"})); - EXPECT_TRUE(mf != nullptr); - - string error; - unique_ptr r(Runfiles::CreateManifestBased(mf->Path(), &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - EXPECT_EQ(r->Rlocation("a/b"), "c/d"); - EXPECT_EQ(r->Rlocation("c/d"), ""); - EXPECT_EQ(r->Rlocation(""), ""); - EXPECT_EQ(r->Rlocation("foo"), ""); - EXPECT_EQ(r->Rlocation("foo/"), ""); - EXPECT_EQ(r->Rlocation("foo/bar"), ""); - EXPECT_EQ(r->Rlocation("../foo"), ""); - EXPECT_EQ(r->Rlocation("foo/.."), ""); - EXPECT_EQ(r->Rlocation("foo/../bar"), ""); - EXPECT_EQ(r->Rlocation("./foo"), ""); - EXPECT_EQ(r->Rlocation("foo/."), ""); - EXPECT_EQ(r->Rlocation("foo/./bar"), ""); - EXPECT_EQ(r->Rlocation("//foo"), ""); - EXPECT_EQ(r->Rlocation("foo//"), ""); - EXPECT_EQ(r->Rlocation("foo//bar"), ""); - EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); - EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); - EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); -} - -TEST_F(RunfilesTest, DirectoryBasedRunfilesRlocation) { - string error; - unique_ptr r(Runfiles::CreateDirectoryBased("whatever", &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - EXPECT_EQ(r->Rlocation("a/b"), "whatever/a/b"); - EXPECT_EQ(r->Rlocation("c/d"), "whatever/c/d"); - EXPECT_EQ(r->Rlocation(""), ""); - EXPECT_EQ(r->Rlocation("foo"), "whatever/foo"); - EXPECT_EQ(r->Rlocation("foo/"), "whatever/foo/"); - EXPECT_EQ(r->Rlocation("foo/bar"), "whatever/foo/bar"); - EXPECT_EQ(r->Rlocation("../foo"), ""); - EXPECT_EQ(r->Rlocation("foo/.."), ""); - EXPECT_EQ(r->Rlocation("foo/../bar"), ""); - EXPECT_EQ(r->Rlocation("./foo"), ""); - EXPECT_EQ(r->Rlocation("foo/."), ""); - EXPECT_EQ(r->Rlocation("foo/./bar"), ""); - EXPECT_EQ(r->Rlocation("//foo"), ""); - EXPECT_EQ(r->Rlocation("foo//"), ""); - EXPECT_EQ(r->Rlocation("foo//bar"), ""); - EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); - EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); - EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); -} - -TEST_F(RunfilesTest, ManifestBasedRunfilesEnvVars) { - const vector suffixes({"/MANIFEST", ".runfiles_manifest", - "runfiles_manifest", ".runfiles", ".manifest", - ".txt"}); - for (vector::size_type i = 0; i < suffixes.size(); ++i) { - unique_ptr mf( - MockFile::Create(string("foo" LINE()) + suffixes[i])); - EXPECT_TRUE(mf != nullptr) << " (suffix=\"" << suffixes[i] << "\")"; - - string error; - unique_ptr r(Runfiles::CreateManifestBased(mf->Path(), &error)); - ASSERT_NE(r, nullptr) << " (suffix=\"" << suffixes[i] << "\")"; - EXPECT_TRUE(error.empty()); - - // The object can compute the runfiles directory when i=0 and i=1, but not - // when i>1 because the manifest file's name doesn't end in a well-known - // way. - const string expected_runfiles_dir( - i < 2 ? mf->Path().substr(0, mf->Path().size() - 9 /* "_manifest" */) - : ""); - vector > expected( - {{"RUNFILES_MANIFEST_FILE", mf->Path()}, - {"JAVA_RUNFILES", expected_runfiles_dir}}); - EXPECT_EQ(r->EnvVars(), expected) << " (suffix=\"" << suffixes[i] << "\")"; - } -} - -TEST_F(RunfilesTest, CreatesDirectoryBasedRunfilesFromDirectoryNextToBinary) { - // We create a directory as a side-effect of creating a mock file. - unique_ptr mf( - MockFile::Create(string("foo" LINE() ".runfiles/dummy"))); - string argv0(mf->Path().substr( - 0, mf->Path().size() - string(".runfiles/dummy").size())); - - string error; - unique_ptr r( - TestOnly_CreateRunfiles(argv0, kEnvWithTestSrcdir, &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - EXPECT_EQ(r->Rlocation("a/b"), argv0 + ".runfiles/a/b"); - // We know it's directory-based because it returns some result for unknown - // paths. - EXPECT_EQ(r->Rlocation("unknown"), argv0 + ".runfiles/unknown"); -} - -TEST_F(RunfilesTest, CreatesDirectoryBasedRunfilesFromEnvvar) { - string error; - unique_ptr r( - TestOnly_CreateRunfiles("ignore-argv0", - [](const string& key) { - if (key == "RUNFILES_DIR") { - return string("runfiles/dir"); - } else if (key == "TEST_SRCDIR") { - return string("always ignored"); - } else { - return string(); - } - }, - &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - EXPECT_EQ(r->Rlocation("a/b"), "runfiles/dir/a/b"); - EXPECT_EQ(r->Rlocation("foo"), "runfiles/dir/foo"); - EXPECT_EQ(r->Rlocation("/Foo"), "/Foo"); - EXPECT_EQ(r->Rlocation("c:/Foo"), "c:/Foo"); - EXPECT_EQ(r->Rlocation("c:\\Foo"), "c:\\Foo"); -} - -TEST_F(RunfilesTest, DirectoryBasedRunfilesEnvVars) { - string error; - unique_ptr r( - Runfiles::CreateDirectoryBased("runfiles/dir", &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - vector > expected( - {{"RUNFILES_DIR", "runfiles/dir"}, {"JAVA_RUNFILES", "runfiles/dir"}}); - EXPECT_EQ(r->EnvVars(), expected); -} - -TEST_F(RunfilesTest, FailsToCreateManifestBasedBecauseManifestDoesNotExist) { - string error; - unique_ptr r( - Runfiles::CreateManifestBased("non-existent-file", &error)); - ASSERT_EQ(r, nullptr); - EXPECT_NE(error.find("cannot open runfiles manifest"), string::npos); -} - -TEST_F(RunfilesTest, FailsToCreateAnyRunfilesBecauseEnvvarsAreNotDefined) { - unique_ptr mf(MockFile::Create(string("foo" LINE()))); - EXPECT_TRUE(mf != nullptr); - - string error; - unique_ptr r( - TestOnly_CreateRunfiles("ignore-argv0", - [&mf](const string& key) { - if (key == "RUNFILES_MANIFEST_FILE") { - return mf->Path(); - } else if (key == "RUNFILES_DIR") { - return string("whatever"); - } else if (key == "TEST_SRCDIR") { - return string("always ignored"); - } else { - return string(); - } - }, - &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - r.reset(TestOnly_CreateRunfiles("ignore-argv0", - [](const string& key) { - if (key == "RUNFILES_DIR") { - return string("whatever"); - } else if (key == "TEST_SRCDIR") { - return string("always ignored"); - } else { - return string(); - } - }, - &error)); - ASSERT_NE(r, nullptr); - EXPECT_TRUE(error.empty()); - - r.reset(TestOnly_CreateRunfiles("ignore-argv0", kEnvWithTestSrcdir, &error)); - ASSERT_EQ(r, nullptr); - EXPECT_NE(error.find("cannot find runfiles"), string::npos); -} - -TEST_F(RunfilesTest, MockFileTest) { - { - unique_ptr mf(MockFile::Create(string("foo" LINE() "/.."))); - EXPECT_TRUE(mf == nullptr); - } - - { - unique_ptr mf(MockFile::Create(string("/Foo" LINE()))); - EXPECT_TRUE(mf == nullptr); - } - - { - unique_ptr mf(MockFile::Create(string("C:/Foo" LINE()))); - EXPECT_TRUE(mf == nullptr); - } - - string path; - { - unique_ptr mf(MockFile::Create(string("foo" LINE() "/bar1/qux"))); - EXPECT_TRUE(mf != nullptr); - path = mf->Path(); - - std::ifstream stm(path); - EXPECT_TRUE(stm.good()); - string actual; - stm >> actual; - EXPECT_TRUE(actual.empty()); - } - { - std::ifstream stm(path); - EXPECT_FALSE(stm.good()); - } - - { - unique_ptr mf( - MockFile::Create(string("foo" LINE() "/bar2/qux"), vector())); - EXPECT_TRUE(mf != nullptr); - path = mf->Path(); - - std::ifstream stm(path); - EXPECT_TRUE(stm.good()); - string actual; - stm >> actual; - EXPECT_TRUE(actual.empty()); - } - { - std::ifstream stm(path); - EXPECT_FALSE(stm.good()); - } - - { - unique_ptr mf( - MockFile::Create(string("foo" LINE() "/bar3/qux"), - {"hello world", "you are beautiful"})); - EXPECT_TRUE(mf != nullptr); - path = mf->Path(); - - std::ifstream stm(path); - EXPECT_TRUE(stm.good()); - string actual; - std::getline(stm, actual); - EXPECT_EQ("hello world", actual); - std::getline(stm, actual); - EXPECT_EQ("you are beautiful", actual); - std::getline(stm, actual); - EXPECT_EQ("", actual); - } - { - std::ifstream stm(path); - EXPECT_FALSE(stm.good()); - } -} - -TEST_F(RunfilesTest, IsAbsolute) { - EXPECT_FALSE(TestOnly_IsAbsolute("foo")); - EXPECT_FALSE(TestOnly_IsAbsolute("foo/bar")); - EXPECT_FALSE(TestOnly_IsAbsolute("\\foo")); - EXPECT_TRUE(TestOnly_IsAbsolute("c:\\foo")); - EXPECT_TRUE(TestOnly_IsAbsolute("c:/foo")); - EXPECT_TRUE(TestOnly_IsAbsolute("/foo")); - EXPECT_TRUE(TestOnly_IsAbsolute("x:\\foo")); - EXPECT_FALSE(TestOnly_IsAbsolute("::\\foo")); - EXPECT_FALSE(TestOnly_IsAbsolute("x\\foo")); - EXPECT_FALSE(TestOnly_IsAbsolute("x:")); - EXPECT_TRUE(TestOnly_IsAbsolute("x:\\")); -} - -} // namespace -} // namespace runfiles -} // namespace bazel -- cgit v1.2.3