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

View File

@ -26,6 +26,7 @@ serde_json = "1.0.111"
shlex = "1.3.0" shlex = "1.3.0"
syn = { version = "2.0.52", features = ["extra-traits", "full", "parsing", "printing"] } syn = { version = "2.0.52", features = ["extra-traits", "full", "parsing", "printing"] }
toml = { version = "0.8.8", features = ["preserve_order"] } toml = { version = "0.8.8", features = ["preserve_order"] }
which = "8.0.0"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0.13" 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. /// Get the default architecture implied by the host rustc's default architecture.
pub fn get_default_arch() -> Arch { pub fn get_default_arch() -> Arch {
let output = std::process::Command::new("rustc") let output = crate::util::new_command_checked_exists("rustc")
.arg("-vV") .arg("-vV")
.output() .output()
.expect("Failed to run rustc to get the host target"); .expect("Failed to run rustc to get the host target");

View File

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

View File

@ -4,7 +4,6 @@ use std::{
fs::{File, OpenOptions}, fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write}, io::{Read, Seek, SeekFrom, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command,
}; };
use linux_bzimage_builder::{ use linux_bzimage_builder::{
@ -17,7 +16,7 @@ use crate::{
bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta}, bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta},
file::BundleFile, 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( 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 { if strip {
// We use rust-strip to reduce the kernel image size. // 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(elf.path())
.arg("-o") .arg("-o")
.arg(result_elf_path.as_os_str()) .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 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![ let mut rustflags = vec![
"-Cdebuginfo=2", "-Cdebuginfo=2",
"-Ccode-model=kernel", "-Ccode-model=kernel",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,6 +29,7 @@ use crate::{
config::unix_args::apply_kv_array, config::unix_args::apply_kv_array,
error::Errno, error::Errno,
error_msg, error_msg,
util::new_command_checked_exists,
}; };
/// The global configuration for the OSDK actions. /// 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 /// This function is used to evaluate the string using the host's shell recursively
/// in order. /// in order.
pub fn eval(cwd: impl AsRef<Path>, s: &String) -> io::Result<String> { 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("-c");
eval.arg(format!("echo \"{}\"", s)); eval.arg(format!("echo \"{}\"", s));
eval.current_dir(cwd.as_ref()); eval.current_dir(cwd.as_ref());

View File

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

View File

@ -23,7 +23,7 @@ pub fn ostd_dep() -> String {
} }
fn cargo() -> Command { fn cargo() -> Command {
Command::new("cargo") new_command_checked_exists("cargo")
} }
/// Create a new library crate with 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 { fn package_contains_ostd_main(package: &serde_json::Value) -> bool {
let src_path = { let src_path = {
let targets = package.get("targets").unwrap().as_array().unwrap(); 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 mut stack_num = 0;
let pc_matcher = regex::Regex::new(r" - pc (0x[0-9a-fA-F]+)").unwrap(); let pc_matcher = regex::Regex::new(r" - pc (0x[0-9a-fA-F]+)").unwrap();
let exe = bin_path.to_string_lossy(); 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]); addr2line.args(["-e", &exe]);
let mut addr2line_proc = addr2line let mut addr2line_proc = addr2line
.stdin(std::process::Stdio::piped()) .stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.spawn() .spawn()
.unwrap(); .unwrap();
for line in lines.into_iter().rev() { for line in lines.into_iter().rev() {
if line.contains("Printing stack trace:") { if line.contains("Printing stack trace:") {
println!("[OSDK] The kernel seems panicked. Parsing stack trace for source lines:"); println!("[OSDK] The kernel seems panicked. Parsing stack trace for source lines:");