Преглед на файлове

Allow GoogleTest tests to locate their executable path. (#3992)

This also factors out the code for doing this location from the driver
to a tiny helper library.

The motivation for this is letting tests locate data files like the
prelude or other files needed by the toolchain more easily. A subsequent
PR will use this heavily in the Clang runner and related logic for
example.
Chandler Carruth преди 1 година
родител
ревизия
582b2c04dd
променени са 10 файла, в които са добавени 198 реда и са изтрити 10 реда
  1. 21 0
      common/BUILD
  2. 23 0
      common/exe_path.cpp
  3. 21 0
      common/exe_path.h
  4. 47 0
      common/exe_path_test.cpp
  5. 15 0
      testing/base/BUILD
  6. 23 0
      testing/base/gtest_main.cpp
  7. 20 0
      testing/base/gtest_main.h
  8. 25 0
      testing/base/gtest_main_test.cpp
  9. 1 0
      toolchain/driver/BUILD
  10. 2 10
      toolchain/driver/driver_main.cpp

+ 21 - 0
common/BUILD

@@ -123,6 +123,27 @@ cc_test(
     ],
 )
 
+cc_library(
+    name = "exe_path",
+    srcs = ["exe_path.cpp"],
+    hdrs = ["exe_path.h"],
+    deps = [
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_test(
+    name = "exe_path_test",
+    size = "small",
+    srcs = ["exe_path_test.cpp"],
+    deps = [
+        ":exe_path",
+        "//testing/base:gtest_main",
+        "@googletest//:gtest",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
 cc_library(
     name = "hashing",
     srcs = ["hashing.cpp"],

+ 23 - 0
common/exe_path.cpp

@@ -0,0 +1,23 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "common/exe_path.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Program.h"
+
+namespace Carbon {
+
+auto FindExecutablePath(llvm::StringRef argv0) -> std::string {
+  if (!llvm::sys::fs::exists(argv0)) {
+    if (llvm::ErrorOr<std::string> path = llvm::sys::findProgramByName(argv0)) {
+      return std::move(*path);
+    }
+  }
+
+  return argv0.str();
+}
+
+}  // namespace Carbon

+ 21 - 0
common/exe_path.h

@@ -0,0 +1,21 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_COMMON_EXE_PATH_H_
+#define CARBON_COMMON_EXE_PATH_H_
+
+#include "llvm/ADT/StringRef.h"
+
+namespace Carbon {
+
+// Computes the executable path for the given `argv[0]` value form `main`.
+//
+// A simplistic approach -- if the provided string isn't already a valid path,
+// we look it up in the PATH environment variable. Doesn't resolve any symlinks
+// and if it fails, simply returns the provided `argv0`.
+auto FindExecutablePath(llvm::StringRef argv0) -> std::string;
+
+}  // namespace Carbon
+
+#endif  // CARBON_COMMON_EXE_PATH_H_

+ 47 - 0
common/exe_path_test.cpp

@@ -0,0 +1,47 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "common/exe_path.h"
+
+#include <gtest/gtest.h>
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace Carbon {
+namespace {
+
+TEST(ExePath, FailureFallback) {
+  llvm::SmallString<128> path = llvm::StringRef(getenv("TEST_TMPDIR"));
+  llvm::sys::path::append(path, "non_existant_binary");
+  std::string exe_path = FindExecutablePath(path);
+  EXPECT_EQ(path, exe_path);
+}
+
+TEST(ExePath, File) {
+  llvm::SmallString<128> path = llvm::StringRef(getenv("TEST_TMPDIR"));
+  llvm::sys::path::append(path, "test_binary");
+  int fd = -1;
+  std::error_code ec = llvm::sys::fs::openFileForWrite(path, fd);
+  ASSERT_TRUE(!ec) << "Error code: " << ec;
+  close(fd);
+
+  std::string exe_path = FindExecutablePath(path);
+  EXPECT_EQ(path, exe_path);
+}
+
+TEST(ExePath, PathLookup) {
+  // TODO: This is not likely to work well on Windows (outside of WSL). But some
+  // of that may be hidden by Bazel's test environment. Regardless, we should
+  // revisit this when we have good coverage of Windows build with something
+  // appropriate for that platform.
+  std::string exe_path = FindExecutablePath("bash");
+  EXPECT_NE(exe_path, "bash");
+  EXPECT_TRUE(llvm::sys::fs::exists(exe_path));
+  EXPECT_TRUE(llvm::sys::fs::can_execute(exe_path));
+}
+
+}  // namespace
+}  // namespace Carbon

+ 15 - 0
testing/base/BUILD

@@ -19,9 +19,24 @@ cc_library(
     name = "gtest_main",
     testonly = 1,
     srcs = ["gtest_main.cpp"],
+    hdrs = ["gtest_main.h"],
     deps = [
+        "//common:check",
+        "//common:exe_path",
         "//common:init_llvm",
         "@googletest//:gtest",
+        "@llvm-project//llvm:Support",
+    ],
+)
+
+cc_test(
+    name = "gtest_main_test",
+    size = "small",
+    srcs = ["gtest_main_test.cpp"],
+    deps = [
+        ":gtest_main",
+        "@googletest//:gtest",
+        "@llvm-project//llvm:Support",
     ],
 )
 

+ 23 - 0
testing/base/gtest_main.cpp

@@ -2,11 +2,34 @@
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
+#include "testing/base/gtest_main.h"
+
 #include <gtest/gtest.h>
 
+#include <string>
+
+#include "common/check.h"
+#include "common/exe_path.h"
 #include "common/init_llvm.h"
 
+static bool after_main = false;
+static llvm::StringRef exe_path;
+
+namespace Carbon::Testing {
+
+auto GetTestExePath() -> llvm::StringRef {
+  CARBON_CHECK(after_main)
+      << "Must not query the executable path until after `main` is entered!";
+  return exe_path;
+}
+
+}  // namespace Carbon::Testing
+
 auto main(int argc, char** argv) -> int {
+  std::string exe_path_storage = Carbon::FindExecutablePath(argv[0]);
+  exe_path = exe_path_storage;
+  after_main = true;
+
   Carbon::InitLLVM init_llvm(argc, argv);
   testing::InitGoogleTest(&argc, argv);
   return RUN_ALL_TESTS();

+ 20 - 0
testing/base/gtest_main.h

@@ -0,0 +1,20 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_TESTING_BASE_GTEST_MAIN_H_
+#define CARBON_TESTING_BASE_GTEST_MAIN_H_
+
+#include "llvm/ADT/StringRef.h"
+
+// When using the Carbon `main` function for GoogleTest, we export some extra
+// information about the test binary that can be accessed with this header.
+
+namespace Carbon::Testing {
+
+// The executable path of the test binary.
+auto GetTestExePath() -> llvm::StringRef;
+
+}  // namespace Carbon::Testing
+
+#endif  // CARBON_TESTING_BASE_GTEST_MAIN_H_

+ 25 - 0
testing/base/gtest_main_test.cpp

@@ -0,0 +1,25 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "testing/base/gtest_main.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "llvm/Support/FileSystem.h"
+
+namespace Carbon::Testing {
+namespace {
+
+using ::testing::StrNe;
+
+TEST(TestExePathTest, Test) {
+  llvm::StringRef exe_path = GetTestExePath();
+  EXPECT_THAT(exe_path, StrNe(""));
+  EXPECT_TRUE(llvm::sys::fs::exists(exe_path));
+  EXPECT_TRUE(llvm::sys::fs::can_execute(exe_path));
+}
+
+}  // namespace
+}  // namespace Carbon::Testing

+ 1 - 0
toolchain/driver/BUILD

@@ -114,6 +114,7 @@ cc_binary(
         ":driver",
         "//common:all_llvm_targets",
         "//common:bazel_working_dir",
+        "//common:exe_path",
         "//common:init_llvm",
         "@llvm-project//llvm:Support",
     ],

+ 2 - 10
toolchain/driver/driver_main.cpp

@@ -5,11 +5,11 @@
 #include <cstdlib>
 
 #include "common/bazel_working_dir.h"
+#include "common/exe_path.h"
 #include "common/init_llvm.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Path.h"
-#include "llvm/Support/Program.h"
 #include "toolchain/driver/driver.h"
 
 auto main(int argc, char** argv) -> int {
@@ -19,15 +19,7 @@ auto main(int argc, char** argv) -> int {
     return EXIT_FAILURE;
   }
 
-  // Find the executable without resolving symlinks. Do a PATH lookup if argv[0]
-  // isn't a valid path.
-  llvm::SmallString<128> exe_path(argv[0]);
-  if (!llvm::sys::fs::exists(exe_path)) {
-    if (llvm::ErrorOr<std::string> path =
-            llvm::sys::findProgramByName(exe_path)) {
-      exe_path = *path;
-    }
-  }
+  std::string exe_path = Carbon::FindExecutablePath(argv[0]);
 
   Carbon::SetWorkingDirForBazel();