Skip to content

[codex] feat: 增强 QEMU 默认覆盖与配置文件解析#53

Merged
ZR233 merged 4 commits intomainfrom
dev-2
Mar 20, 2026
Merged

[codex] feat: 增强 QEMU 默认覆盖与配置文件解析#53
ZR233 merged 4 commits intomainfrom
dev-2

Conversation

@ZR233
Copy link
Member

@ZR233 ZR233 commented Mar 19, 2026

Summary

This PR improves ostool's QEMU configuration workflow in two connected areas:

First, it adds support for default QEMU overrides so callers can append extra QEMU arguments and expected output regex patterns without replacing the rest of the generated defaults.

Second, it refactors build and QEMU config discovery so configuration can be resolved from an explicit search directory and can prefer architecture-specific filenames before falling back to generic ones.

User Impact

Users running different targets can now keep per-architecture QEMU configs such as qemu-aarch64.toml or .qemu-aarch64.toml and let ostool discover them automatically. Users can also place config files in a dedicated config_search_dir instead of being forced into a single manifest or workspace location.

For runtime behavior, the new default override structure makes it easier to extend generated QEMU defaults with additional command-line arguments and output matchers while preserving the existing defaults.

Root Cause

Before these changes:

  • QEMU config discovery searched a narrower fixed set of locations
  • build config loading only used the workspace fallback path when no explicit path was passed
  • menuconfig wrote back to a fixed .qemu.toml path instead of the resolved config path
  • generated QEMU defaults were less extensible for callers that only needed additive customization

That combination made multi-architecture setups less predictable and made small runtime customizations more invasive than necessary.

Fix

This branch introduces the following behavior updates:

  • add a QEMU default override structure to append extra args and regex-based output matchers on top of generated defaults
  • add shared path resolution helpers for build and QEMU config lookup
  • resolve QEMU configs by priority: explicit path, config_search_dir, then manifest directory, checking architecture-specific names before generic names
  • resolve build configs by priority: explicit path, config_search_dir/.build.toml, then workspace/.build.toml
  • when no QEMU config exists, create/use a default path that matches the architecture-aware naming convention
  • make menuconfig use the shared QEMU resolver and save back to the resolved file path
  • update CLI help text to describe the new default config behavior
  • ignore .qemu*.toml so local architecture-specific variants do not appear as untracked noise
  • expand unit coverage around override merging and config path resolution

Validation

Validated with targeted Rust tests:

  • cargo test -p ostool qemu_config_ -- --nocapture
  • cargo test -p ostool build_config_ -- --nocapture

