Make OSDK errors clear if commands don't exist

This commit is contained in:
Zhang Junyang
2025-06-13 11:19:00 +08:00
committed by Tate, Hongliang Tian
parent 4a9977d9a7
commit f3f0e9a244
13 changed files with 97 additions and 25 deletions

53
osdk/Cargo.lock generated
View File

@ -203,6 +203,7 @@ dependencies = [
"shlex",
"syn",
"toml",
"which",
]
[[package]]
@ -380,6 +381,12 @@ dependencies = [
"regex",
]
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "env_logger"
version = "0.11.8"
@ -399,6 +406,16 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "getrandom"
version = "0.3.3"
@ -608,6 +625,12 @@ dependencies = [
"xmas-elf",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "lock_api"
version = "0.4.12"
@ -811,6 +834,19 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
[[package]]
name = "rustix"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags 2.9.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.20"
@ -1063,6 +1099,17 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "which"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d"
dependencies = [
"env_home",
"rustix",
"winsafe",
]
[[package]]
name = "windows-core"
version = "0.61.0"
@ -1204,6 +1251,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"

View File

@ -26,6 +26,7 @@ serde_json = "1.0.111"
shlex = "1.3.0"
syn = { version = "2.0.52", features = ["extra-traits", "full", "parsing", "printing"] }
toml = { version = "0.8.8", features = ["preserve_order"] }
which = "8.0.0"
[dev-dependencies]
assert_cmd = "2.0.13"

View File

@ -80,7 +80,7 @@ impl Display for Arch {
/// Get the default architecture implied by the host rustc's default architecture.
pub fn get_default_arch() -> Arch {
let output = std::process::Command::new("rustc")
let output = crate::util::new_command_checked_exists("rustc")
.arg("-vV")
.output()
.expect("Failed to run rustc to get the host target");

View File

@ -11,7 +11,6 @@ use vm_image::{AsterVmImage, AsterVmImageType};
use std::{
path::{Path, PathBuf},
process::Command,
time::SystemTime,
};
@ -22,7 +21,7 @@ use crate::{
},
error::Errno,
error_msg,
util::DirGuard,
util::{new_command_checked_exists, DirGuard},
};
/// The osdk bundle artifact that stores as `bundle` directory.
@ -199,8 +198,8 @@ impl Bundle {
ActionChoice::Run => &config.run,
ActionChoice::Test => &config.test,
};
let mut qemu_cmd = Command::new(&action.qemu.path);
let mut qemu_cmd = new_command_checked_exists(&action.qemu.path);
qemu_cmd.current_dir(&config.work_dir);
match action.boot.method {

View File

@ -4,7 +4,6 @@ use std::{
fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
path::{Path, PathBuf},
process::Command,
};
use linux_bzimage_builder::{
@ -17,7 +16,7 @@ use crate::{
bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta},
file::BundleFile,
},
util::{get_current_crates, hard_link_or_copy},
util::{get_current_crates, hard_link_or_copy, new_command_checked_exists},
};
pub fn make_install_bzimage(
@ -95,7 +94,7 @@ pub fn make_elf_for_qemu(install_dir: impl AsRef<Path>, elf: &AsterBin, strip: b
if strip {
// We use rust-strip to reduce the kernel image size.
let status = Command::new("rust-strip")
let status = new_command_checked_exists("rust-strip")
.arg(elf.path())
.arg("-o")
.arg(result_elf_path.as_os_str())
@ -170,7 +169,7 @@ fn install_setup_with_arch(
}
let target_dir = std::fs::canonicalize(target_dir).unwrap();
let mut cmd = Command::new("cargo");
let mut cmd = new_command_checked_exists("cargo");
let mut rustflags = vec![
"-Cdebuginfo=2",
"-Ccode-model=kernel",

View File

@ -16,7 +16,7 @@ use crate::{
scheme::{ActionChoice, BootProtocol},
Config,
},
util::{get_current_crates, hard_link_or_copy},
util::{get_current_crates, hard_link_or_copy, new_command_checked_exists},
};
pub fn create_bootdev_image(
@ -84,7 +84,7 @@ pub fn create_bootdev_image(
// Make the boot device CDROM image using `grub-mkrescue`.
let iso_path = &target_dir.as_ref().join(target_name.to_string() + ".iso");
let mut grub_mkrescue_cmd = std::process::Command::new(action.grub.grub_mkrescue.as_os_str());
let mut grub_mkrescue_cmd = new_command_checked_exists(action.grub.grub_mkrescue.as_os_str());
grub_mkrescue_cmd
.arg(iso_root.as_os_str())
.arg("-o")
@ -166,7 +166,7 @@ fn generate_grub_cfg(
}
fn get_grub_mkrescue_version(grub_mkrescue: &PathBuf) -> String {
let mut cmd = std::process::Command::new(grub_mkrescue);
let mut cmd = new_command_checked_exists(grub_mkrescue);
cmd.arg("--version");
let output = cmd.output().unwrap();
String::from_utf8(output.stdout).unwrap()

View File

@ -8,6 +8,7 @@ use crate::{
vm_image::{AsterQcow2ImageMeta, AsterVmImage, AsterVmImageType},
},
error_msg,
util::new_command_checked_exists,
};
pub fn convert_iso_to_qcow2(iso: AsterVmImage) -> AsterVmImage {
@ -17,7 +18,7 @@ pub fn convert_iso_to_qcow2(iso: AsterVmImage) -> AsterVmImage {
let iso_path = iso.path();
let qcow2_path = iso_path.with_extension("qcow2");
// Convert the ISO to QCOW2 using `qemu-img`.
let mut qemu_img = process::Command::new("qemu-img");
let mut qemu_img = new_command_checked_exists("qemu-img");
qemu_img.args([
"convert",
"-O",

View File

@ -3,9 +3,8 @@
use crate::{
cli::DebugArgs,
commands::util::bin_file_name,
util::{get_kernel_crate, get_target_directory},
util::{get_kernel_crate, get_target_directory, new_command_checked_exists},
};
use std::process::Command;
pub fn execute_debug_command(_profile: &str, args: &DebugArgs) {
let remote = &args.remote;
@ -16,7 +15,7 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) {
.join(bin_file_name());
println!("Debugging {}", file_path.display());
let mut gdb = Command::new("gdb");
let mut gdb = new_command_checked_exists("gdb");
gdb.args([
format!("{}", file_path.display()).as_str(),
"-ex",
@ -27,7 +26,7 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) {
#[test]
fn have_gdb_installed() {
let output = Command::new("gdb").arg("--version").output();
let output = new_command_checked_exists("gdb").arg("--version").output();
assert!(output.is_ok(), "Failed to run gdb");
let stdout = String::from_utf8_lossy(&output.unwrap().stdout).to_string();
assert!(stdout.contains("GNU gdb"));

View File

@ -13,7 +13,7 @@ use inferno::flamegraph;
use crate::{
cli::{ProfileArgs, ProfileFormat},
commands::util::bin_file_name,
util::{get_kernel_crate, get_target_directory},
util::{get_kernel_crate, get_target_directory, new_command_checked_exists},
};
use regex::Regex;
use std::{
@ -21,7 +21,7 @@ use std::{
fs::File,
io::{BufRead, Write},
path::PathBuf,
process::{Command, Stdio},
process::Stdio,
thread, time,
};
@ -92,7 +92,7 @@ fn do_collect_stack_traces(args: &ProfileArgs) {
];
gdb_args.append(&mut vec![backtrace_cmd_seq; *samples].concat());
Command::new("gdb")
new_command_checked_exists("gdb")
.args(gdb_args)
.stdout(Stdio::piped())
.spawn()
@ -119,7 +119,7 @@ fn do_collect_stack_traces(args: &ProfileArgs) {
}
gdb_output.clear();
thread::sleep(time::Duration::from_secs_f64(*interval));
let _ = Command::new("kill")
let _ = new_command_checked_exists("kill")
.args(["-INT", &format!("{}", gdb_process.id())])
.output();
}

View File

@ -2,7 +2,7 @@
use std::process::Command;
use crate::util::get_kernel_crate;
use crate::util::{get_kernel_crate, new_command_checked_exists};
pub const COMMON_CARGO_ARGS: &[&str] = &[
"-Zbuild-std=core,alloc,compiler_builtins",
@ -12,7 +12,7 @@ pub const COMMON_CARGO_ARGS: &[&str] = &[
pub const DEFAULT_TARGET_RELPATH: &str = "osdk";
pub fn cargo() -> Command {
Command::new("cargo")
new_command_checked_exists("cargo")
}
pub fn profile_name_adapter(profile: &str) -> &str {

View File

@ -29,6 +29,7 @@ use crate::{
config::unix_args::apply_kv_array,
error::Errno,
error_msg,
util::new_command_checked_exists,
};
/// The global configuration for the OSDK actions.
@ -155,7 +156,7 @@ fn canonicalize_and_eval(action_scheme: &mut ActionScheme, workdir: &PathBuf) {
/// This function is used to evaluate the string using the host's shell recursively
/// in order.
pub fn eval(cwd: impl AsRef<Path>, s: &String) -> io::Result<String> {
let mut eval = process::Command::new("bash");
let mut eval = new_command_checked_exists("bash");
eval.arg("-c");
eval.arg(format!("echo \"{}\"", s));
eval.current_dir(cwd.as_ref());

View File

@ -14,6 +14,7 @@ pub enum Errno {
BadCrateName = 9,
NoKernelCrate = 10,
TooManyCrates = 11,
ExecutableNotFound = 12,
}
/// Print error message to console

View File

@ -23,7 +23,7 @@ pub fn ostd_dep() -> String {
}
fn cargo() -> Command {
Command::new("cargo")
new_command_checked_exists("cargo")
}
/// Create a new library crate with cargo
@ -163,6 +163,22 @@ pub fn get_kernel_crate() -> CrateInfo {
}
}
/// Check if an OSDK dependent executable (e.g. QEMU) exists.
///
/// If it exists, create a command. If not, print an error message and exit the
/// process.
pub fn new_command_checked_exists(executable: impl AsRef<Path>) -> Command {
let executable = executable.as_ref();
if which::which(executable).is_err() {
error_msg!(
"Executable {:?} cannot be found in PATH, please install the corresponding package",
executable
);
std::process::exit(Errno::ExecutableNotFound as _);
}
Command::new(executable)
}
fn package_contains_ostd_main(package: &serde_json::Value) -> bool {
let src_path = {
let targets = package.get("targets").unwrap().as_array().unwrap();
@ -262,13 +278,15 @@ pub fn trace_panic_from_log(qemu_log: File, bin_path: PathBuf) {
let mut stack_num = 0;
let pc_matcher = regex::Regex::new(r" - pc (0x[0-9a-fA-F]+)").unwrap();
let exe = bin_path.to_string_lossy();
let mut addr2line = Command::new("addr2line");
let mut addr2line = new_command_checked_exists("addr2line");
addr2line.args(["-e", &exe]);
let mut addr2line_proc = addr2line
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.unwrap();
for line in lines.into_iter().rev() {
if line.contains("Printing stack trace:") {
println!("[OSDK] The kernel seems panicked. Parsing stack trace for source lines:");