Skip to main content

bindeps_simple/
lib.rs

1use std::path::PathBuf;
2
3use anyhow::{Result, anyhow};
4use std::process::{Command, Stdio};
5
6#[derive(Default)]
7pub struct Builder {
8    pub name: String,
9    pub force_rebuild: bool,
10    pub env: Vec<(String, String)>,
11    pub features: Vec<String>,
12    pub target: Option<String>,
13    pub output_dir: Option<PathBuf>,
14    pub cargo_args: Vec<String>,
15    pub manifest_path: Option<PathBuf>,
16    /// 调用Bindeps的crate名,用于测试
17    pub user_crate_name: Option<String>,
18}
19impl Builder {
20    pub fn new(name: &str) -> Self {
21        Self {
22            name: name.to_string(),
23            ..Default::default()
24        }
25    }
26
27    pub fn env(mut self, key: &str, value: &str) -> Self {
28        self.env.push((key.to_string(), value.to_string()));
29        self
30    }
31
32    pub fn feature(mut self, feature: &str) -> Self {
33        self.features.push(feature.to_string());
34        self
35    }
36
37    pub fn cargo_args<T: AsRef<str>>(mut self, args: &[T]) -> Self {
38        self.cargo_args
39            .extend(args.iter().map(|s| s.as_ref().to_string()));
40        self
41    }
42
43    pub fn cargo_arg<T: AsRef<str>>(mut self, arg: T) -> Self {
44        self.cargo_args.push(arg.as_ref().to_string());
45        self
46    }
47
48    pub fn target(mut self, target: &str) -> Self {
49        self.target = Some(target.to_string());
50        self
51    }
52
53    pub fn build(self) -> Result<Output> {
54        let output_dir = self
55            .output_dir
56            .unwrap_or_else(|| PathBuf::from(std::env::var("OUT_DIR").unwrap()));
57
58        let target = self.target.unwrap_or_else(|| {
59            std::env::var("TARGET").expect("TARGET environment variable is not set")
60        });
61
62        BinCrate {
63            name: self.name,
64            force_rebuild: self.force_rebuild,
65            envs: self.env,
66            features: self.features,
67            target,
68            output_dir,
69            cargo_args: self.cargo_args,
70            manifest_path: self.manifest_path,
71        }
72        .run()
73    }
74}
75
76#[derive(Default)]
77pub struct BinCrate {
78    pub name: String,
79    pub force_rebuild: bool,
80    pub envs: Vec<(String, String)>,
81    pub features: Vec<String>,
82    pub target: String,
83    pub output_dir: PathBuf,
84    cargo_args: Vec<String>,
85    manifest_path: Option<PathBuf>,
86}
87
88impl BinCrate {
89    pub fn run(&mut self) -> Result<Output> {
90        let manifest_path = self
91            .manifest_path
92            .clone()
93            .unwrap_or_else(|| std::env::var("CARGO_MANIFEST_PATH").unwrap().into());
94
95        let mut cargo_metadata = cargo_metadata::MetadataCommand::new();
96
97        let metadata = cargo_metadata.manifest_path(&manifest_path).exec()?;
98
99        let package = metadata
100            .packages
101            .iter()
102            .find(|p| p.name.eq_ignore_ascii_case(&self.name))
103            .ok_or_else(|| anyhow!("Cannot find package {}, is it has lib.rs?", &self.name))?;
104
105        println!("mf: {}", package.manifest_path);
106        let mf_path = package.manifest_path.as_str();
107        if mf_path.contains(".cargo/registry") || mf_path.contains(".cargo\\registry") {
108            println!("is online");
109        } else {
110            println!("is local");
111            println!(
112                "cargo:rerun-if-changed={}",
113                package.manifest_path.parent().unwrap()
114            );
115        }
116
117        self.manifest_path = Some(package.manifest_path.as_os_str().into());
118
119        self.build_crate()?;
120        let out_dir = self.output_dir.join("bin");
121        Ok(Output {
122            dir: out_dir.clone(),
123            elf: out_dir.join(&self.name),
124        })
125    }
126
127    /// 编译 crate 并返回可执行文件路径
128    fn build_crate(&self) -> Result<()> {
129        let manifest = self.manifest_path.as_ref().unwrap().clone();
130        println!("开始编译...");
131
132        let mut cargo = Command::new("cargo");
133
134        cargo
135            .arg("install")
136            .arg(&self.name)
137            .args(["--locked", "-Z", "unstable-options", "--target"])
138            .arg(&self.target)
139            .arg("--root")
140            .arg(&self.output_dir)
141            .arg("--path")
142            .arg(manifest.parent().unwrap())
143            .env_remove("RUSTFLAGS")
144            .env_remove("CARGO_ENCODED_RUSTFLAGS")
145            .env_remove("RUSTC_WORKSPACE_WRAPPER") // used by clippy
146            .stdout(Stdio::inherit()) // 将输出传递到父进程
147            .stderr(Stdio::inherit()); // 将错误传递到父进程
148
149        for (key, value) in self.envs.iter() {
150            cargo.env(key, value);
151        }
152        for f in self.features.iter() {
153            cargo.arg("--features").arg(f);
154        }
155
156        for a in self.cargo_args.iter() {
157            cargo.arg(a);
158        }
159
160        println!("cmd: {cargo:?}");
161
162        // 执行 cargo build 并显示实时输出
163        let status = cargo.status()?;
164
165        if !status.success() {
166            anyhow::bail!("编译失败: {}", status);
167        }
168
169        Ok(())
170    }
171}
172
173#[derive(Debug, Clone)]
174pub struct Output {
175    pub dir: PathBuf,
176    pub elf: PathBuf,
177}