Skip to content

Commit fb8e4d8

Browse files
committed
feat: 支持从 Cargo 包目录解析 QEMU 配置文件
1 parent 9180882 commit fb8e4d8

4 files changed

Lines changed: 198 additions & 10 deletions

File tree

ostool/src/build/mod.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ use crate::{
2626
cargo_builder::CargoBuilder,
2727
config::{Cargo, Custom},
2828
},
29-
run::{qemu::RunQemuArgs, uboot::RunUbootArgs},
29+
run::{
30+
qemu::{RunQemuArgs, resolve_qemu_config_path_in_dir},
31+
uboot::RunUbootArgs,
32+
},
3033
};
3134

3235
/// Cargo builder implementation for building projects.
@@ -50,6 +53,12 @@ pub enum CargoRunnerKind {
5053
debug: bool,
5154
/// Whether to dump the device tree blob.
5255
dtb_dump: bool,
56+
/// Extra default QEMU command-line arguments.
57+
args: Vec<String>,
58+
/// Regex patterns that indicate successful execution.
59+
success_regex: Vec<String>,
60+
/// Regex patterns that indicate failed execution.
61+
fail_regex: Vec<String>,
5362
},
5463
/// Run the built artifact on real hardware via U-Boot.
5564
Uboot {
@@ -157,13 +166,28 @@ impl Tool {
157166
CargoRunnerKind::Qemu {
158167
qemu_config,
159168
dtb_dump,
169+
args,
170+
success_regex,
171+
fail_regex,
160172
..
161173
} => {
162-
self.run_qemu(RunQemuArgs {
163-
qemu_config: qemu_config.clone(),
164-
dtb_dump: *dtb_dump,
165-
show_output: true,
166-
})
174+
let package_dir = self.resolve_package_manifest_dir(&config.package)?;
175+
let resolved_qemu_config = resolve_qemu_config_path_in_dir(
176+
&package_dir,
177+
self.ctx.arch,
178+
qemu_config.clone(),
179+
)?;
180+
181+
self.run_qemu_with_more_default_args(
182+
RunQemuArgs {
183+
qemu_config: Some(resolved_qemu_config),
184+
dtb_dump: *dtb_dump,
185+
show_output: true,
186+
},
187+
args.clone(),
188+
success_regex.clone(),
189+
fail_regex.clone(),
190+
)
167191
.await?;
168192
}
169193
CargoRunnerKind::Uboot { uboot_config } => {

ostool/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ pub struct QemuArgs {
6060
/// Path to the qemu configuration file
6161
///
6262
/// Default behavior when not specified:
63+
/// - Cargo build system: use the target package directory
64+
/// - Custom build system: use the workspace directory
6365
/// - With architecture detected: .qemu-{arch}.toml (e.g., .qemu-aarch64.toml)
6466
/// - Without architecture: .qemu.toml
6567
#[arg(short, long)]
@@ -118,6 +120,9 @@ async fn try_main() -> Result<()> {
118120
qemu_config: qemu_args.qemu_config,
119121
debug: qemu_args.debug,
120122
dtb_dump: qemu_args.dtb_dump,
123+
args: vec![],
124+
success_regex: vec![],
125+
fail_regex: vec![],
121126
},
122127
RunSubCommands::Uboot(uboot_args) => CargoRunnerKind::Uboot {
123128
uboot_config: uboot_args.uboot_config,

ostool/src/run/qemu.rs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -677,12 +677,20 @@ impl QemuRunner<'_> {
677677
pub(crate) fn resolve_qemu_config_path(
678678
tool: &Tool,
679679
explicit_path: Option<PathBuf>,
680+
) -> anyhow::Result<PathBuf> {
681+
resolve_qemu_config_path_in_dir(tool.workspace_dir(), tool.ctx.arch, explicit_path)
682+
}
683+
684+
pub(crate) fn resolve_qemu_config_path_in_dir(
685+
search_dir: &Path,
686+
arch: Option<Architecture>,
687+
explicit_path: Option<PathBuf>,
680688
) -> anyhow::Result<PathBuf> {
681689
if let Some(path) = explicit_path {
682690
return Ok(path);
683691
}
684692

685-
let arch_str = tool.ctx.arch.map(|arch| format!("{arch:?}").to_lowercase());
693+
let arch_str = arch.map(|arch| format!("{arch:?}").to_lowercase());
686694

687695
// 文件名优先级顺序
688696
let candidates: Vec<String> = if let Some(ref arch) = arch_str {
@@ -697,7 +705,7 @@ pub(crate) fn resolve_qemu_config_path(
697705
};
698706

699707
for filename in &candidates {
700-
let path = tool.workspace_dir().join(filename);
708+
let path = search_dir.join(filename);
701709
if path.exists() {
702710
return Ok(path);
703711
}
@@ -709,14 +717,14 @@ pub(crate) fn resolve_qemu_config_path(
709717
".qemu.toml".to_string()
710718
};
711719

712-
Ok(tool.workspace_dir().join(default_filename))
720+
Ok(search_dir.join(default_filename))
713721
}
714722

715723
#[cfg(test)]
716724
mod tests {
717725
use super::{
718726
QemuConfig, QemuDefaultOverrides, QemuRunner, build_default_qemu_config,
719-
resolve_qemu_config_path,
727+
resolve_qemu_config_path, resolve_qemu_config_path_in_dir,
720728
};
721729
use object::Architecture;
722730
use std::path::PathBuf;
@@ -939,6 +947,50 @@ mod tests {
939947
assert_eq!(result, tmp.path().join("qemu.toml"));
940948
}
941949

950+
#[test]
951+
fn qemu_config_search_dir_prefers_arch_specific_files() {
952+
let tmp = TempDir::new().unwrap();
953+
std::fs::write(tmp.path().join("qemu-aarch64.toml"), "").unwrap();
954+
std::fs::write(tmp.path().join("qemu.toml"), "").unwrap();
955+
956+
let result = resolve_qemu_config_path_in_dir(
957+
tmp.path(),
958+
Some(Architecture::Aarch64),
959+
None,
960+
)
961+
.unwrap();
962+
assert_eq!(result, tmp.path().join("qemu-aarch64.toml"));
963+
}
964+
965+
#[test]
966+
fn qemu_config_search_dir_uses_hidden_generic_before_hidden_default_creation() {
967+
let tmp = TempDir::new().unwrap();
968+
std::fs::write(tmp.path().join(".qemu.toml"), "").unwrap();
969+
970+
let result =
971+
resolve_qemu_config_path_in_dir(tmp.path(), Some(Architecture::Aarch64), None)
972+
.unwrap();
973+
assert_eq!(result, tmp.path().join(".qemu.toml"));
974+
}
975+
976+
#[test]
977+
fn qemu_config_search_dir_defaults_to_arch_specific_hidden_file() {
978+
let tmp = TempDir::new().unwrap();
979+
980+
let result =
981+
resolve_qemu_config_path_in_dir(tmp.path(), Some(Architecture::Aarch64), None)
982+
.unwrap();
983+
assert_eq!(result, tmp.path().join(".qemu-aarch64.toml"));
984+
}
985+
986+
#[test]
987+
fn qemu_config_search_dir_defaults_without_arch() {
988+
let tmp = TempDir::new().unwrap();
989+
990+
let result = resolve_qemu_config_path_in_dir(tmp.path(), None, None).unwrap();
991+
assert_eq!(result, tmp.path().join(".qemu.toml"));
992+
}
993+
942994
#[test]
943995
fn build_config_explicit_path_wins() {
944996
let tmp = TempDir::new().unwrap();

ostool/src/tool.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,28 @@ impl Tool {
171171
})
172172
}
173173

174+
pub(crate) fn resolve_package_manifest_dir(&self, package: &str) -> anyhow::Result<PathBuf> {
175+
let metadata = self.metadata()?;
176+
let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == package) else {
177+
bail!(
178+
"package '{}' not found in cargo metadata under {}",
179+
package,
180+
self.manifest_dir().display()
181+
);
182+
};
183+
184+
pkg.manifest_path
185+
.parent()
186+
.map(|path| path.as_std_path().to_path_buf())
187+
.ok_or_else(|| {
188+
anyhow!(
189+
"package '{}' manifest has no parent: {}",
190+
package,
191+
pkg.manifest_path
192+
)
193+
})
194+
}
195+
174196
/// Sets the ELF artifact path and synchronizes derived runtime metadata.
175197
pub async fn set_elf_artifact_path(&mut self, path: PathBuf) -> anyhow::Result<()> {
176198
let path = path
@@ -485,6 +507,8 @@ fn resolve_manifest_path(input: Option<PathBuf>) -> anyhow::Result<PathBuf> {
485507
#[cfg(test)]
486508
mod tests {
487509
use super::{Tool, ToolConfig, resolve_manifest_context};
510+
use crate::run::qemu::resolve_qemu_config_path_in_dir;
511+
use object::Architecture;
488512

489513
#[tokio::test]
490514
async fn set_elf_artifact_path_updates_dirs_and_arch() {
@@ -548,4 +572,87 @@ mod tests {
548572
assert_eq!(manifest.manifest_dir, app_dir);
549573
assert_eq!(manifest.workspace_dir, temp.path());
550574
}
575+
576+
#[test]
577+
fn resolve_package_manifest_dir_uses_selected_package() {
578+
let temp = tempfile::tempdir().unwrap();
579+
std::fs::write(
580+
temp.path().join("Cargo.toml"),
581+
"[workspace]\nmembers = [\"app\", \"kernel\"]\nresolver = \"3\"\n",
582+
)
583+
.unwrap();
584+
585+
let app_dir = temp.path().join("app");
586+
std::fs::create_dir_all(app_dir.join("src")).unwrap();
587+
std::fs::write(
588+
app_dir.join("Cargo.toml"),
589+
"[package]\nname = \"app\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
590+
)
591+
.unwrap();
592+
std::fs::write(app_dir.join("src/main.rs"), "fn main() {}\n").unwrap();
593+
594+
let kernel_dir = temp.path().join("kernel");
595+
std::fs::create_dir_all(kernel_dir.join("src")).unwrap();
596+
std::fs::write(
597+
kernel_dir.join("Cargo.toml"),
598+
"[package]\nname = \"kernel\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
599+
)
600+
.unwrap();
601+
std::fs::write(kernel_dir.join("src/main.rs"), "fn main() {}\n").unwrap();
602+
603+
let tool = Tool::new(ToolConfig {
604+
manifest: Some(app_dir.clone()),
605+
..Default::default()
606+
})
607+
.unwrap();
608+
609+
let resolved = tool.resolve_package_manifest_dir("kernel").unwrap();
610+
assert_eq!(resolved, kernel_dir);
611+
}
612+
613+
#[test]
614+
fn cargo_qemu_config_resolution_prefers_package_dir_over_workspace_root() {
615+
let temp = tempfile::tempdir().unwrap();
616+
std::fs::write(
617+
temp.path().join("Cargo.toml"),
618+
"[workspace]\nmembers = [\"app\", \"kernel\"]\nresolver = \"3\"\n",
619+
)
620+
.unwrap();
621+
std::fs::write(temp.path().join("qemu-aarch64.toml"), "").unwrap();
622+
623+
let app_dir = temp.path().join("app");
624+
std::fs::create_dir_all(app_dir.join("src")).unwrap();
625+
std::fs::write(
626+
app_dir.join("Cargo.toml"),
627+
"[package]\nname = \"app\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
628+
)
629+
.unwrap();
630+
std::fs::write(app_dir.join("src/main.rs"), "fn main() {}\n").unwrap();
631+
632+
let kernel_dir = temp.path().join("kernel");
633+
std::fs::create_dir_all(kernel_dir.join("src")).unwrap();
634+
std::fs::write(
635+
kernel_dir.join("Cargo.toml"),
636+
"[package]\nname = \"kernel\"\nversion = \"0.1.0\"\nedition = \"2024\"\n",
637+
)
638+
.unwrap();
639+
std::fs::write(kernel_dir.join("src/main.rs"), "fn main() {}\n").unwrap();
640+
std::fs::write(kernel_dir.join(".qemu-aarch64.toml"), "").unwrap();
641+
642+
let tool = Tool::new(ToolConfig {
643+
manifest: Some(app_dir),
644+
..Default::default()
645+
})
646+
.unwrap();
647+
648+
let package_dir = tool.resolve_package_manifest_dir("kernel").unwrap();
649+
let resolved = resolve_qemu_config_path_in_dir(
650+
&package_dir,
651+
Some(Architecture::Aarch64),
652+
None,
653+
)
654+
.unwrap();
655+
656+
assert_eq!(resolved, kernel_dir.join(".qemu-aarch64.toml"));
657+
}
551658
}

0 commit comments

Comments
 (0)