equi_ty/extractor/
plugin.rs

1use std::borrow::Cow;
2use std::env;
3use std::process::Command;
4
5use clap::Parser;
6use rustc_driver::TimePassesCallbacks;
7use rustc_plugin::{CrateFilter, RustcPlugin, RustcPluginArgs, Utf8Path};
8use serde::{Deserialize, Serialize};
9use tracing::{debug, trace};
10
11use crate::extractor::callbacks::ExtractorCallbacks;
12use crate::utils_crate;
13
14// This struct is the plugin provided to the rustc_plugin framework.
15// It must be exported for use by the CLI/driver binaries.
16#[derive(Copy, Clone, Default, Debug)]
17pub struct Plugin {
18    // pub results: HashMap<String, String>,
19    // // pub demotions: HashSet<DemotionInfo>,
20    // // pub nostd_replacements: HashSet<NoStdReplacementInfo>,
21    // pub replacements: HashSet<ReplacementRecord>,
22    // pub suggestions: BTreeMap<PathBuf, HashSet<Suggestion>>,
23}
24
25// Parse CLI args
26#[derive(Debug, Parser, Serialize, Deserialize)]
27pub struct ExtractorPluginArgs {
28    // #[arg(long, value_parser=utils_misc::parse_file)]
29    // pub global_private_reexport_fixes_json: Option<String>,
30    //
31    // #[arg(long, value_parser=utils_misc::parse_file)]
32    // pub global_equivalents_json: Option<String>,
33    //
34    // /// if None then pick equivalents from *all* entries in `GLOBAL_EQUIVALENTS.json`.
35    // /// Otherwise only use the selected crates from `GLOBAL_EQUIVALENTS.json`.
36    // #[arg(long)]
37    // pub equivalents_from: Option<Vec<String>>,
38    #[clap(last = true)]
39    cargo_args: Vec<String>,
40}
41
42impl RustcPlugin for Plugin {
43    type Args = ExtractorPluginArgs;
44
45    fn version(&self) -> Cow<'static, str> {
46        env!("CARGO_PKG_VERSION").into()
47    }
48
49    fn driver_name(&self) -> Cow<'static, str> {
50        "equity-extractor-driver".into()
51    }
52
53    fn args(&self, _target_dir: &Utf8Path) -> RustcPluginArgs<Self::Args> {
54        let args = ExtractorPluginArgs::parse_from(env::args().skip(1));
55        let filter = CrateFilter::OnlyWorkspace;
56        RustcPluginArgs { args, filter }
57    }
58
59    // Pass Cargo arguments (like --feature) from the top-level CLI to Cargo.
60    fn modify_cargo(&self, cargo: &mut Command, args: &Self::Args) {
61        cargo.args(&args.cargo_args);
62    }
63
64    // use the Rustc API to start a compiler session
65    // as per the args
66    fn run(
67        self,
68        compiler_args: Vec<String>,
69        plugin_args: Self::Args,
70    ) -> rustc_interface::interface::Result<()> {
71        let (should_analyze, opt_typsuffix) = utils_crate::should_analyze_build(&compiler_args)
72            .expect("failed to check if this is a lib");
73
74        if !should_analyze {
75            // we need to build the crate normally here, using cargo.
76            // Otherwise the build scripts might not be executed.
77            debug!(">> building normally as should_analyze_build returned false");
78            let mut callbacks = TimePassesCallbacks::default();
79            rustc_driver::run_compiler(&compiler_args, &mut callbacks);
80            return Ok(());
81        }
82
83        let _typsuffix = opt_typsuffix.expect("typsuffix was none!");
84
85        let mut callbacks = ExtractorCallbacks::new(&plugin_args);
86        let mut compiler_args = compiler_args;
87        // allow warnings => -Awarnings
88        compiler_args.push("-Awarnings".to_string());
89        // // emit only llvm-ir (to save time by not linking)
90        // compiler_args.push("--emit".to_string());
91        // compiler_args.push("llvm-ir".to_string());
92        trace!(">> compiler_args:\n{compiler_args:#?}");
93
94        debug!(">> starting compiler.run() ...");
95        rustc_driver::run_compiler(&compiler_args, &mut callbacks);
96        debug!(">> compiler.run() finished.");
97
98        // // FINAL OUTPUT
99        // println!("{:#?}", callbacks.results);
100        //
101        // utils::save_replacements(callbacks.replacements)
102        //     .expect("failed to save replacements to json file");
103
104        Ok(())
105    }
106}