Skip to content

Commit a0a0e17

Browse files
authored
refactor: 优化 ostool run,减少依赖条件 (#39)
* feat: 添加 thiserror 依赖并改进错误处理 * feat: 添加 someboot 模块并实现自动检测构建配置 * feat: 更新 someboot 模块的导入方式并改进构建配置检测 * feat: 更新 ostool 版本至 0.8.12,并增强 CargoBuilder 以支持从 Cargo JSON 消息解析可执行文件路径 * feat: 移除不必要的 schema 生成代码以简化 run_uboot 函数 * feat: 优化 detect_build_config 函数的参数格式并改进错误处理
1 parent e4c7e68 commit a0a0e17

14 files changed

Lines changed: 727 additions & 233 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ostool/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
88
name = "ostool"
99
readme = "../README.md"
1010
repository = "https://github.com/drivercraft/ostool"
11-
version = "0.8.11"
11+
version = "0.8.12"
1212

1313
[package.metadata.binstall]
1414
bin-dir = "{ name }-{ target }-v{ version }/{ bin }{ binary-ext }"
@@ -57,4 +57,5 @@ lzma-rs = "0.3"
5757
regex = "1"
5858
sha2 = "0.10"
5959
tar = "0.4"
60+
thiserror = {workspace = true}
6061
ureq = "3.0"

ostool/src/bin/cargo-osrun.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::{env, path::PathBuf, process::exit};
1+
use std::{
2+
env,
3+
path::PathBuf,
4+
process::{ExitCode, exit},
5+
};
26

37
use clap::{Parser, Subcommand};
48
use log::{LevelFilter, debug};
@@ -76,7 +80,18 @@ struct CliUboot {
7680
}
7781

7882
#[tokio::main]
79-
async fn main() -> anyhow::Result<()> {
83+
async fn main() -> ExitCode {
84+
match try_main().await {
85+
Ok(()) => ExitCode::SUCCESS,
86+
Err(err) => {
87+
eprintln!("Error: {err:#}");
88+
eprintln!("\nTrace:\n{err:?}");
89+
ExitCode::FAILURE
90+
}
91+
}
92+
}
93+
94+
async fn try_main() -> anyhow::Result<()> {
8095
env_logger::builder()
8196
.format_module_path(false)
8297
.filter_level(LevelFilter::Info)
@@ -118,7 +133,7 @@ async fn main() -> anyhow::Result<()> {
118133
..Default::default()
119134
};
120135

121-
app.set_elf_path(args.elf).await;
136+
app.set_elf_path(args.elf).await?;
122137
app.objcopy_elf()?;
123138

124139
app.debug = args.debug;

ostool/src/build/cargo_builder.rs

Lines changed: 164 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
77
use std::{
88
collections::HashMap,
9+
io::BufReader,
910
path::{Path, PathBuf},
11+
process::Stdio,
1012
};
1113

14+
use anyhow::{Context, anyhow, bail};
15+
use cargo_metadata::{Message, PackageId};
1216
use colored::Colorize;
1317

14-
use crate::{build::config::Cargo, ctx::AppContext, utils::Command};
18+
use crate::{
19+
build::{config::Cargo, someboot},
20+
ctx::AppContext,
21+
utils::{Command, PathResultExt},
22+
};
1523

1624
/// A builder for constructing and executing Cargo commands.
1725
///
@@ -35,6 +43,8 @@ pub struct CargoBuilder<'a> {
3543
extra_args: Vec<String>,
3644
extra_envs: HashMap<String, String>,
3745
skip_objcopy: bool,
46+
resolve_artifact_from_json: bool,
47+
resolved_elf_path: Option<PathBuf>,
3848
config_path: Option<PathBuf>,
3949
}
4050

@@ -54,6 +64,8 @@ impl<'a> CargoBuilder<'a> {
5464
extra_args: Vec::new(),
5565
extra_envs: HashMap::new(),
5666
skip_objcopy: false,
67+
resolve_artifact_from_json: false,
68+
resolved_elf_path: None,
5769
config_path,
5870
}
5971
}
@@ -73,6 +85,8 @@ impl<'a> CargoBuilder<'a> {
7385
extra_args: Vec::new(),
7486
extra_envs: HashMap::new(),
7587
skip_objcopy: true,
88+
resolve_artifact_from_json: false,
89+
resolved_elf_path: None,
7690
config_path,
7791
}
7892
}
@@ -130,6 +144,12 @@ impl<'a> CargoBuilder<'a> {
130144
self
131145
}
132146

147+
/// Enables artifact path resolution from Cargo JSON messages.
148+
pub fn resolve_artifact_from_json(mut self, enable: bool) -> Self {
149+
self.resolve_artifact_from_json = enable;
150+
self
151+
}
152+
133153
/// Executes the configured Cargo command.
134154
///
135155
/// This runs pre-build commands, executes Cargo, handles output artifacts,
@@ -162,11 +182,78 @@ impl<'a> CargoBuilder<'a> {
162182
}
163183

164184
async fn run_cargo(&mut self) -> anyhow::Result<()> {
185+
if self.resolve_artifact_from_json {
186+
return self.run_cargo_and_resolve_artifact().await;
187+
}
188+
165189
let mut cmd = self.build_cargo_command().await?;
166190
cmd.run()?;
167191
Ok(())
168192
}
169193

194+
async fn run_cargo_and_resolve_artifact(&mut self) -> anyhow::Result<()> {
195+
let (target_pkg_id, default_run) = self.target_package_info()?;
196+
let mut cmd = self.build_cargo_command().await?;
197+
198+
cmd.stdout(Stdio::piped());
199+
cmd.stderr(Stdio::inherit());
200+
cmd.print_cmd();
201+
202+
let mut child = cmd
203+
.spawn()
204+
.context("failed to spawn cargo build command for artifact resolution")?;
205+
206+
let stdout = child
207+
.stdout
208+
.take()
209+
.ok_or_else(|| anyhow!("failed to capture cargo stdout for message parsing"))?;
210+
let reader = BufReader::new(stdout);
211+
212+
let mut executable_artifacts: Vec<(String, PathBuf)> = Vec::new();
213+
for message in Message::parse_stream(reader) {
214+
let message = message.context("failed to parse cargo JSON message stream")?;
215+
match message {
216+
Message::CompilerArtifact(artifact) => {
217+
if artifact.package_id == target_pkg_id
218+
&& artifact.target.is_bin()
219+
&& let Some(executable) = artifact.executable
220+
{
221+
executable_artifacts
222+
.push((artifact.target.name, executable.into_std_path_buf()));
223+
}
224+
}
225+
Message::CompilerMessage(msg) => {
226+
if let Some(rendered) = msg.message.rendered {
227+
eprint!("{rendered}");
228+
}
229+
}
230+
Message::TextLine(line) => {
231+
println!("{line}");
232+
}
233+
_ => {}
234+
}
235+
}
236+
237+
let status = child
238+
.wait()
239+
.context("failed waiting for cargo build process")?;
240+
if !status.success() {
241+
bail!("failed with status: {status}");
242+
}
243+
244+
let resolved = self.pick_executable_artifact(&executable_artifacts, default_run.as_deref());
245+
let Some(resolved) = resolved else {
246+
bail!(
247+
"no executable artifact found for package '{}' and target '{}'; please check system.Cargo.package/system.Cargo.target",
248+
self.config.package,
249+
self.config.target
250+
);
251+
};
252+
253+
self.resolved_elf_path = Some(resolved);
254+
Ok(())
255+
}
256+
170257
async fn build_cargo_command(&mut self) -> anyhow::Result<Command> {
171258
let mut cmd = self.ctx.command("cargo");
172259

@@ -212,11 +299,32 @@ impl<'a> CargoBuilder<'a> {
212299
cmd.arg(arg);
213300
}
214301

302+
// Auto-detected args from someboot/build-info.toml
303+
let workspace_manifest = self.ctx.paths.workspace.join("Cargo.toml");
304+
if workspace_manifest.exists() {
305+
let detected_args =
306+
someboot::detect_build_config(&workspace_manifest, &self.config.target)
307+
.with_context(|| {
308+
format!(
309+
"failed to detect someboot build config from {}",
310+
workspace_manifest.display()
311+
)
312+
})?;
313+
for arg in detected_args {
314+
cmd.arg(arg);
315+
}
316+
}
317+
215318
// Release mode
216319
if !self.ctx.debug {
217320
cmd.arg("--release");
218321
}
219322

323+
if self.resolve_artifact_from_json {
324+
cmd.arg("--message-format");
325+
cmd.arg("json-render-diagnostics");
326+
}
327+
220328
// Extra args
221329
for arg in &self.extra_args {
222330
cmd.arg(arg);
@@ -230,14 +338,18 @@ impl<'a> CargoBuilder<'a> {
230338
}
231339

232340
async fn handle_output(&mut self) -> anyhow::Result<()> {
233-
let target_dir = self.ctx.paths.build_dir();
341+
let elf_path = if let Some(path) = &self.resolved_elf_path {
342+
path.clone()
343+
} else {
344+
let target_dir = self.ctx.paths.build_dir();
234345

235-
let elf_path = target_dir
236-
.join(&self.config.target)
237-
.join(if self.ctx.debug { "debug" } else { "release" })
238-
.join(&self.config.package);
346+
target_dir
347+
.join(&self.config.target)
348+
.join(if self.ctx.debug { "debug" } else { "release" })
349+
.join(&self.config.package)
350+
};
239351

240-
self.ctx.set_elf_path(elf_path).await;
352+
self.ctx.set_elf_path(elf_path).await?;
241353

242354
if self.config.to_bin && !self.skip_objcopy {
243355
self.ctx.objcopy_output_bin()?;
@@ -253,6 +365,43 @@ impl<'a> CargoBuilder<'a> {
253365
Ok(())
254366
}
255367

368+
fn target_package_info(&self) -> anyhow::Result<(PackageId, Option<String>)> {
369+
let metadata = self.ctx.metadata()?;
370+
let Some(package) = metadata
371+
.packages
372+
.iter()
373+
.find(|pkg| pkg.name == self.config.package)
374+
else {
375+
bail!(
376+
"package '{}' not found in cargo metadata under {}",
377+
self.config.package,
378+
self.ctx.paths.manifest.display()
379+
);
380+
};
381+
Ok((package.id.clone(), package.default_run.clone()))
382+
}
383+
384+
fn pick_executable_artifact(
385+
&self,
386+
executable_artifacts: &[(String, PathBuf)],
387+
default_run: Option<&str>,
388+
) -> Option<PathBuf> {
389+
executable_artifacts
390+
.iter()
391+
.rev()
392+
.find(|(name, _)| name == &self.config.package)
393+
.or_else(|| {
394+
default_run.and_then(|default_bin| {
395+
executable_artifacts
396+
.iter()
397+
.rev()
398+
.find(|(name, _)| name == default_bin)
399+
})
400+
})
401+
.or_else(|| executable_artifacts.last())
402+
.map(|(_, path)| path.clone())
403+
}
404+
256405
fn build_features(&self) -> Vec<String> {
257406
let mut features = self.config.features.clone();
258407
if let Some(log_level) = self.log_level_feature() {
@@ -310,7 +459,12 @@ impl<'a> CargoBuilder<'a> {
310459
if let Some(ref config_path) = self.config_path {
311460
let combined = config_path
312461
.parent()
313-
.ok_or_else(|| anyhow::anyhow!("Invalid config path"))?
462+
.ok_or_else(|| {
463+
anyhow::anyhow!(
464+
"invalid config path without parent: {}",
465+
config_path.display()
466+
)
467+
})?
314468
.join(extra);
315469
Ok(Some(combined))
316470
} else {
@@ -397,7 +551,8 @@ impl<'a> CargoBuilder<'a> {
397551
// Write to temp file
398552
tokio::fs::write(&target_path, content)
399553
.await
400-
.map_err(|e| anyhow::anyhow!("Failed to write to temp file: {}", e))?;
554+
.with_path("failed to write downloaded cargo config", &target_path)
555+
.with_context(|| format!("while downloading cargo config from {url}"))?;
401556

402557
println!("Config downloaded to: {}", target_path.display());
403558

0 commit comments

Comments
 (0)