Просмотр исходного кода

Added GetRunfilesFile() to be able to reliably determine prelude location under various invocation scenarios

The logic is using bazel Runfiles API per Jon's suggestion.

One of the scenarios used by the fuzzing framework is to place the binary and its runfiles into a folder, resulting in a directory layout like this:

[temp dir]
fuzzer
fuzzer.runfiles/

Tested:
bazel test -c opt explorer/fuzzing:explorer_fuzzer
bazel build -c opt explorer/fuzzing:explorer_fuzzer + run the binary from various locations
pk19604014 4 лет назад
Родитель
Сommit
150057f260

+ 2 - 0
explorer/fuzzing/BUILD

@@ -25,11 +25,13 @@ cc_library(
     hdrs = ["fuzzer_util.h"],
     deps = [
         "//common:check",
+        "//common:error",
         "//common/fuzzing:carbon_cc_proto",
         "//common/fuzzing:proto_to_carbon_lib",
         "//explorer/interpreter:exec_program",
         "//explorer/syntax",
         "//explorer/syntax:prelude",
+        "@bazel_tools//tools/cpp/runfiles",
         "@com_google_protobuf//:protobuf_headers",
         "@llvm-project//llvm:Support",
     ],

+ 24 - 1
explorer/fuzzing/fuzzer_util.cpp

@@ -9,7 +9,10 @@
 #include "explorer/interpreter/exec_program.h"
 #include "explorer/syntax/parse.h"
 #include "explorer/syntax/prelude.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
+#include "tools/cpp/runfiles/runfiles.h"
 
 namespace Carbon {
 
@@ -21,6 +24,23 @@ fn Main() -> i32 {
 }
 )";
 
+auto Internal::GetRunfilesFile(const std::string& file)
+    -> ErrorOr<std::string> {
+  using bazel::tools::cpp::runfiles::Runfiles;
+  std::string error;
+  // `Runfiles::Create()` fails if passed an empty `argv0`.
+  std::unique_ptr<Runfiles> runfiles(Runfiles::Create(
+      /*argv0=*/llvm::sys::fs::getMainExecutable(nullptr, nullptr), &error));
+  if (runfiles == nullptr) {
+    return Error(error);
+  }
+  std::string full_path = runfiles->Rlocation(file);
+  if (!llvm::sys::fs::exists(full_path)) {
+    return ErrorBuilder() << full_path << " doesn't exist";
+  }
+  return full_path;
+}
+
 auto ProtoToCarbonWithMain(const Fuzzing::CompilationUnit& compilation_unit)
     -> std::string {
   const bool has_main = std::any_of(
@@ -43,7 +63,10 @@ void ParseAndExecute(const Fuzzing::CompilationUnit& compilation_unit) {
     llvm::errs() << "Parsing failed: " << ast.error().message() << "\n";
     return;
   }
-  AddPrelude("explorer/data/prelude.carbon", &arena, &ast->declarations);
+  const ErrorOr<std::string> prelude_path =
+      Internal::GetRunfilesFile("carbon/explorer/data/prelude.carbon");
+  CHECK(prelude_path.ok()) << prelude_path.error().message();
+  AddPrelude(*prelude_path, &arena, &ast->declarations);
   const ErrorOr<int> result =
       ExecProgram(&arena, *ast, /*trace_stream=*/std::nullopt);
   if (!result.ok()) {

+ 8 - 0
explorer/fuzzing/fuzzer_util.h

@@ -5,6 +5,7 @@
 #ifndef EXPLORER_FUZZING_FUZZER_UTIL_H_
 #define EXPLORER_FUZZING_FUZZER_UTIL_H_
 
+#include "common/error.h"
 #include "common/fuzzing/carbon.pb.h"
 
 namespace Carbon {
@@ -17,6 +18,13 @@ auto ProtoToCarbonWithMain(const Fuzzing::CompilationUnit& compilation_unit)
 // Parses and executes a fuzzer-generated program.
 void ParseAndExecute(const Fuzzing::CompilationUnit& compilation_unit);
 
+namespace Internal {
+
+// Returns a full path for a file under bazel runfiles.
+// Exposed for testing.
+auto GetRunfilesFile(const std::string& file) -> ErrorOr<std::string>;
+
+}  // namespace Internal
 }  // namespace Carbon
 
 #endif  // EXPLORER_FUZZING_FUZZER_UTIL_H_

+ 13 - 0
explorer/fuzzing/fuzzer_util_test.cpp

@@ -36,6 +36,19 @@ TEST(FuzzerUtilTest, RunFuzzerOnCorpus) {
   EXPECT_GT(file_count, 0);
 }
 
+TEST(FuzzerUtilTest, GetRunfilesFile) {
+  EXPECT_THAT(*Internal::GetRunfilesFile(
+                  "carbon/explorer/fuzzing/fuzzer_corpus/empty.textproto"),
+              testing::EndsWith("/explorer/fuzzing/fuzzer_corpus/"
+                                "empty.textproto"));
+  EXPECT_THAT(Internal::GetRunfilesFile(
+                  "explorer/fuzzing/fuzzer_corpus/empty.textproto")
+                  .error()
+                  .message(),
+              testing::EndsWith("fuzzer_util_test.runfiles/explorer/fuzzing/"
+                                "fuzzer_corpus/empty.textproto doesn't exist"));
+}
+
 }  // namespace
 }  // namespace Carbon::Testing
 

+ 3 - 0
scripts/fix_cc_deps.py

@@ -36,6 +36,9 @@ EXTERNAL_REPOS: Dict[str, Callable[[str], str]] = {
     "@com_google_libprotobuf_mutator": lambda x: re.sub(
         "^(.*:)", "libprotobuf_mutator/", x
     ),
+    # @bazel_tools//tools/cpp/runfiles:runfiles.h ->
+    #   tools/cpp/runfiles/runfiles.h
+    "@bazel_tools": lambda x: re.sub(":", "/", x),
 }
 
 # TODO: proto rules are aspect-based and their generated files don't show up in