Skip to content

Commit 831c9fb

Browse files
thunderseethecopybara-github
authored andcommitted
Add standard library bindings in the subcommand.
Included in this change is a fix for Pat types, which was uncovered by binding the stdlib. Lookup the sysroot from rustc and use that to find the rmetas for the standard library. Generate bindings to those standard library metas and pass them to other crates in the graph. PiperOrigin-RevId: 911371167
1 parent 67163eb commit 831c9fb

2 files changed

Lines changed: 152 additions & 4 deletions

File tree

cc_bindings_from_rs/cargo-cpp_api_from_rust.rs

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,134 @@ impl BindingGenerationContext {
225225
Ok(Self { pkg_to_artifact, root, ordered, dirs, resolve: resolve.clone() })
226226
}
227227

228+
fn get_sysroot() -> Result<Utf8PathBuf> {
229+
// The user requested finding this from cargo_metadata, but since it's not actually
230+
// present in `cargo metadata` output, we fall back to invoking `rustc --print sysroot`.
231+
let output = std::process::Command::new("rustc").arg("--print").arg("sysroot").output()?;
232+
if !output.status.success() {
233+
bail!("Failed to get sysroot from rustc");
234+
}
235+
let path = std::str::from_utf8(&output.stdout)?.trim();
236+
Ok(Utf8PathBuf::from(path))
237+
}
238+
239+
fn find_stdlib_rmetas(sysroot: &Utf8PathBuf) -> Result<Vec<(String, Utf8PathBuf)>> {
240+
let mut rmetas = Vec::new();
241+
let libs = ["core", "alloc", "std", "proc_macro"];
242+
// Walk the lib directory to find the matching rmeta files.
243+
// Usually sysroot/lib/rustlib/<target>/lib/
244+
let rustlib = sysroot.join("lib").join("rustlib");
245+
let mut target_lib_dir = None;
246+
if rustlib.exists() {
247+
for entry in std::fs::read_dir(rustlib)? {
248+
let entry = entry?;
249+
let path = entry.path();
250+
if path.is_dir() {
251+
let lib_sub = path.join("lib");
252+
if lib_sub.exists() {
253+
// Make sure it's the target directory by checking if any rlib/rmeta is there.
254+
for sub_entry in std::fs::read_dir(&lib_sub)? {
255+
let sub_entry = sub_entry?;
256+
if sub_entry
257+
.path()
258+
.extension()
259+
.is_some_and(|ext| ext == "rmeta" || ext == "rlib")
260+
{
261+
target_lib_dir = Some(Utf8PathBuf::from_path_buf(lib_sub).unwrap());
262+
break;
263+
}
264+
}
265+
if target_lib_dir.is_some() {
266+
break;
267+
}
268+
}
269+
}
270+
}
271+
}
272+
let lib_dir =
273+
target_lib_dir.ok_or_else(|| anyhow!("Could not find target lib dir in sysroot"))?;
274+
275+
for lib in libs {
276+
let mut found = None;
277+
for entry in std::fs::read_dir(&lib_dir)? {
278+
let entry = entry?;
279+
let path = entry.path();
280+
if let Some(ext) = path.extension() {
281+
if ext == "rmeta"
282+
&& let Some(file_name) = path.file_name().and_then(|s| s.to_str())
283+
&& file_name.starts_with(&format!("lib{}-", lib))
284+
{
285+
found = Some(Utf8PathBuf::from_path_buf(path).unwrap());
286+
break;
287+
}
288+
}
289+
}
290+
let path = found.ok_or_else(|| anyhow!("Failed to find rmeta for {}", lib))?;
291+
rmetas.push((lib.to_string(), path));
292+
}
293+
Ok(rmetas)
294+
}
295+
228296
fn generate_bindings(&self) -> Result<String> {
229297
let mut pkg_to_header = HashMap::new();
230298
let mut lib_rs_content = String::new();
231299
let deps_dir = &self.dirs.deps_dir;
232300
let headers_dir = &self.dirs.headers_dir;
233301

234302
fs::create_dir_all(headers_dir)?;
303+
304+
// 1. Locate standard library crates and generate bindings for them first.
305+
let sysroot = Self::get_sysroot()?;
306+
let stdlib_crates = Self::find_stdlib_rmetas(&sysroot)?;
307+
let mut stdlib_externs = Vec::new();
308+
for (crate_name, rmeta_path) in &stdlib_crates {
309+
let intermediate_h = deps_dir.join(format!("{}.h", crate_name));
310+
let final_h = headers_dir.join(format!("{}.h", crate_name));
311+
let intermediate_rs = deps_dir.join(format!("lib{}.rs", crate_name));
312+
313+
if intermediate_h.exists() && intermediate_rs.exists() {
314+
pkg_to_header.insert(crate_name.to_string(), format!("{}.h", crate_name));
315+
stdlib_externs.push((crate_name.clone(), rmeta_path.clone()));
316+
if !final_h.exists() {
317+
fs::copy(&intermediate_h, &final_h)?;
318+
}
319+
continue;
320+
}
321+
322+
let mut current_args = vec![
323+
"cpp_api_from_rust".to_string(),
324+
"--crubit-support-path-format=<support/{header}>".to_string(),
325+
"--enable-rmeta-interface".to_string(),
326+
format!("--source-crate-name={}", crate_name),
327+
format!("--h-out={}", intermediate_h),
328+
format!("--rs-out={}", intermediate_rs),
329+
format!("--extern={}={}", crate_name, rmeta_path),
330+
format!("-Ldependency={}", deps_dir.as_str()),
331+
];
332+
// Explicitly pass preceding standard library crates to succeeding ones.
333+
for (prev_name, prev_path) in &stdlib_externs {
334+
current_args.push(format!("--extern={}={}", prev_name, prev_path));
335+
if let Some(prev_header) = pkg_to_header.get(prev_name) {
336+
current_args.push(format!("--crate-header={}={}", prev_name, prev_header));
337+
}
338+
}
339+
340+
let cmdline = Cmdline::new(&current_args).map_err(|err| {
341+
match err.downcast_ref::<clap::Error>() {
342+
Some(clap_err) => {
343+
let _: std::convert::Infallible = clap_err.exit();
344+
}
345+
None => err,
346+
}
347+
})?;
348+
cpp_api_from_rust_lib::run_with_cmdline_args(&cmdline)?;
349+
pkg_to_header.insert(crate_name.to_string(), format!("{}.h", crate_name));
350+
stdlib_externs.push((crate_name.clone(), rmeta_path.clone()));
351+
352+
fs::copy(&intermediate_h, &final_h)?;
353+
}
354+
355+
// 2. Generate bindings for user packages in topological order.
235356
for pkg_id in self.ordered.iter() {
236357
let Some(artifact_info) = self.pkg_to_artifact.get(&pkg_id.repr) else {
237358
continue;
@@ -265,6 +386,15 @@ impl BindingGenerationContext {
265386
format!("--extern={}={}", crate_name, artifact_info.path),
266387
format!("-Ldependency={}", deps_dir.as_str()),
267388
];
389+
390+
// Pass stdlib dependencies to user crates
391+
for (std_name, std_path) in &stdlib_externs {
392+
current_args.push(format!("--extern={}={}", std_name, std_path));
393+
if let Some(std_header) = pkg_to_header.get(std_name.as_str()) {
394+
current_args.push(format!("--crate-header={}={}", std_name, std_header));
395+
}
396+
}
397+
268398
let resolve_node = self
269399
.resolve
270400
.nodes
@@ -381,8 +511,17 @@ bridge_rust = {{ package = "crubit_bridge_rust", version = "0.0.1" }}
381511
let message =
382512
message.map_err(|err| anyhow!("Failed to parse cargo message: {}", err))?;
383513
// TODO: Extract an iterator wrapper that prints out lines out output and returns artifacts.
384-
let Message::CompilerArtifact(artifact) = message else {
385-
continue;
514+
let artifact = match message {
515+
Message::CompilerArtifact(artifact) => artifact,
516+
Message::CompilerMessage(msg) => {
517+
eprintln!("{}", msg);
518+
continue;
519+
}
520+
Message::TextLine(line) => {
521+
println!("{}", line);
522+
continue;
523+
}
524+
_ => continue,
386525
};
387526
if artifact.target.kind.contains(&cargo_metadata::TargetKind::StaticLib) {
388527
cargo_static_lib_path = artifact.filenames.first().cloned();

cc_bindings_from_rs/generate_bindings/query_compiler.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,20 @@ pub fn is_c_abi_compatible_by_value<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bo
7777
return false;
7878
};
7979
#[rustversion::before(2026-04-19)]
80-
let ty = tcx.type_of(field.did).instantiate(tcx, substs);
80+
let mut ty = tcx.type_of(field.did).instantiate(tcx, substs);
8181
#[rustversion::since(2026-04-19)]
82-
let ty = tcx.type_of(field.did).instantiate(tcx, substs).skip_normalization();
82+
let mut ty = tcx.type_of(field.did).instantiate(tcx, substs).skip_normalization();
83+
84+
// Pattern types can be considered by value when they're embedded within an ADT.
85+
// We dont' want to do that for pattern types at large because they might mean they're
86+
// in a function signature, and we cannot uphold a pattern types invariants across the
87+
// FFI boundary leading to UB.
88+
if let ty::TyKind::Pat(pat_ty, _) = ty.kind() {
89+
ty = *pat_ty;
90+
}
8391
is_c_abi_compatible_by_value(tcx, ty)
8492
}
93+
ty::TyKind::Pat(_, _) => false,
8594

8695
// Arrays are explicitly not ABI-compatible (though they are layout-compatible).
8796
ty::TyKind::Array { .. } => false,

0 commit comments

Comments
 (0)