diff --git a/README.en.md b/README.en.md index 8df9701..95e4cdb 100644 --- a/README.en.md +++ b/README.en.md @@ -102,6 +102,9 @@ ostool build # Build with specific config file ostool build --config custom-build.toml +# Temporarily override Cargo package / binary target +ostool build --config custom-build.toml --package paging-test --bin basic + # Build in specified working directory ostool --workdir /path/to/project build ``` @@ -121,6 +124,9 @@ ostool run qemu --dtb-dump # Run with specific Qemu config file ostool run qemu --qemu-config my-qemu.toml +# Run after temporarily overriding Cargo package / binary target +ostool run qemu --package paging-test --bin basic + # Run with U-Boot ostool run uboot @@ -135,6 +141,9 @@ ostool board ls # Run on a remote board ostool board run + +# Run a specific Cargo binary target on a remote board +ostool board run --package paging-test --bin basic ``` > Exit shortcut: In the serial terminal (e.g., `ostool run uboot`), press `Ctrl+A` then `x` to quit; the tool captures this sequence and exits gracefully instead of sending it to the target device. @@ -162,6 +171,10 @@ target = "aarch64-unknown-none" # Package name package = "my-os-kernel" +# Binary target name. Omit it when the package has only one binary; +# set it here or pass `--bin ` when the package has multiple binaries. +bin = "my-os-kernel" + # Enabled features features = ["page-alloc-4g"] diff --git a/README.md b/README.md index 5e6ef99..f31a259 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,9 @@ ostool build # 指定配置文件构建 ostool build --config custom-build.toml +# 临时覆盖 Cargo package / binary target +ostool build --config custom-build.toml --package paging-test --bin basic + # 在指定工作目录中构建 ostool --workdir /path/to/project build ``` @@ -121,6 +124,9 @@ ostool run qemu --dtb-dump # 指定 Qemu 配置文件运行 ostool run qemu --qemu-config my-qemu.toml +# 临时覆盖 Cargo package / binary target 后运行 +ostool run qemu --package paging-test --bin basic + # 使用 U-Boot 运行 ostool run uboot @@ -135,6 +141,9 @@ ostool board ls # 在远端开发板上运行 ostool board run + +# 在远端开发板上运行指定 Cargo binary target +ostool board run --package paging-test --bin basic ``` > 交互退出:在串口终端(如 `ostool run uboot`)中,按下 `Ctrl+A` 后再按 `x`,工具会检测到该序列并优雅退出,不会将按键发送到目标设备。 @@ -162,6 +171,10 @@ target = "aarch64-unknown-none" # 包名称 package = "my-os-kernel" +# 二进制 target 名称。包内只有一个 binary 时可省略; +# 包内有多个 binary 时建议设置,或在命令行传 `--bin `。 +bin = "my-os-kernel" + # 启用的特性 features = ["page-alloc-4g"] diff --git a/ostool/src/board/config.rs b/ostool/src/board/config.rs index 27f7186..33eaa6f 100644 --- a/ostool/src/board/config.rs +++ b/ostool/src/board/config.rs @@ -347,6 +347,7 @@ dtb_file = "${package}/board.dtb" env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "kernel".into(), + bin: None, features: vec![], log: None, extra_config: None, diff --git a/ostool/src/build/cargo_builder.rs b/ostool/src/build/cargo_builder.rs index 697670a..22cb7ba 100644 --- a/ostool/src/build/cargo_builder.rs +++ b/ostool/src/build/cargo_builder.rs @@ -193,14 +193,12 @@ impl<'a> CargoBuilder<'a> { bail!("failed with status: {status}"); } - let resolved = self.pick_executable_artifact(&executable_artifacts, default_run.as_deref()); - let Some(resolved) = resolved else { - bail!( - "no executable bin artifact found in cargo JSON output for package '{}' and target '{}'; ostool currently resolves only Cargo bin targets. Please check system.Cargo.package/system.Cargo.target", - self.config.package, - self.config.target - ); - }; + let resolved = select_executable_artifact( + &executable_artifacts, + self.config.bin.as_deref(), + default_run.as_deref(), + &self.config.package, + )?; self.resolved_artifact = Some(resolved); Ok(()) @@ -229,6 +227,10 @@ impl<'a> CargoBuilder<'a> { // Package and target cmd.arg("-p"); cmd.arg(&self.config.package); + if let Some(bin) = &self.config.bin { + cmd.arg("--bin"); + cmd.arg(bin); + } cmd.arg("--target"); cmd.arg(&self.config.target); cmd.arg("-Z"); @@ -328,27 +330,6 @@ impl<'a> CargoBuilder<'a> { Ok((package.id.clone(), package.default_run.clone())) } - fn pick_executable_artifact( - &self, - executable_artifacts: &[(String, ResolvedCargoArtifact)], - default_run: Option<&str>, - ) -> Option { - executable_artifacts - .iter() - .rev() - .find(|(name, _)| name == &self.config.package) - .or_else(|| { - default_run.and_then(|default_bin| { - executable_artifacts - .iter() - .rev() - .find(|(name, _)| name == default_bin) - }) - }) - .or_else(|| executable_artifacts.last()) - .map(|(_, path)| path.clone()) - } - fn build_features(&self) -> Vec { let mut features = self.config.features.clone(); if let Some(log_level) = self.log_level_feature() { @@ -510,3 +491,59 @@ impl<'a> CargoBuilder<'a> { Ok(target_path) } } + +fn select_executable_artifact( + executable_artifacts: &[(String, ResolvedCargoArtifact)], + explicit_bin: Option<&str>, + default_run: Option<&str>, + package: &str, +) -> anyhow::Result { + if let Some(bin) = explicit_bin { + return executable_artifacts + .iter() + .rev() + .find(|(name, _)| name == bin) + .map(|(_, artifact)| artifact.clone()) + .ok_or_else(|| { + anyhow!( + "binary target `{bin}` was not built for package `{package}`; check system.Cargo.bin or --bin" + ) + }); + } + + if executable_artifacts.is_empty() { + bail!( + "no executable bin artifact found in cargo JSON output for package `{package}`; ostool currently resolves only Cargo bin targets" + ); + } + + if let Some((_, artifact)) = executable_artifacts + .iter() + .rev() + .find(|(name, _)| name == package) + { + return Ok(artifact.clone()); + } + + if let Some(default_bin) = default_run + && let Some((_, artifact)) = executable_artifacts + .iter() + .rev() + .find(|(name, _)| name == default_bin) + { + return Ok(artifact.clone()); + } + + if executable_artifacts.len() == 1 { + return Ok(executable_artifacts[0].1.clone()); + } + + let bins = executable_artifacts + .iter() + .map(|(name, _)| name.as_str()) + .collect::>() + .join(", "); + bail!( + "package `{package}` has multiple binary targets ({bins}); pass system.Cargo.bin or --bin" + ) +} diff --git a/ostool/src/build/config.rs b/ostool/src/build/config.rs index 5ff70cf..5cc8e3d 100644 --- a/ostool/src/build/config.rs +++ b/ostool/src/build/config.rs @@ -10,6 +10,7 @@ //! [system.Cargo] //! target = "aarch64-unknown-none" //! package = "my-kernel" +//! bin = "my-kernel" //! features = ["feature1", "feature2"] //! to_bin = true //! ``` @@ -72,6 +73,13 @@ pub struct Cargo { pub target: String, /// Package name to build. pub package: String, + /// Binary target name to build within the selected package. + /// + /// When omitted, Cargo's normal package binary resolution is used when it + /// is unambiguous. Packages with multiple binary targets should set this + /// field or pass `--bin` on the command line. + #[serde(default)] + pub bin: Option, /// Cargo features to enable. pub features: Vec, /// Log level feature to automatically enable. diff --git a/ostool/src/main.rs b/ostool/src/main.rs index 9504e29..ff8ff74 100644 --- a/ostool/src/main.rs +++ b/ostool/src/main.rs @@ -32,6 +32,8 @@ enum SubCommands { /// Path to the build configuration file #[arg(short, long)] config: Option, + #[command(flatten)] + cargo_selector: CargoSelectorArgs, }, Run { #[command(subcommand)] @@ -75,6 +77,8 @@ struct RunQemuCommand { #[arg(short, long)] config: Option, #[command(flatten)] + cargo_selector: CargoSelectorArgs, + #[command(flatten)] qemu: QemuArgs, } @@ -84,14 +88,34 @@ struct RunUbootCommand { #[arg(short, long)] config: Option, #[command(flatten)] + cargo_selector: CargoSelectorArgs, + #[command(flatten)] uboot: UbootArgs, } +#[derive(Args, Debug, Default, Clone)] +struct CargoSelectorArgs { + /// Override the Cargo package from the build configuration + #[arg(long)] + package: Option, + /// Select a Cargo binary target within the selected package + #[arg(long)] + bin: Option, +} + +impl CargoSelectorArgs { + fn is_empty(&self) -> bool { + self.package.is_none() && self.bin.is_none() + } +} + #[derive(Args, Debug)] struct BoardRunArgs { /// Path to the build configuration file #[arg(short, long)] config: Option, + #[command(flatten)] + cargo_selector: CargoSelectorArgs, /// Path to the board runner configuration file, defaults to `pwd/.board.toml` #[arg(long = "board-config")] board_config: Option, @@ -174,8 +198,9 @@ async fn try_main() -> Result<()> { } BoardSubCommands::Run(args) => { let (mut tool, manifest_ctx) = init_tool(manifest.clone())?; - let build_config = + let mut build_config = load_build_config(&mut tool, &manifest_ctx, args.config.as_deref()).await?; + apply_cargo_selector(&mut tool, &mut build_config, &args.cargo_selector)?; let board_config = load_board_config(&mut tool, &manifest_ctx, args.board_config.as_deref()) .await?; @@ -194,21 +219,30 @@ async fn try_main() -> Result<()> { board::config()?; } }, - SubCommands::Build { config } => { + SubCommands::Build { + config, + cargo_selector, + } => { let (mut tool, manifest_ctx) = init_tool(manifest)?; - let build_config = + let mut build_config = load_build_config(&mut tool, &manifest_ctx, config.as_deref()).await?; + apply_cargo_selector(&mut tool, &mut build_config, &cargo_selector)?; tool.build_with_config(&build_config).await?; } SubCommands::Run { command } => match command { RunSubCommands::Qemu(args) => { - let RunQemuCommand { config, qemu } = args; + let RunQemuCommand { + config, + cargo_selector, + qemu, + } = args; let debug = qemu.debug; let dtb_dump = qemu.dtb_dump; let (mut tool, manifest_ctx) = init_tool(manifest.clone())?; - let build_config = + let mut build_config = load_build_config(&mut tool, &manifest_ctx, config.as_deref()).await?; + apply_cargo_selector(&mut tool, &mut build_config, &cargo_selector)?; match &build_config.system { build::config::BuildSystem::Cargo(config) => { let qemu_config = match qemu.qemu_config.as_deref() { @@ -248,11 +282,16 @@ async fn try_main() -> Result<()> { } } RunSubCommands::Uboot(args) => { - let RunUbootCommand { config, uboot } = args; + let RunUbootCommand { + config, + cargo_selector, + uboot, + } = args; let (mut tool, manifest_ctx) = init_tool(manifest.clone())?; - let build_config = + let mut build_config = load_build_config(&mut tool, &manifest_ctx, config.as_deref()).await?; + apply_cargo_selector(&mut tool, &mut build_config, &cargo_selector)?; match &build_config.system { build::config::BuildSystem::Cargo(config) => { let uboot_config = match uboot.uboot_config.as_deref() { @@ -321,6 +360,30 @@ async fn load_build_config( } } +fn apply_cargo_selector( + tool: &mut Tool, + build_config: &mut build::config::BuildConfig, + selector: &CargoSelectorArgs, +) -> Result<()> { + if selector.is_empty() { + return Ok(()); + } + + let build::config::BuildSystem::Cargo(cargo) = &mut build_config.system else { + anyhow::bail!("--package/--bin can only be used with system.Cargo build configs"); + }; + + if let Some(package) = &selector.package { + cargo.package = package.clone(); + } + if let Some(bin) = &selector.bin { + cargo.bin = Some(bin.clone()); + } + + tool.ctx_mut().build_config = Some(build_config.clone()); + Ok(()) +} + async fn load_qemu_config( tool: &mut Tool, manifest: &ManifestContext, diff --git a/ostool/src/run/qemu.rs b/ostool/src/run/qemu.rs index 642d835..52bfb86 100644 --- a/ostool/src/run/qemu.rs +++ b/ostool/src/run/qemu.rs @@ -922,6 +922,7 @@ fail_regex = [] env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "kernel".into(), + bin: None, features: vec![], log: None, extra_config: None, @@ -943,6 +944,7 @@ fail_regex = [] env: HashMap::new(), target: "riscv64gc-unknown-none-elf".into(), package: "sample".into(), + bin: None, features: vec![], log: None, extra_config: None, @@ -1104,6 +1106,7 @@ timeout = 0 env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "sample".into(), + bin: None, features: vec![], log: None, extra_config: None, diff --git a/ostool/src/run/uboot.rs b/ostool/src/run/uboot.rs index 6c9c678..984d2bb 100644 --- a/ostool/src/run/uboot.rs +++ b/ostool/src/run/uboot.rs @@ -1581,6 +1581,7 @@ timeout = 0 env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "sample".into(), + bin: None, features: vec![], log: None, extra_config: None, @@ -1760,6 +1761,7 @@ baud_rate = "115200" env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "kernel".into(), + bin: None, features: vec![], log: None, extra_config: None, diff --git a/ostool/src/tool.rs b/ostool/src/tool.rs index f9646d7..b45c78b 100644 --- a/ostool/src/tool.rs +++ b/ostool/src/tool.rs @@ -1119,6 +1119,7 @@ mod tests { env: HashMap::new(), target: "aarch64-unknown-none".into(), package: "kernel".into(), + bin: None, features: vec![], log: None, extra_config: None,