Delay the canonicalization until after processing the CLI arguments

This commit is contained in:
Jianfeng Jiang 2024-08-28 11:15:33 +00:00 committed by Tate, Hongliang Tian
parent 539984bbed
commit b3b5e136bd
4 changed files with 101 additions and 110 deletions

2
osdk/Cargo.lock generated
View File

@ -146,7 +146,7 @@ dependencies = [
[[package]]
name = "cargo-osdk"
version = "0.8.0"
version = "0.8.1"
dependencies = [
"assert_cmd",
"clap",

View File

@ -1,24 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
//! The module implementing the evaluation feature.
use std::{io, path::Path, process};
/// 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");
eval.arg("-c");
eval.arg(format!("echo \"{}\"", s));
eval.current_dir(cwd.as_ref());
let output = eval.output()?;
if !output.stderr.is_empty() {
println!(
"[Info] {}",
String::from_utf8_lossy(&output.stderr).trim_end_matches('\n')
);
}
Ok(String::from_utf8_lossy(&output.stdout)
.trim_end_matches('\n')
.to_string())
}

View File

@ -12,7 +12,7 @@ use serde::{de, Deserialize, Deserializer, Serialize};
use super::scheme::Scheme;
use crate::{config::scheme::QemuScheme, error::Errno, error_msg, util::get_cargo_metadata};
use crate::{error::Errno, error_msg, util::get_cargo_metadata};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OsdkMeta {
@ -128,82 +128,7 @@ fn deserialize_toml_manifest(path: impl AsRef<Path>) -> Option<TomlManifest> {
for scheme in manifest.map.values_mut() {
scheme.work_dir = Some(cwd.to_path_buf());
}
// Canonicalize all the path fields
let canonicalize = |target: &mut PathBuf| {
let last_cwd = std::env::current_dir().unwrap();
std::env::set_current_dir(cwd).unwrap();
*target = target.canonicalize().unwrap_or_else(|err| {
error_msg!(
"Cannot canonicalize path `{}`: {}",
target.to_string_lossy(),
err,
);
std::env::set_current_dir(&last_cwd).unwrap();
process::exit(Errno::GetMetadata as _);
});
std::env::set_current_dir(last_cwd).unwrap();
};
let canonicalize_scheme = |scheme: &mut Scheme| {
macro_rules! canonicalize_paths_in_scheme {
($scheme:expr) => {
if let Some(ref mut boot) = $scheme.boot {
if let Some(ref mut initramfs) = boot.initramfs {
canonicalize(initramfs);
}
}
if let Some(ref mut qemu) = $scheme.qemu {
if let Some(ref mut qemu_path) = qemu.path {
canonicalize(qemu_path);
}
}
if let Some(ref mut grub) = $scheme.grub {
if let Some(ref mut grub_mkrescue_path) = grub.grub_mkrescue {
canonicalize(grub_mkrescue_path);
}
}
};
}
canonicalize_paths_in_scheme!(scheme);
if let Some(ref mut run) = scheme.run {
canonicalize_paths_in_scheme!(run);
}
if let Some(ref mut test) = scheme.test {
canonicalize_paths_in_scheme!(test);
}
};
canonicalize_scheme(&mut manifest.default_scheme);
for scheme in manifest.map.values_mut() {
canonicalize_scheme(scheme);
}
// Do evaluations on the need to be evaluated string field, namely,
// QEMU arguments.
use super::eval::eval;
let eval_scheme = |scheme: &mut Scheme| {
let eval_qemu = |qemu: &mut Option<QemuScheme>| {
if let Some(ref mut qemu) = qemu {
if let Some(ref mut args) = qemu.args {
*args = match eval(cwd, args) {
Ok(v) => v,
Err(e) => {
error_msg!("Failed to evaluate qemu args: {:#?}", e);
process::exit(Errno::ParseMetadata as _);
}
}
}
}
};
eval_qemu(&mut scheme.qemu);
if let Some(ref mut run) = scheme.run {
eval_qemu(&mut run.qemu);
}
if let Some(ref mut test) = scheme.test {
eval_qemu(&mut test.qemu);
}
};
eval_scheme(&mut manifest.default_scheme);
for scheme in manifest.map.values_mut() {
eval_scheme(scheme);
}
Some(manifest)
}

View File

@ -5,8 +5,6 @@
//! arguments if the arguments is missing, e.g., the path of QEMU. The final configuration is stored in `BuildConfig`,
//! `RunConfig` and `TestConfig`. These `*Config` are used for `build`, `run` and `test` subcommand.
mod eval;
pub mod manifest;
pub mod scheme;
pub mod unix_args;
@ -14,7 +12,11 @@ pub mod unix_args;
#[cfg(test)]
mod test;
use std::{env, path::PathBuf};
use std::{
env, io,
path::{Path, PathBuf},
process,
};
use linux_bzimage_builder::PayloadEncoding;
use scheme::{
@ -25,6 +27,8 @@ use crate::{
arch::{get_default_arch, Arch},
cli::CommonArgs,
config::unix_args::apply_kv_array,
error::Errno,
error_msg,
};
/// The global configuration for the OSDK actions.
@ -37,7 +41,11 @@ pub struct Config {
pub test: Action,
}
fn apply_args_before_finalize(action_scheme: &mut ActionScheme, args: &CommonArgs) {
fn apply_args_before_finalize(
action_scheme: &mut ActionScheme,
args: &CommonArgs,
workdir: &PathBuf,
) {
if action_scheme.grub.is_none() {
action_scheme.grub = Some(GrubScheme::default());
}
@ -61,7 +69,11 @@ fn apply_args_before_finalize(action_scheme: &mut ActionScheme, args: &CommonArg
}
}
if let Some(initramfs) = &args.initramfs {
boot.initramfs = Some(initramfs.clone());
let Ok(initramfs) = initramfs.canonicalize() else {
error_msg!("The initramfs path provided with argument `--initramfs` does not match any files.");
process::exit(Errno::GetMetadata as _);
};
boot.initramfs = Some(initramfs);
}
if let Some(boot_method) = args.boot_method {
boot.method = Some(boot_method);
@ -73,12 +85,90 @@ fn apply_args_before_finalize(action_scheme: &mut ActionScheme, args: &CommonArg
}
if let Some(ref mut qemu) = action_scheme.qemu {
if let Some(path) = &args.qemu_exe {
qemu.path = Some(path.clone());
let Ok(qemu_path) = path.canonicalize() else {
error_msg!(
"The QEMU path provided with argument `--qemu-exe` does not match any files."
);
process::exit(Errno::GetMetadata as _);
};
qemu.path = Some(qemu_path);
}
if let Some(bootdev_options) = &args.bootdev_append_options {
qemu.bootdev_append_options = Some(bootdev_options.clone());
}
}
canonicalize_and_eval(action_scheme, workdir);
}
fn canonicalize_and_eval(action_scheme: &mut ActionScheme, workdir: &PathBuf) {
let canonicalize = |target: &mut PathBuf| {
let last_cwd = std::env::current_dir().unwrap();
std::env::set_current_dir(workdir).unwrap();
*target = target.canonicalize().unwrap_or_else(|err| {
error_msg!(
"Cannot canonicalize path `{}`: {}",
target.to_string_lossy(),
err,
);
std::env::set_current_dir(&last_cwd).unwrap();
process::exit(Errno::GetMetadata as _);
});
std::env::set_current_dir(last_cwd).unwrap();
};
if let Some(ref mut boot) = action_scheme.boot {
if let Some(ref mut initramfs) = boot.initramfs {
canonicalize(initramfs);
}
if let Some(ref mut qemu) = action_scheme.qemu {
if let Some(ref mut qemu_path) = qemu.path {
canonicalize(qemu_path);
}
}
if let Some(ref mut grub) = action_scheme.grub {
if let Some(ref mut grub_mkrescue_path) = grub.grub_mkrescue {
canonicalize(grub_mkrescue_path);
}
}
}
// Do evaluations on the need to be evaluated string field, namely,
// QEMU arguments.
if let Some(ref mut qemu) = action_scheme.qemu {
if let Some(ref mut args) = qemu.args {
*args = match eval(workdir, args) {
Ok(v) => v,
Err(e) => {
error_msg!("Failed to evaluate qemu args: {:#?}", e);
process::exit(Errno::ParseMetadata as _);
}
}
}
}
}
/// 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");
eval.arg("-c");
eval.arg(format!("echo \"{}\"", s));
eval.current_dir(cwd.as_ref());
let output = eval.output()?;
if !output.stderr.is_empty() {
println!(
"[Info] {}",
String::from_utf8_lossy(&output.stderr).trim_end_matches('\n')
);
}
Ok(String::from_utf8_lossy(&output.stdout)
.trim_end_matches('\n')
.to_string())
}
fn apply_args_after_finalize(action: &mut Action, args: &CommonArgs) {
@ -111,7 +201,7 @@ impl Config {
let run = {
let mut run = scheme.run.clone().unwrap_or_default();
run.inherit(&default_scheme);
apply_args_before_finalize(&mut run, common_args);
apply_args_before_finalize(&mut run, common_args, scheme.work_dir.as_ref().unwrap());
let mut run = run.finalize(target_arch);
apply_args_after_finalize(&mut run, common_args);
check_compatibility(run.grub.boot_protocol, run.build.encoding.clone());
@ -120,7 +210,7 @@ impl Config {
let test = {
let mut test = scheme.test.clone().unwrap_or_default();
test.inherit(&default_scheme);
apply_args_before_finalize(&mut test, common_args);
apply_args_before_finalize(&mut test, common_args, scheme.work_dir.as_ref().unwrap());
let mut test = test.finalize(target_arch);
apply_args_after_finalize(&mut test, common_args);
check_compatibility(test.grub.boot_protocol, test.build.encoding.clone());