Make `bindings_driver` mostly Crubit-agnostic.
PiperOrigin-RevId: 477525955
diff --git a/cc_bindings_from_rs/cc_bindings_from_rs.rs b/cc_bindings_from_rs/cc_bindings_from_rs.rs
index 9eb0afe..e2c003b 100644
--- a/cc_bindings_from_rs/cc_bindings_from_rs.rs
+++ b/cc_bindings_from_rs/cc_bindings_from_rs.rs
@@ -19,66 +19,73 @@
mod cmdline;
mod lib;
-use cmdline::Cmdline;
+use anyhow::Context;
use itertools::Itertools;
+use rustc_middle::ty::TyCtxt;
+use std::path::Path;
-mod bindings_main {
+use cmdline::Cmdline;
+use lib::GeneratedBindings;
+use token_stream_printer::tokens_to_string;
- use anyhow::Context;
- use rustc_middle::ty::TyCtxt;
- use std::path::Path;
-
- use crate::cmdline::Cmdline;
- use crate::lib::GeneratedBindings;
- use token_stream_printer::tokens_to_string;
-
- pub fn main(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()> {
- let bindings = GeneratedBindings::generate(tcx);
- write_file(cmdline.h_out(), &tokens_to_string(bindings.h_body)?)
- }
-
- fn write_file(path: &Path, content: &str) -> anyhow::Result<()> {
- std::fs::write(path, content)
- .with_context(|| format!("Error when writing to {}", path.display()))
- }
-}
-
-/// Glue that enables the top-level `fn main() -> anyhow::Result<()>` to call
-/// into `fn main(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()>` in the
-/// `bindings_main` module. This mostly wraps and simplifies a subset of APIs
-/// from the `rustc_driver` module.
+/// This mostly wraps and simplifies a subset of APIs from the `rustc_driver`
+/// module.
mod bindings_driver {
use rustc_interface::interface::Compiler;
use rustc_interface::Queries;
+ use rustc_middle::ty::TyCtxt;
- use crate::cmdline::Cmdline;
use crate::lib::enter_tcx;
- /// Wrapper around `rustc_driver::RunCompiler` that exposes a simplified API
- /// (e.g. doesn't take arbitrary `Callbacks` but always calls into
- /// `bindings_main::main`).
- pub struct RunCompiler<'a>(BindingsCallbacks<'a>);
+ /// Wrapper around `rustc_driver::RunCompiler::run` that exposes a
+ /// simplified API:
+ /// - Takes a `callback` that will be invoked from within Rust compiler,
+ /// after parsing and analysis are done,
+ /// - Compilation will stop after parsing, analysis, and the `callback are
+ /// done,
+ /// - Returns the combined results from the Rust compiler *and* the
+ /// `callback`.
+ pub fn run_after_analysis_and_stop<F, R>(
+ rustc_args: &[String],
+ callback: F,
+ ) -> anyhow::Result<R>
+ where
+ F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+ R: Send,
+ {
+ AfterAnalysisCallback::new(rustc_args, callback).run()
+ }
- impl<'a> RunCompiler<'a> {
- /// Creates new Rust compiler runner that will
- /// - pass `cmdline.rustc_args()` to the Rust compiler
- /// - pass `cmdline` to `bindings_main::main` (in addition to passing
- /// `TyCtxt` - see the doc comment of `RunCompiler::run` below).
- pub fn new(cmdline: &'a Cmdline) -> Self {
- Self(BindingsCallbacks { cmdline, bindings_main_result: None })
+ struct AfterAnalysisCallback<'a, F, R>
+ where
+ F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+ R: Send,
+ {
+ args: &'a [String],
+ callback: Option<F>,
+ callback_result: Option<anyhow::Result<R>>,
+ }
+
+ impl<'a, F, R> AfterAnalysisCallback<'a, F, R>
+ where
+ F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+ R: Send,
+ {
+ fn new(args: &'a [String], callback: F) -> Self {
+ Self { args, callback: Some(callback), callback_result: None }
}
/// Runs Rust compiler and then passes the `TyCtxt` of the
/// parsed+analyzed Rust crate into `bindings_main::main`.
/// Returns the combined results from Rust compiler *and*
/// `bindings_main::main`.
- pub fn run(mut self) -> anyhow::Result<()> {
+ fn run(mut self) -> anyhow::Result<R> {
// Rust compiler unwinds with a special sentinel value to abort compilation on
// fatal errors. We use `catch_fatal_errors` to 1) catch such panics and
// translate them into a Result, and 2) resume and propagate other panics.
let rustc_result = rustc_driver::catch_fatal_errors(|| {
- rustc_driver::RunCompiler::new(self.0.cmdline.rustc_args(), &mut self.0).run()
+ rustc_driver::RunCompiler::new(self.args, &mut self).run()
});
// Flatten `Result<Result<T, ...>>` into `Result<T, ...>` (i.e. get the Result
@@ -96,24 +103,22 @@
anyhow::format_err!("Errors reported by Rust compiler.")
});
- // Return either `rustc_result` or `self.0.bindings_main_result`.
+ // Return either `rustc_result` or `self.callback_result`.
rustc_result.and_then(|()| {
assert!(
- self.0.bindings_main_result.is_some(),
- "BindingsCallbacks::run_main should have been called by now"
+ self.callback_result.is_some(),
+ "The callback should have been called by now"
);
- self.0.bindings_main_result.unwrap()
+ self.callback_result.unwrap()
})
}
}
- /// Non-`pub` to avoid exposing `impl rustc_driver::Callbacks`.
- struct BindingsCallbacks<'a> {
- cmdline: &'a Cmdline,
- bindings_main_result: Option<anyhow::Result<()>>,
- }
-
- impl rustc_driver::Callbacks for BindingsCallbacks<'_> {
+ impl<'a, F, R> rustc_driver::Callbacks for AfterAnalysisCallback<'_, F, R>
+ where
+ F: FnOnce(TyCtxt) -> anyhow::Result<R> + Send,
+ R: Send,
+ {
fn after_analysis<'tcx>(
&mut self,
_compiler: &Compiler,
@@ -121,11 +126,11 @@
) -> rustc_driver::Compilation {
let rustc_result = enter_tcx(queries, |tcx| {
assert!(
- self.bindings_main_result.is_none(),
- "after_analysis should only run once"
+ self.callback_result.is_none(),
+ "after_analysis should only run once (1)"
);
- self.bindings_main_result =
- Some(crate::bindings_main::main(self.cmdline, tcx))
+ let callback = self.callback.take().expect("after_analysis should only run once (2)");
+ self.callback_result = Some(callback(tcx));
});
// `expect`ing no errors in `rustc_result`, because `after_analysis` is only
@@ -139,12 +144,24 @@
}
}
+fn write_file(path: &Path, content: &str) -> anyhow::Result<()> {
+ std::fs::write(path, content)
+ .with_context(|| format!("Error when writing to {}", path.display()))
+}
+
+fn run_with_tcx(cmdline: &Cmdline, tcx: TyCtxt) -> anyhow::Result<()> {
+ let bindings = GeneratedBindings::generate(tcx);
+ write_file(cmdline.h_out(), tokens_to_string(bindings.h_body)?.as_str())
+}
+
/// Main entrypoint that (unlike `main`) doesn't do any intitializations that
/// should only happen once for the binary (e.g. it doesn't call
/// `install_ice_hook`) and therefore can be used from the tests module below.
fn run_with_cmdline_args(args: &[String]) -> anyhow::Result<()> {
let cmdline = Cmdline::new(args)?;
- bindings_driver::RunCompiler::new(&cmdline).run()
+ bindings_driver::run_after_analysis_and_stop(cmdline.rustc_args(), |tcx| {
+ run_with_tcx(&cmdline, tcx)
+ })
}
// TODO(lukasza): Add end-to-end shell tests that invoke our executable