Copilot AI review requested due to automatic review settings March 19, 2026 09:52
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8490435a42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +151 to +157
if config_path.exists() {
let config_content = fs::read_to_string(&config_path)
.await
.with_path("failed to read file", &config_path)?;
let config: QemuConfig = toml::from_str(&config_content)
.with_context(|| format!("failed to parse QEMU config: {}", config_path.display()))?;
config
} else {
let mut config = QemuConfig {
to_bin: true,
..Default::default()
};
config.args.push("-nographic".to_string());
if let Some(arch) = ctx.arch {
match arch {
Architecture::Aarch64 => {
config.args.push("-cpu".to_string());
config.args.push("cortex-a53".to_string());
}
Architecture::Riscv64 => {
config.args.push("-cpu".to_string());
config.args.push("rv64".to_string());
}
_ => {}
return Ok(config);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Merge runtime overrides when a QEMU config already exists

When run_qemu_with_more_default_args() is used in a workspace that already has a QEMU config file (the normal case, since find_qemu_config() and menuconfig both prefer .qemu.toml), this early return drops the supplied args, success_regex, and fail_regex entirely. In that scenario the new helper behaves exactly like run_qemu(), so callers cannot actually add the advertised extra defaults unless the config file is absent.

Useful? React with 👍 / 👎.

Comment on lines +160 to +163
let config = build_default_qemu_config(ctx.arch, overrides);
fs::write(&config_path, toml::to_string_pretty(&config)?)
.await
.with_path("failed to write file", &config_path)?;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep helper overrides out of the generated .qemu.toml

These values now come from a per-invocation helper, but this branch serializes them into the auto-created config file. If a caller passes temporary QEMU flags or a bad regex while no config exists, the first run leaves a sticky or even invalid .qemu.toml behind, and later plain run_qemu() calls keep inheriting or re-failing on those persisted overrides. This makes behavior depend on whether the file existed before the run.

Useful? React with 👍 / 👎.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 在 ostool 的 QEMU 运行逻辑中引入“默认配置覆盖”机制,用于在首次生成 .qemu.toml 默认配置时追加自定义 QEMU 参数及成功/失败正则,从而支持更灵活的测试/运行场景配置。

Changes:

  • 新增 QemuDefaultOverrides,并将默认配置生成逻辑抽取为 build_default_qemu_config
  • run_qemu 重构为通过 run_qemu_with_defaults/load_or_create_qemu_config/run_qemu_with_config 组织流程
  • 增加单元测试覆盖默认配置生成与覆盖追加行为

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +111 to +117
pub async fn run_qemu_with_more_default_args(
ctx: AppContext,
run_args: RunQemuArgs,
args: Vec<String>,
success_regex: Vec<String>,
fail_regex: Vec<String>,
) -> anyhow::Result<()> {
Comment on lines +118 to +127
run_qemu_with_defaults(
ctx,
run_args,
QemuDefaultOverrides {
args,
success_regex,
fail_regex,
},
)
.await
Comment on lines +111 to +116
pub async fn run_qemu_with_more_default_args(
ctx: AppContext,
run_args: RunQemuArgs,
args: Vec<String>,
success_regex: Vec<String>,
fail_regex: Vec<String>,
@ZR233 ZR233 changed the title feat: 添加 QEMU 默认覆盖结构体以支持自定义参数和正则表达式 [codex] feat: 增强 QEMU 默认覆盖与配置文件解析 Mar 20, 2026
Copilot AI review requested due to automatic review settings March 20, 2026 02:49
@ZR233 ZR233 merged commit 11ba180 into main Mar 20, 2026
4 checks passed
@ZR233 ZR233 deleted the dev-2 branch March 20, 2026 02:52
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances ostool’s QEMU configuration workflow by (1) allowing callers to add extra default QEMU args/output matchers without fully replacing generated defaults, and (2) improving QEMU/build config discovery via shared path resolution with architecture-aware filename preference.

Changes:

  • Add “default overrides” support when generating a new QEMU config, plus unit tests for override merging.
  • Introduce shared QEMU config path resolution with arch-specific filename priority and optional config_search_dir, and update menuconfig to save back to the resolved path.
  • Update build config path resolution to optionally prefer config_search_dir/.build.toml over workspace/.build.toml, and expand path-resolution test coverage.

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ostool/src/run/qemu.rs Adds default override plumbing, QEMU config path resolver, and unit tests for override/path resolution.
ostool/src/menuconfig.rs Uses the shared QEMU config resolver and saves edits back to the resolved config path.
ostool/src/main.rs Updates CLI help text for QEMU config default behavior.
ostool/src/ctx.rs Adds config_search_dir and introduces build config path resolution helper.
ostool/Cargo.toml Adds tempfile dev-dep and updates dependency versions/features.
Cargo.lock Lockfile updates from dependency changes.
.gitignore Ignores .qemu*.toml to avoid local arch-specific QEMU configs showing as untracked.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +170 to +173
success_regex: overrides.success_regex,
fail_regex: overrides.fail_regex,
..Default::default()
};
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

build_default_qemu_config currently replaces success_regex/fail_regex with the override vectors (success_regex: overrides.success_regex, fail_regex: overrides.fail_regex). The PR description/API naming suggests these should be appended on top of any generated defaults (like how args are extended). Consider initializing from defaults and extend-ing with the override patterns so overrides are additive rather than replacing.

Suggested change
success_regex: overrides.success_regex,
fail_regex: overrides.fail_regex,
..Default::default()
};
..Default::default()
};
// Make override patterns additive: append to any default regexes
config.success_regex.extend(overrides.success_regex);
config.fail_regex.extend(overrides.fail_regex);

Copilot uses AI. Check for mistakes.
Comment on lines 94 to 99
pub build_config: Option<BuildConfig>,
/// Path to the build configuration file.
pub build_config_path: Option<PathBuf>,
/// 可选的配置文件搜索目录,设置后优先于 workspace/manifest 进行配置发现
pub config_search_dir: Option<PathBuf>,
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config_search_dir is introduced and used by config resolution, but it doesn’t appear to be set from any CLI flag, env var, or config loading path (e.g., ostool/src/main.rs and src/bin/cargo-osrun.rs construct AppContext without it). As-is, end users can’t actually take advantage of the new search-dir behavior described in the PR. Please wire this field up to a user-facing option (or remove it until it’s supported).

Copilot uses AI. Check for mistakes.
schemars = {workspace = true, features = ["derive"]}
serde = {workspace = true, features = ["derive"]}
serde_json = {workspace = true}
reqwest = { version = "0.13" }
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reqwest dependency is changed from an explicit default-features = false + rustls-tls setup to reqwest = { version = "0.13" }, which implicitly enables reqwest’s default TLS/backend feature set. That can materially change binary size, platform support, and TLS/root-store behavior, and it isn’t mentioned in the PR description (which focuses on QEMU/config discovery). If this bump isn’t required for the QEMU work/tests, consider reverting to the previous feature selection or documenting the intended TLS/backend change.

Suggested change
reqwest = { version = "0.13" }
reqwest = { version = "0.13", default-features = false, features = ["rustls-tls"] }

Copilot uses AI. Check for mistakes.
Comment on lines +878 to +953
#[test]
fn build_config_explicit_path_wins() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();
let search_dir = workspace.join("config");
std::fs::create_dir(&search_dir).unwrap();

// 创建多个配置文件
std::fs::write(search_dir.join(".build.toml"), "").unwrap();
std::fs::write(workspace.join(".build.toml"), "").unwrap();

let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: Some(search_dir),
..Default::default()
};

// 显式路径应该优先
let explicit = workspace.join("custom.build.toml");
let result = ctx.resolve_build_config_path(Some(explicit.clone()));
assert_eq!(result, explicit);
}

