Skip to content

Commit b3724ba

Browse files
committed
feat: 添加 someboot 模块并实现自动检测构建配置
1 parent 8bb26e8 commit b3724ba

File tree

3 files changed

+222
-1
lines changed

3 files changed

+222
-1
lines changed

ostool/src/build/cargo_builder.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use colored::Colorize;
1414
use anyhow::Context;
1515

1616
use crate::{
17-
build::config::Cargo,
17+
build::{config::Cargo, someboot},
1818
ctx::AppContext,
1919
utils::{Command, PathResultExt},
2020
};
@@ -218,6 +218,21 @@ impl<'a> CargoBuilder<'a> {
218218
cmd.arg(arg);
219219
}
220220

221+
// Auto-detected args from someboot/build-info.toml
222+
let workspace_manifest = self.ctx.paths.workspace.join("Cargo.toml");
223+
if workspace_manifest.exists() {
224+
let detected_args = someboot::detect_build_config(&workspace_manifest, &self.config.target)
225+
.with_context(|| {
226+
format!(
227+
"failed to detect someboot build config from {}",
228+
workspace_manifest.display()
229+
)
230+
})?;
231+
for arg in detected_args {
232+
cmd.arg(arg);
233+
}
234+
}
235+
221236
// Release mode
222237
if !self.ctx.debug {
223238
cmd.arg("--release");

ostool/src/build/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub mod cargo_builder;
3737
/// Build configuration types and structures.
3838
pub mod config;
3939

40+
mod someboot;
41+
4042
/// Specifies the type of runner to use after building.
4143
///
4244
/// This enum determines how the built artifact will be executed,

ostool/src/build/someboot.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
use std::{collections::HashMap, path::PathBuf};
2+
3+
use anyhow::Context;
4+
use serde::Deserialize;
5+
6+
#[derive(Debug, Default, Deserialize)]
7+
struct TargetBuildInfo {
8+
#[serde(default)]
9+
rustflags: Vec<String>,
10+
#[serde(default)]
11+
cargoargs: Vec<String>,
12+
}
13+
14+
pub fn detect_build_config(manifest_path: &PathBuf, target: &str) -> anyhow::Result<Vec<String>> {
15+
let mut cargo_args = Vec::new();
16+
17+
let meta = read_metadata(manifest_path, true)?;
18+
let mut someboot_roots = collect_someboot_roots(&meta);
19+
if someboot_roots.is_empty() {
20+
// `--no-deps` metadata does not include transitive crates.io packages.
21+
// Fall back to full metadata when no local/direct hit is found.
22+
let meta_with_deps = read_metadata(manifest_path, false)?;
23+
someboot_roots = collect_someboot_roots(&meta_with_deps);
24+
}
25+
26+
if someboot_roots.is_empty() {
27+
return Ok(cargo_args);
28+
}
29+
30+
let build_info_path = someboot_roots
31+
.into_iter()
32+
.map(|root| root.join("build-info.toml"))
33+
.find(|p| p.exists());
34+
35+
let Some(build_info_path) = build_info_path else {
36+
return Ok(cargo_args);
37+
};
38+
39+
let build_info_raw = std::fs::read_to_string(&build_info_path).with_context(|| {
40+
format!(
41+
"failed to read build-info.toml: {}",
42+
build_info_path.display()
43+
)
44+
})?;
45+
46+
let build_info: HashMap<String, TargetBuildInfo> = toml::from_str(&build_info_raw)
47+
.with_context(|| {
48+
format!(
49+
"failed to parse build-info.toml at {}",
50+
build_info_path.display()
51+
)
52+
})?;
53+
54+
let Some(matched) = pick_target_build_info(&build_info, target) else {
55+
return Ok(cargo_args);
56+
};
57+
58+
cargo_args.extend(matched.cargoargs.iter().cloned());
59+
60+
if !matched.rustflags.is_empty() {
61+
cargo_args.push("--config".to_string());
62+
cargo_args.push(rustflags_to_cargo_override(target, &matched.rustflags));
63+
}
64+
65+
Ok(cargo_args)
66+
}
67+
68+
fn read_metadata(manifest_path: &PathBuf, no_deps: bool) -> anyhow::Result<cargo_metadata::Metadata> {
69+
let mut cmd = cargo_metadata::MetadataCommand::new();
70+
cmd.manifest_path(manifest_path);
71+
if no_deps {
72+
cmd.no_deps();
73+
}
74+
75+
cmd.exec().with_context(|| {
76+
let mode = if no_deps { "--no-deps" } else { "with deps" };
77+
format!(
78+
"failed to read Cargo metadata ({mode}) from manifest path: {}",
79+
manifest_path.display()
80+
)
81+
})
82+
}
83+
84+
fn collect_someboot_roots(meta: &cargo_metadata::Metadata) -> Vec<PathBuf> {
85+
let mut someboot_roots: Vec<PathBuf> = meta
86+
.packages
87+
.iter()
88+
.flat_map(|pkg| pkg.dependencies.iter())
89+
.filter(|dep| dep.name == "someboot")
90+
.filter_map(|dep| dep.path.clone())
91+
.map(|p| p.into_std_path_buf())
92+
.collect();
93+
94+
someboot_roots.extend(
95+
meta.packages
96+
.iter()
97+
.filter(|pkg| pkg.name == "someboot")
98+
.filter_map(|pkg| {
99+
pkg.manifest_path
100+
.parent()
101+
.map(|p| p.as_std_path().to_path_buf())
102+
}),
103+
);
104+
105+
someboot_roots.sort();
106+
someboot_roots.dedup();
107+
someboot_roots
108+
}
109+
110+
fn pick_target_build_info<'a>(
111+
build_info: &'a HashMap<String, TargetBuildInfo>,
112+
target: &str,
113+
) -> Option<&'a TargetBuildInfo> {
114+
if let Some(exact) = build_info.get(target) {
115+
return Some(exact);
116+
}
117+
118+
let mut contains_target: Vec<_> = build_info
119+
.iter()
120+
.filter(|(cfg_target, _)| target.contains(cfg_target.as_str()))
121+
.collect();
122+
123+
contains_target.sort_by(|a, b| b.0.len().cmp(&a.0.len()).then_with(|| a.0.cmp(b.0)));
124+
if let Some((_, info)) = contains_target.first() {
125+
return Some(*info);
126+
}
127+
128+
let mut target_contains: Vec<_> = build_info
129+
.iter()
130+
.filter(|(cfg_target, _)| cfg_target.contains(target))
131+
.collect();
132+
133+
target_contains.sort_by(|a, b| b.0.len().cmp(&a.0.len()).then_with(|| a.0.cmp(b.0)));
134+
target_contains.first().map(|(_, info)| *info)
135+
}
136+
137+
fn rustflags_to_cargo_override(target: &str, rustflags: &[String]) -> String {
138+
let rustflags_toml =
139+
toml::Value::Array(rustflags.iter().cloned().map(toml::Value::String).collect())
140+
.to_string();
141+
142+
format!("target.{target}.rustflags={rustflags_toml}")
143+
}
144+
145+
#[cfg(test)]
146+
mod tests {
147+
use super::detect_build_config;
148+
use std::path::PathBuf;
149+
150+
#[test]
151+
fn test_local() {
152+
detect_build_config_works_for_sparreal_manifest(
153+
"/home/ubuntu/workspace/sparreal-os/Cargo.toml",
154+
);
155+
}
156+
157+
#[test]
158+
fn test_crateio() {
159+
detect_build_config_works_for_sparreal_manifest(
160+
"/home/ubuntu/workspace/tgoskits/Cargo.toml",
161+
);
162+
}
163+
164+
fn detect_build_config_works_for_sparreal_manifest(p: &str) {
165+
let manifest_path = PathBuf::from(p);
166+
if !manifest_path.exists() {
167+
return;
168+
}
169+
170+
let args = detect_build_config(&manifest_path, "aarch64-unknown-none")
171+
.expect("detect_build_config should succeed");
172+
173+
println!("Detected cargo args: ");
174+
for arg in &args {
175+
println!(" {arg}");
176+
}
177+
178+
assert!(
179+
args.len() >= 4,
180+
"expected at least cargoargs and rustflags config, got: {args:?}"
181+
);
182+
assert_eq!(&args[0..2], ["-Z", "build-std=core,alloc"]);
183+
assert_eq!(args[2], "--config");
184+
185+
let rustflags_config = args
186+
.iter()
187+
.find(|arg| arg.starts_with("target.aarch64-unknown-none.rustflags="))
188+
.expect("target rustflags command-line override should exist");
189+
190+
assert!(rustflags_config.contains("\"-C\""));
191+
assert!(rustflags_config.contains("\"relocation-model=pic\""));
192+
assert!(rustflags_config.contains("\"-Clink-args=-pie\""));
193+
194+
let contains_match_args =
195+
detect_build_config(&manifest_path, "aarch64-unknown-none-softfloat")
196+
.expect("contains match should succeed");
197+
assert_eq!(&contains_match_args[0..2], ["-Z", "build-std=core,alloc"]);
198+
assert!(
199+
contains_match_args
200+
.iter()
201+
.any(|arg| arg.starts_with("target.aarch64-unknown-none-softfloat.rustflags="))
202+
);
203+
}
204+
}

0 commit comments

Comments
 (0)