diff options
-rw-r--r-- | src/test/py/bazel/runfiles_test.py | 88 | ||||
-rwxr-xr-x | src/test/shell/bazel/bazel_windows_example_test.sh | 3 | ||||
-rw-r--r-- | src/tools/launcher/launcher.cc | 51 |
3 files changed, 127 insertions, 15 deletions
diff --git a/src/test/py/bazel/runfiles_test.py b/src/test/py/bazel/runfiles_test.py index cda4bf11be..e3f697cc55 100644 --- a/src/test/py/bazel/runfiles_test.py +++ b/src/test/py/bazel/runfiles_test.py @@ -172,6 +172,94 @@ class RunfilesTest(test_base.TestBase): self.fail("lines(%s): %s" % (lang[0], lines)) self.assertEqual(lines[0], "data for " + lang[2]) + def testRunfilesLibrariesFindRunfilesWithRunfilesManifestEnvvar(self): + for s, t in [ + ("WORKSPACE.mock", "WORKSPACE"), + ("bar/BUILD.mock", "bar/BUILD"), + # Note: do not test Python here, because py_binary always needs a + # runfiles tree, even on Windows, because it needs __init__.py files in + # every directory where there may be importable modules, so Bazel always + # needs to create a runfiles tree for py_binary. + ("bar/Bar.java", "bar/Bar.java"), + ("bar/bar-java-data.txt", "bar/bar-java-data.txt"), + ]: + self.CopyFile( + self.Rlocation( + "io_bazel/src/test/py/bazel/testdata/runfiles_test/" + s), t) + + exit_code, stdout, stderr = self.RunBazel(["info", "bazel-bin"]) + self.AssertExitCode(exit_code, 0, stderr) + bazel_bin = stdout[0] + + exit_code, _, stderr = self.RunBazel( + ["build", "--experimental_enable_runfiles=no", "//bar:bar-java"]) + self.AssertExitCode(exit_code, 0, stderr) + + if test_base.TestBase.IsWindows(): + bin_path = os.path.join(bazel_bin, "bar/bar-java.exe") + else: + bin_path = os.path.join(bazel_bin, "bar/bar-java") + + manifest_path = bin_path + ".runfiles_manifest" + self.assertTrue(os.path.exists(bin_path)) + self.assertTrue(os.path.exists(manifest_path)) + + # Create a copy of the runfiles manifest, replacing + # "bar/bar-java-data.txt" with a custom file. + mock_bar_dep = self.ScratchFile("bar-java-mockdata.txt", ["mock java data"]) + if test_base.TestBase.IsWindows(): + # Runfiles manifests use forward slashes as path separators, even on + # Windows. + mock_bar_dep = mock_bar_dep.replace("\\", "/") + manifest_key = "foo_ws/bar/bar-java-data.txt" + mock_manifest_line = manifest_key + " " + mock_bar_dep + with open(manifest_path, "rt") as f: + # Only rstrip newlines. Do not rstrip() completely, because that would + # remove spaces too. This is necessary in order to have at least one + # space in every manifest line. + # Some manifest entries don't have any path after this space, namely the + # "__init__.py" entries. (Bazel writes such manifests on every + # platform). The reason is that these files are never symlinks in the + # runfiles tree, Bazel actually creates empty __init__.py files (again + # on every platform). However to keep these manifest entries correct, + # they need to have a space character. + # We could probably strip thses lines completely, but this test doesn't + # aim to exercise what would happen in that case. + mock_manifest_data = [ + mock_manifest_line + if line.split(" ", 1)[0] == manifest_key else line.rstrip("\n\r") + for line in f + ] + + substitute_manifest = self.ScratchFile("mock-java.runfiles/MANIFEST", + mock_manifest_data) + + exit_code, stdout, stderr = self.RunProgram( + [bin_path], + env_remove=set(["RUNFILES_DIR"]), + env_add={ + # On Linux/macOS, the Java launcher picks up JAVA_RUNFILES and + # ignores RUNFILES_MANIFEST_FILE. + "JAVA_RUNFILES": substitute_manifest[:-len("/MANIFEST")], + # On Windows, the Java launcher picks up RUNFILES_MANIFEST_FILE. + "RUNFILES_MANIFEST_FILE": substitute_manifest, + "RUNFILES_MANIFEST_ONLY": "1", + "TEST_SRCDIR": "__ignore_me__", + }) + + self.AssertExitCode(exit_code, 0, stderr) + if len(stdout) < 2: + self.fail("stdout: %s" % stdout) + self.assertEqual(stdout[0], "Hello Java Bar!") + six.assertRegex(self, stdout[1], "^rloc=" + mock_bar_dep) + self.assertNotIn("__ignore_me__", stdout[1]) + + with open(stdout[1].split("=", 1)[1], "r") as f: + lines = [l.strip() for l in f.readlines()] + if len(lines) != 1: + self.fail("lines: %s" % lines) + self.assertEqual(lines[0], "mock java data") + if __name__ == "__main__": unittest.main() diff --git a/src/test/shell/bazel/bazel_windows_example_test.sh b/src/test/shell/bazel/bazel_windows_example_test.sh index 53070e0e94..97de66108d 100755 --- a/src/test/shell/bazel/bazel_windows_example_test.sh +++ b/src/test/shell/bazel/bazel_windows_example_test.sh @@ -53,6 +53,9 @@ function assert_binary_run_from_subdir() { cd x && unset JAVA_RUNFILES && unset TEST_SRCDIR && + unset RUNFILES_MANIFEST_FILE && + unset RUNFILES_MANIFEST_ONLY && + unset RUNFILES_DIR && assert_binary_run "../$1" "$2" ) } diff --git a/src/tools/launcher/launcher.cc b/src/tools/launcher/launcher.cc index 11bf86b57a..a657fa41e7 100644 --- a/src/tools/launcher/launcher.cc +++ b/src/tools/launcher/launcher.cc @@ -44,27 +44,48 @@ BinaryLauncherBase::BinaryLauncherBase( ParseManifestFile(&this->manifest_file_map, this->manifest_file); } -string BinaryLauncherBase::FindManifestFile(const char* argv0) { - // Get the name of the binary - string binary = GetBinaryPathWithExtension(argv0); +static bool FindManifestFileImpl(const char* argv0, string* result) { + // If this binary X runs as the data-dependency of some other binary Y, then + // X has no runfiles manifest/directory and should use Y's. + if (GetEnv("RUNFILES_MANIFEST_FILE", result) && + DoesFilePathExist(result->c_str())) { + return true; + } - // The path will be set as the RUNFILES_MANIFEST_FILE envvar and used by the - // shell script, so let's convert backslashes to forward slashes. - std::replace(binary.begin(), binary.end(), '\\', '/'); + string directory; + if (GetEnv("RUNFILES_DIR", &directory)) { + *result = directory + "/MANIFEST"; + if (DoesFilePathExist(result->c_str())) { + return true; + } + } - // Try to find <path to binary>.runfiles/MANIFEST - string manifest_file = binary + ".runfiles/MANIFEST"; - if (DoesFilePathExist(manifest_file.c_str())) { - return manifest_file; + // If this binary X runs by itself (not as a data-dependency of another + // binary), then look for the manifest in a runfiles directory next to the + // main binary, then look for it (the manifest) next to the main binary. + directory = GetBinaryPathWithExtension(argv0) + ".runfiles"; + *result = directory + "/MANIFEST"; + if (DoesFilePathExist(result->c_str())) { + return true; } - // Also try to check if <path to binary>.runfiles_manifest exists - manifest_file = binary + ".runfiles_manifest"; - if (DoesFilePathExist(manifest_file.c_str())) { - return manifest_file; + *result = directory + "_manifest"; + if (DoesFilePathExist(result->c_str())) { + return true; } - die("Couldn't find MANIFEST file under %s.runfiles\\", binary.c_str()); + return false; +} + +string BinaryLauncherBase::FindManifestFile(const char* argv0) { + string manifest_file; + if (!FindManifestFileImpl(argv0, &manifest_file)) { + die("Couldn't find runfiles manifest file."); + } + // The path will be set as the RUNFILES_MANIFEST_FILE envvar and used by the + // shell script, so let's convert backslashes to forward slashes. + std::replace(manifest_file.begin(), manifest_file.end(), '\\', '/'); + return manifest_file; } string BinaryLauncherBase::GetRunfilesPath() const { |