#[test]
fn build_config_search_dir_beats_workspace() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();
let search_dir = workspace.join("config");
std::fs::create_dir(&search_dir).unwrap();

// 创建多个配置文件
std::fs::write(search_dir.join(".build.toml"), "[system]").unwrap();
std::fs::write(workspace.join(".build.toml"), "[system]").unwrap();

let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: Some(search_dir.clone()),
..Default::default()
};

let result = ctx.resolve_build_config_path(None);
assert_eq!(result, search_dir.join(".build.toml"));
}

#[test]
fn build_config_fallback_to_workspace() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();

let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: None,
..Default::default()
};

let result = ctx.resolve_build_config_path(None);
assert_eq!(result, workspace.join(".build.toml"));
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests under this module include build-config path resolution cases (resolve_build_config_path), which live in ctx.rs. Keeping these tests colocated with the code under test (e.g., in ctx.rs’s test module or an integration test) would make future maintenance easier and avoid mixing unrelated concerns into run/qemu.rs.

Suggested change
#[test]
fn build_config_explicit_path_wins() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();
let search_dir = workspace.join("config");
std::fs::create_dir(&search_dir).unwrap();
// 创建多个配置文件
std::fs::write(search_dir.join(".build.toml"), "").unwrap();
std::fs::write(workspace.join(".build.toml"), "").unwrap();
let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: Some(search_dir),
..Default::default()
};
// 显式路径应该优先
let explicit = workspace.join("custom.build.toml");
let result = ctx.resolve_build_config_path(Some(explicit.clone()));
assert_eq!(result, explicit);
}
#[test]
fn build_config_search_dir_beats_workspace() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();
let search_dir = workspace.join("config");
std::fs::create_dir(&search_dir).unwrap();
// 创建多个配置文件
std::fs::write(search_dir.join(".build.toml"), "[system]").unwrap();
std::fs::write(workspace.join(".build.toml"), "[system]").unwrap();
let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: Some(search_dir.clone()),
..Default::default()
};
let result = ctx.resolve_build_config_path(None);
assert_eq!(result, search_dir.join(".build.toml"));
}
#[test]
fn build_config_fallback_to_workspace() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().to_path_buf();
let manifest = workspace.join("manifest");
std::fs::create_dir(&manifest).unwrap();
let ctx = AppContext {
paths: PathConfig {
workspace: workspace.clone(),
manifest,
..Default::default()
},
config_search_dir: None,
..Default::default()
};
let result = ctx.resolve_build_config_path(None);
assert_eq!(result, workspace.join(".build.toml"));
}

Copilot uses AI. Check for mistakes.
@github-actions github-actions bot mentioned this pull request Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants