Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -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

Expand All @@ -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.
Expand Down Expand Up @@ -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 <name>` when the package has multiple binaries.
bin = "my-os-kernel"

# Enabled features
features = ["page-alloc-4g"]

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand All @@ -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

Expand All @@ -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`,工具会检测到该序列并优雅退出,不会将按键发送到目标设备。
Expand Down Expand Up @@ -162,6 +171,10 @@ target = "aarch64-unknown-none"
# 包名称
package = "my-os-kernel"

# 二进制 target 名称。包内只有一个 binary 时可省略;
# 包内有多个 binary 时建议设置,或在命令行传 `--bin <name>`。
bin = "my-os-kernel"

# 启用的特性
features = ["page-alloc-4g"]

Expand Down
1 change: 1 addition & 0 deletions ostool/src/board/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
95 changes: 66 additions & 29 deletions ostool/src/build/cargo_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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<ResolvedCargoArtifact> {
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<String> {
let mut features = self.config.features.clone();
if let Some(log_level) = self.log_level_feature() {
Expand Down Expand Up @@ -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<ResolvedCargoArtifact> {
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::<Vec<_>>()
.join(", ");
bail!(
"package `{package}` has multiple binary targets ({bins}); pass system.Cargo.bin or --bin"
)
}
8 changes: 8 additions & 0 deletions ostool/src/build/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//! [system.Cargo]
//! target = "aarch64-unknown-none"
//! package = "my-kernel"
//! bin = "my-kernel"
//! features = ["feature1", "feature2"]
//! to_bin = true
//! ```
Expand Down Expand Up @@ -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<String>,
/// Cargo features to enable.
pub features: Vec<String>,
/// Log level feature to automatically enable.
Expand Down
77 changes: 70 additions & 7 deletions ostool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ enum SubCommands {
/// Path to the build configuration file
#[arg(short, long)]
config: Option<PathBuf>,
#[command(flatten)]
cargo_selector: CargoSelectorArgs,
},
Run {
#[command(subcommand)]
Expand Down Expand Up @@ -75,6 +77,8 @@ struct RunQemuCommand {
#[arg(short, long)]
config: Option<PathBuf>,
#[command(flatten)]
cargo_selector: CargoSelectorArgs,
#[command(flatten)]
qemu: QemuArgs,
}

Expand All @@ -84,14 +88,34 @@ struct RunUbootCommand {
#[arg(short, long)]
config: Option<PathBuf>,
#[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<String>,
/// Select a Cargo binary target within the selected package
#[arg(long)]
bin: Option<String>,
}

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<PathBuf>,
#[command(flatten)]
cargo_selector: CargoSelectorArgs,
/// Path to the board runner configuration file, defaults to `pwd/.board.toml`
#[arg(long = "board-config")]
board_config: Option<PathBuf>,
Expand Down Expand Up @@ -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?;
Expand All @@ -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() {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading