Bundle the Centipede binary with the subprocess engine controller.

PiperOrigin-RevId: 994436974
diff --git a/centipede/BUILD b/centipede/BUILD
index 3c045ca..ca7783e 100644
--- a/centipede/BUILD
+++ b/centipede/BUILD
@@ -15,6 +15,7 @@
 # Description:
 #   Centipede: an experimental distributed fuzzing engine.
 
+load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
 load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
 load("@rules_cc//cc:cc_library.bzl", "cc_library")
 load("@rules_cc//cc:cc_static_library.bzl", "cc_static_library")
@@ -1004,10 +1005,26 @@
     ],
 )
 
+bool_flag(
+    name = "bundle_controller_binary",
+    build_setting_default = True,
+)
+
+config_setting(
+    name = "is_controller_binary_bundled",
+    flag_values = {":bundle_controller_binary": "True"},
+)
+
 cc_library(
     name = "engine_controller_with_subprocess",
     srcs = ["engine_controller_with_subprocess.cc"],
     hdrs = ["engine_controller_abi.h"],
+    data = select({
+        ":is_controller_binary_bundled": [
+            ":centipede_uninstrumented",
+        ],
+        "//conditions:default": [],
+    }),
     deps = [
         "@com_google_fuzztest//centipede:engine_abi",
         "@com_google_fuzztest//fuzztest/internal:escaping",
diff --git a/centipede/centipede_test.cc b/centipede/centipede_test.cc
index 3c45b83..1d7d830 100644
--- a/centipede/centipede_test.cc
+++ b/centipede/centipede_test.cc
@@ -1551,17 +1551,13 @@
 }
 
 TEST_F(CentipedeWithTemporaryLocalDir, EngineWorksInStandaloneMode) {
-  const std::string centipede_path =
-      GetDataDependencyFilepath("centipede/centipede");
   const std::string test_binary_path = GetDataDependencyFilepath(
       "centipede/testing/test_binary_for_engine_testing");
   // Create a temporary dir and enter it for running the test binary, because
   // the test binary uses CWD as the engine workdir.
   TempCorpusDir tmp_dir{test_info_->name()};
   const auto test_command =
-      absl::StrCat("cd ", tmp_dir.path().string(),
-                   " && env FUZZTEST_CENTIPEDE_BINARY_PATH=", centipede_path,
-                   " ", test_binary_path);
+      absl::StrCat("cd ", tmp_dir.path().string(), " && ", test_binary_path);
   EXPECT_DEATH(
       [&] {
         const int status = std::system(test_command.c_str());
diff --git a/centipede/engine_controller_with_subprocess.cc b/centipede/engine_controller_with_subprocess.cc
index 2ffc89d..078cd61 100644
--- a/centipede/engine_controller_with_subprocess.cc
+++ b/centipede/engine_controller_with_subprocess.cc
@@ -14,25 +14,64 @@
 
 #include <sys/wait.h>
 
+#include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <filesystem>  // NOLINT
 #include <string>
+#include <system_error>  // NOLINT
 
 #include "./centipede/engine_abi.h"
 #include "./centipede/engine_controller_abi.h"
 #include "./fuzztest/internal/escaping.h"
 
+namespace {
+
 using fuzztest::internal::ShellEscape;
 
+std::string GetBundledCentipedeBinaryPath() {
+  constexpr const char* kBundledCentipedePathSuffix =
+  "centipede/centipede_uninstrumented";
+  const char* test_workspace = std::getenv("TEST_WORKSPACE");
+  if (test_workspace == nullptr) {
+    test_workspace = "_main";
+  }
+  std::string runfiles_dir;
+  if (const char* test_srcdir = std::getenv("TEST_SRCDIR");
+      test_srcdir != nullptr) {
+    runfiles_dir = test_srcdir;
+  }
+  std::error_code ec;
+  if (!runfiles_dir.empty()) {
+    const auto path = std::filesystem::path{runfiles_dir} / test_workspace /
+                      kBundledCentipedePathSuffix;
+    if (std::filesystem::exists(path, ec)) {
+      return path.string();
+    }
+  }
+  return "";
+}
+
+}  // namespace
+
 FuzzTestControllerStatus FuzzTestControllerRun(
     const FuzzTestAdapterManager* manager, const FuzzTestBytesViews* flags) {
-  // TODO(xinhaoyuan): Use the FuzzTest controller env var later.
   static auto centipede_binary_path = []() -> const char* {
-    const char* env = std::getenv("FUZZTEST_CENTIPEDE_BINARY_PATH");
-    if (env == nullptr) return nullptr;
-    return strdup(env);
+    // TODO(xinhaoyuan): Use the FuzzTest controller env var later.
+    if (const char* env = std::getenv("FUZZTEST_CENTIPEDE_BINARY_PATH");
+        env != nullptr) {
+      return strdup(env);
+    }
+    const std::string bundled_path = GetBundledCentipedeBinaryPath();
+    if (!bundled_path.empty()) {
+      return strdup(bundled_path.c_str());
+    }
+    return nullptr;
   }();
   if (centipede_binary_path == nullptr) {
+    fprintf(stderr,
+            "Failed to locate the controller binary - please specify the env "
+            "var `FUZZTEST_CENTIPEDE_BINARY_PATH`\n");
     return kFuzzTestControllerFailure;
   }
   std::string command;
diff --git a/rust/e2e_tests/BUILD b/rust/e2e_tests/BUILD
index 6eeab33..bc706bd 100644
--- a/rust/e2e_tests/BUILD
+++ b/rust/e2e_tests/BUILD
@@ -33,7 +33,6 @@
     name = "standalone_mode_test",
     srcs = ["standalone_mode_test.rs"],
     data = [
-        "@com_google_fuzztest//centipede:centipede_uninstrumented",
         "@com_google_fuzztest//rust/e2e_tests/testdata:fuzztest_main",
         "@com_google_fuzztest//rust/e2e_tests/testdata:standalone_fuzz_tests_bin",
     ],
@@ -100,7 +99,6 @@
     name = "replay_test",
     srcs = ["replay_test.rs"],
     data = [
-        "@com_google_fuzztest//centipede:centipede_uninstrumented",
         "@com_google_fuzztest//rust/e2e_tests/testdata:replay_fuzz_tests_bin",
     ],
     edition = "2024",
diff --git a/rust/e2e_tests/replay_test.rs b/rust/e2e_tests/replay_test.rs
index 80c37ed..388bbb4 100644
--- a/rust/e2e_tests/replay_test.rs
+++ b/rust/e2e_tests/replay_test.rs
@@ -51,7 +51,6 @@
         .env("FUZZTEST_CONTINUE_AFTER_CRASH", "true")
         .env("FUZZTEST_CORPUS_DB", &db_dir)
         .env("FUZZTEST_WORKDIR_ROOT", &workdir_root_dir)
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .status()
         .expect("Failed to spawn binary");
 
@@ -64,7 +63,6 @@
         .env("FUZZTEST_LIST_CRASH_IDS", "true")
         .env("FUZZTEST_LIST_CRASH_IDS_FILE", &crash_ids_file)
         .env("FUZZTEST_CORPUS_DB", &db_dir)
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .status()
         .expect("Failed to execute target binary to list crash IDs");
     expect_true!(status.success());
@@ -87,7 +85,6 @@
             .arg("--nocapture")
             .env("FUZZTEST_REPLAY_ID", crash_id)
             .env("FUZZTEST_CORPUS_DB", &db_dir)
-            .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
             .stdout(Stdio::piped())
             .stderr(Stdio::piped())
             .spawn()
@@ -131,7 +128,6 @@
         .env("FUZZTEST_CONTINUE_AFTER_CRASH", "true")
         .env("FUZZTEST_CORPUS_DB", &db_dir)
         .env("FUZZTEST_WORKDIR_ROOT", &workdir_root_dir)
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .status()
         .expect("Failed to spawn binary");
 
@@ -141,7 +137,6 @@
         .arg("--exact")
         .env("FUZZTEST_REPLAY_FINDINGS", "true")
         .env("FUZZTEST_CORPUS_DB", &db_dir)
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .stdout(Stdio::piped())
         .stderr(Stdio::piped())
         .spawn()
diff --git a/rust/e2e_tests/standalone_mode_test.rs b/rust/e2e_tests/standalone_mode_test.rs
index 2ca7acc..f0e0dde 100644
--- a/rust/e2e_tests/standalone_mode_test.rs
+++ b/rust/e2e_tests/standalone_mode_test.rs
@@ -27,7 +27,6 @@
         .arg(test_name)
         .env("FUZZTEST_FUZZ_FOR", "3s")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("RUST_TEST_NOCAPTURE", "1")
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
@@ -39,8 +38,8 @@
 
     // Assert that Centipede actually ran the test binary, and forwarded the worker log.
     // We check for "LOG: STANDALONE_VALIDATION_WORKER_EXECUTED". The prefix "LOG: " indicates that
-    // this output is from centipede forwarding the output of the worker (due to --print_runner_log),
-    // whereas the rest is from the fuzztest itself.
+    // this output is from centipede forwarding the output of the worker (due to
+    // --print_runner_log), whereas the rest is from the fuzztest itself.
     expect_that!(
         stderr,
         matchers::contains_substring("LOG: STANDALONE_VALIDATION_WORKER_EXECUTED")
@@ -57,7 +56,6 @@
         .arg(test_name)
         .env("FUZZTEST_FUZZ_FOR", "3s")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("RUST_TEST_NOCAPTURE", "1")
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
@@ -85,7 +83,6 @@
         .arg(test_name)
         .env("FUZZTEST_FUZZ_FOR", "15s")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("RUST_TEST_NOCAPTURE", "1")
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
@@ -111,7 +108,6 @@
         .env("FUZZTEST_FUZZ_FOR", "15s")
         .env("FUZZTEST_CONTINUE_AFTER_CRASH", "true")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("RUST_TEST_NOCAPTURE", "1")
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
@@ -138,7 +134,6 @@
         .env("FUZZTEST_FUZZ_FOR", "5s")
         .env("FUZZTEST_JOBS", "4")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("RUST_TEST_NOCAPTURE", "1")
         .stdout(std::process::Stdio::piped())
         .stderr(std::process::Stdio::piped())
@@ -215,7 +210,6 @@
         .env("FUZZTEST_REPLAY_CORPUS_FOR", "3s")
         .env("FUZZTEST_TIME_BUDGET_TYPE", "per-test")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("FUZZTEST_CORPUS_DB", corpus_db.to_str().unwrap())
         .env("FUZZTEST_WORKDIR_ROOT", workdir_root.to_str().unwrap())
         .env("RUST_TEST_NOCAPTURE", "1")
@@ -307,7 +301,6 @@
         .env("FUZZTEST_REPLAY_CORPUS_FOR", "3s")
         .env("FUZZTEST_TIME_BUDGET_TYPE", "total")
         .env("FUZZTEST_PRINT_SUBPROCESS_LOG", "true")
-        .env("FUZZTEST_CENTIPEDE_BINARY_PATH", &fixture.centipede_path)
         .env("FUZZTEST_CORPUS_DB", corpus_db.to_str().unwrap())
         .env("FUZZTEST_WORKDIR_ROOT", workdir_root.to_str().unwrap())
         .env("RUST_TEST_NOCAPTURE", "1")