From 2ba430e0ba96258d62e6923b2d1f8085e1a22a13 Mon Sep 17 00:00:00 2001 From: Dhairya Gupta Date: Wed, 22 Jul 2026 14:10:41 -0700 Subject: [PATCH] Build Crubit explorer via Crubit cc_bindings_from_rs Bazel Binary PiperOrigin-RevId: 952323092 --- MODULE.bazel | 4 +- cc_bindings_from_rs/BUILD | 1 + .../cc_bindings_from_rs_sh_test.sh | 2 +- crubit_explorer/.bazelrc | 1 + crubit_explorer/.bazelversion | 1 + crubit_explorer/BUILD | 56 ++++--- crubit_explorer/Cargo.toml | 1 - crubit_explorer/MODULE.bazel | 26 ++- crubit_explorer/src/main.rs | 149 ++++++++++++++++++ patches/BUILD | 0 10 files changed, 212 insertions(+), 29 deletions(-) create mode 100644 crubit_explorer/.bazelrc create mode 100644 crubit_explorer/.bazelversion create mode 100644 patches/BUILD diff --git a/MODULE.bazel b/MODULE.bazel index 5b8c7c299..828c4b587 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -39,8 +39,8 @@ bazel_dep(name = "rules_foreign_cc", version = "0.15.0") crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate") crate.from_cargo( name = "crate_index", - lockfile = "//:Cargo.Bazel.lock", cargo_lockfile = "//:Cargo.lock", + lockfile = "//:Cargo.Bazel.lock", manifests = [ "//:Cargo.toml", ], @@ -52,7 +52,7 @@ crubit_toolchains.configure( llvm_version = "22.1.0", rust_version = "nightly/2026-07-01", ) -use_repo(crubit_toolchains, "llvm_toolchain", "rust_toolchains", "llvm_version_check") +use_repo(crubit_toolchains, "llvm_toolchain", "llvm_version_check", "rust_toolchains") register_toolchains( "@rust_toolchains//:all", diff --git a/cc_bindings_from_rs/BUILD b/cc_bindings_from_rs/BUILD index 6f7b03927..6428a4279 100644 --- a/cc_bindings_from_rs/BUILD +++ b/cc_bindings_from_rs/BUILD @@ -76,6 +76,7 @@ crubit_rust_binary( "not_run", "notap", ], + visibility = ["//visibility:public"], deps = [ ":cmdline", ":cpp_api_from_rust_lib", diff --git a/cc_bindings_from_rs/cc_bindings_from_rs_sh_test.sh b/cc_bindings_from_rs/cc_bindings_from_rs_sh_test.sh index 8c47e9ea1..e91831bc5 100755 --- a/cc_bindings_from_rs/cc_bindings_from_rs_sh_test.sh +++ b/cc_bindings_from_rs/cc_bindings_from_rs_sh_test.sh @@ -19,7 +19,7 @@ function delete_all_test_outputs() { rm -rf "$STDERR_PATH" "$STDOUT_PATH" "$H_OUT_PATH" "$RS_OUT_PATH" "$TARGET_JSON_PATH" } -readonly CC_BINDINGS_FROM_RS_PATH="${RUNFILES}/cc_bindings_from_rs/cc_bindings_from_rs" +readonly CC_BINDINGS_FROM_RS_PATH="${RUNFILES}/rules_crubit/cc_bindings_from_rs/cc_bindings_from_rs" readonly SYSROOT_PATH="${RUNFILES}/${G3_SYSROOT_PATH}" readonly DEFAULT_CLANG_FORMAT_EXE_PATH="${CRUBIT_CLANG_FORMAT_EXE_PATH}" diff --git a/crubit_explorer/.bazelrc b/crubit_explorer/.bazelrc new file mode 100644 index 000000000..c0224835f --- /dev/null +++ b/crubit_explorer/.bazelrc @@ -0,0 +1 @@ +import %workspace%/../.bazelrc \ No newline at end of file diff --git a/crubit_explorer/.bazelversion b/crubit_explorer/.bazelversion new file mode 100644 index 000000000..85f864fe8 --- /dev/null +++ b/crubit_explorer/.bazelversion @@ -0,0 +1 @@ +9.2.0 \ No newline at end of file diff --git a/crubit_explorer/BUILD b/crubit_explorer/BUILD index 66578f8cf..cdf7a15d4 100644 --- a/crubit_explorer/BUILD +++ b/crubit_explorer/BUILD @@ -2,8 +2,12 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +load( + "@rules_crubit//common:crubit_wrapper_macros_oss.bzl", + "crubit_rust_binary", + "crubit_rust_test", +) load("@rules_license//rules:license.bzl", "license") -load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test") package( default_applicable_licenses = [":license"], @@ -15,35 +19,51 @@ license( package_name = "crubit_explorer", ) -rust_binary( +crubit_rust_binary( name = "server", srcs = ["src/main.rs"], + data = [ + "@rules_crubit//cc_bindings_from_rs", + "@rules_rust//rust/toolchain:current_rustc_files", + "@rules_rust//rust/toolchain:current_rustc_lib_files", + ], edition = "2024", + env = { + "RUSTC_RUNFILES_PATH": "$(rlocationpath @rules_rust//rust/toolchain:current_rustc_files)", + }, visibility = ["//visibility:public"], deps = [ - "@crate_index//:axum", # v0_8 - "@crate_index//:base64", # v0_22 - "@crate_index//:serde", # v1 - "@crate_index//:serde_json", # v1 - "@crate_index//:tempfile", # v3 - "@crate_index//:tokio", # v1 - "@crate_index//:tower-http", + "@crubit_explorer_crates//:axum", + "@crubit_explorer_crates//:base64", + "@crubit_explorer_crates//:serde", + "@crubit_explorer_crates//:serde_json", + "@crubit_explorer_crates//:tempfile", + "@crubit_explorer_crates//:tokio", + "@rules_rust//tools/runfiles", ], ) -rust_test( +crubit_rust_test( name = "server_test", size = "small", srcs = ["src/main.rs"], + data = [ + "@rules_crubit//cc_bindings_from_rs", + "@rules_rust//rust/toolchain:current_rustc_files", + "@rules_rust//rust/toolchain:current_rustc_lib_files", + ], edition = "2024", + env = { + "RUSTC_RUNFILES_PATH": "$(rlocationpath @rules_rust//rust/toolchain:current_rustc_files)", + }, deps = [ - "@crate_index//:axum", # v0_8 - "@crate_index//:base64", # v0_22 - "@crate_index//:serde", # v1 - "@crate_index//:serde_json", # v1 - "@crate_index//:tempfile", # v3 - "@crate_index//:tokio", # v1 - "@crate_index//:tower", # v0_5 - "@crate_index//:tower-http", + "@crubit_explorer_crates//:axum", + "@crubit_explorer_crates//:base64", + "@crubit_explorer_crates//:serde", + "@crubit_explorer_crates//:serde_json", + "@crubit_explorer_crates//:tempfile", + "@crubit_explorer_crates//:tokio", + "@crubit_explorer_crates//:tower", + "@rules_rust//tools/runfiles", ], ) diff --git a/crubit_explorer/Cargo.toml b/crubit_explorer/Cargo.toml index 859b27ded..39657e3a3 100644 --- a/crubit_explorer/Cargo.toml +++ b/crubit_explorer/Cargo.toml @@ -15,7 +15,6 @@ serde_json = "1.0.150" tempfile = "3.27.0" tokio = { version = "1.52.3", features = ["full"] } tower = { version = "0.5.2", features = ["util"] } -tower-http = { version = "0.7.0", features = ["fs"] } [workspace] diff --git a/crubit_explorer/MODULE.bazel b/crubit_explorer/MODULE.bazel index 8f500900d..0298231d1 100644 --- a/crubit_explorer/MODULE.bazel +++ b/crubit_explorer/MODULE.bazel @@ -9,20 +9,32 @@ module( bazel_dep(name = "rules_rust", version = "0.71.3") bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep(name = "rules_crubit") +local_path_override( + module_name = "rules_crubit", + path = "..", +) -rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") -rust.toolchain( - edition = "2024", +crubit_toolchains = use_extension("@rules_crubit//bazel:extensions.bzl", "crubit_toolchains") +crubit_toolchains.configure( + llvm_version = "22.1.0", + rust_version = "nightly/2026-07-01", ) -use_repo(rust, "rust_toolchains") -register_toolchains("@rust_toolchains//:all") +register_toolchains("@rust_toolchains//:all", "@llvm_toolchain//:all") + +use_repo( + crubit_toolchains, + "llvm_toolchain", + "llvm_version_check", + "rust_toolchains", +) # Crate Universe to import dependencies from Cargo.toml crate = use_extension("@rules_rust//crate_universe:extensions.bzl", "crate") crate.from_cargo( - name = "crate_index", + name = "crubit_explorer_crates", cargo_lockfile = "//:Cargo.lock", manifests = ["//:Cargo.toml"], ) -use_repo(crate, "crate_index") +use_repo(crate, "crubit_explorer_crates") diff --git a/crubit_explorer/src/main.rs b/crubit_explorer/src/main.rs index d12234412..2c552b69f 100644 --- a/crubit_explorer/src/main.rs +++ b/crubit_explorer/src/main.rs @@ -4,14 +4,117 @@ use axum::{routing, Router}; use std::net::SocketAddr; +use std::path::PathBuf; use tokio::net::TcpListener; +use runfiles::Runfiles; + +const CC_BINDINGS_FROM_RS_RLOCATION: &str = + "rules_crubit/cc_bindings_from_rs/cc_bindings_from_rs"; + +fn get_cc_bindings_from_rs_path() -> Result> { + // Environment variable for location to cc_bindings_from_rs binary + if let Ok(env_path) = std::env::var("CC_BINDINGS_FROM_RS") + && let path = PathBuf::from(env_path) + && path.exists() + { + return Ok(path); + } + + // Check bazel runfiles tree + if let Ok(r) = Runfiles::create() + && let Some(path) = runfiles::rlocation!(r, CC_BINDINGS_FROM_RS_RLOCATION) + && path.exists() + { + return Ok(path); + } + + // Check if cc_bindings_from_rs is in the same directory as the executable + // This is useful when crubit_explorer is run in a tarball or Docker container with + // a specific directory structure + if let Ok(mut exe_path) = std::env::current_exe() { + exe_path.pop(); // Remove the executable name, leaving the directory + let adjacent_path = exe_path.join("cc_bindings_from_rs"); + if adjacent_path.exists() { + return Ok(adjacent_path); + } + } + + // Check system PATH + if let Some(path) = find_in_path("cc_bindings_from_rs") { + return Ok(path); + } + + Err("cc_bindings_from_rs binary not found via CC_BINDINGS_FROM_RS env var, Bazel runfiles, adjacent to executable, or in system PATH".into()) +} + +fn new_cc_bindings_from_rs_command() -> Result> { + let binary_path = get_cc_bindings_from_rs_path()?; + let mut cmd = std::process::Command::new(binary_path); + + let mut extra_lib_dirs = Vec::new(); + + // Check Bazel runfiles + if let Ok(r) = Runfiles::create() + && let Ok(rustc_runfiles_env) = std::env::var("RUSTC_RUNFILES_PATH") + && let Some(rustc_path) = runfiles::rlocation!(r, &rustc_runfiles_env) + { + let mut lib_dir = rustc_path; + lib_dir.pop(); // pop rustc + lib_dir.pop(); // pop bin + let lib_dir = lib_dir.join("lib"); + if lib_dir.exists() { + extra_lib_dirs.push(lib_dir); + } + } + + // Check adjacent lib directory to the current executable (useful in Docker/production tarball) + if let Ok(mut exe_path) = std::env::current_exe() { + exe_path.pop(); + let adjacent_lib = exe_path.join("lib"); + if adjacent_lib.exists() { + extra_lib_dirs.push(adjacent_lib); + } + } + + if !extra_lib_dirs.is_empty() { + const LIB_PATH_ENV: &str = cfg_select! { + target_os = "macos" => "DYLD_LIBRARY_PATH", + target_os = "windows" => "PATH", + _ => "LD_LIBRARY_PATH", + }; + + let mut paths = extra_lib_dirs; + if let Some(old_val) = std::env::var_os(LIB_PATH_ENV) { + paths.extend(std::env::split_paths(&old_val)); + } + + let new_val = std::env::join_paths(paths)?; + cmd.env(LIB_PATH_ENV, new_val); + } + + Ok(cmd) +} + +fn find_in_path(name: &str) -> Option { + let paths = std::env::var_os("PATH")?; + std::env::split_paths(&paths).find_map(|dir| { + let full_path = dir.join(name); + full_path.exists().then_some(full_path) + }) +} + fn app() -> Router { Router::new().route("/", routing::get(|| async { "Hello, World!" })) } #[tokio::main] async fn main() -> Result<(), Box> { + match get_cc_bindings_from_rs_path() { + Ok(path) => println!("cc_bindings_from_rs found at: {}", path.display()), + Err(err) => eprintln!("Error locating cc_bindings_from_rs: {}", err), + } + let app = app(); let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); @@ -43,4 +146,50 @@ mod tests { let body = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap(); assert_eq!(&body[..], b"Hello, World!"); } + + #[tokio::test] + async fn test_cc_bindings_from_rs_help() { + let mut cmd = new_cc_bindings_from_rs_command() + .expect("Failed to create cc_bindings_from_rs command"); + let output = cmd.arg("--help").output().expect("Failed to execute cc_bindings_from_rs"); + + assert!(output.status.success()); + let stdout = String::from_utf8(output.stdout).unwrap(); + assert!(stdout.contains("Generates C++ bindings for a Rust crate")); + } + + #[tokio::test] + async fn test_generate_bindings() { + let temp_dir = tempfile::tempdir().expect("Failed to create temp dir"); + let rs_input = temp_dir.path().join("input.rs"); + let h_out = temp_dir.path().join("output.h"); + let rs_out = temp_dir.path().join("output.rs"); + + std::fs::write(&rs_input, b"#[no_mangle] pub extern \"C\" fn foo() {}") + .expect("Failed to write input file"); + + let mut cmd = new_cc_bindings_from_rs_command() + .expect("Failed to create cc_bindings_from_rs command"); + cmd.arg(format!("--h-out={}", h_out.display())) + .arg(format!("--rs-out={}", rs_out.display())) + .arg("--crubit-support-path-format=") + .arg("--") + .arg(&rs_input) + .arg("--crate-type=lib"); + + let output = cmd.output().expect("Failed to execute cc_bindings_from_rs"); + + let stderr = String::from_utf8_lossy(&output.stderr); + + assert!(output.status.success(), "Command failed with stderr: {}", stderr); + assert!(h_out.exists()); + assert!(rs_out.exists()); + + let h_content = std::fs::read_to_string(&h_out).expect("Failed to read h_out"); + let rs_content = std::fs::read_to_string(&rs_out).expect("Failed to read rs_out"); + + assert!(!h_content.is_empty()); + assert!(!rs_content.is_empty()); + assert!(h_content.contains("foo")); + } } diff --git a/patches/BUILD b/patches/BUILD new file mode 100644 index 000000000..e69de29bb