Refactor the parsing of OSDK crate types

This commit is contained in:
Zhang Junyang
2025-02-09 17:53:02 +08:00
committed by Tate, Hongliang Tian
parent fecf766771
commit 32a6025819
11 changed files with 131 additions and 101 deletions

View File

@ -303,7 +303,7 @@ impl DebugProfileOutArgs {
.display() .display()
) )
} else { } else {
let crate_name = crate::util::get_current_crate_info().name; let crate_name = crate::util::get_current_crates().remove(0).name;
let time_stamp = std::time::SystemTime::now(); let time_stamp = std::time::SystemTime::now();
let time_stamp: DateTime<Local> = time_stamp.into(); let time_stamp: DateTime<Local> = time_stamp.into();
let time_stamp = time_stamp.format("%H%M%S"); let time_stamp = time_stamp.format("%H%M%S");

View File

@ -17,7 +17,7 @@ use crate::{
bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta}, bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta},
file::BundleFile, file::BundleFile,
}, },
util::{get_current_crate_info, hard_link_or_copy}, util::{get_current_crates, hard_link_or_copy},
}; };
pub fn make_install_bzimage( pub fn make_install_bzimage(
@ -27,7 +27,7 @@ pub fn make_install_bzimage(
linux_x86_legacy_boot: bool, linux_x86_legacy_boot: bool,
encoding: PayloadEncoding, encoding: PayloadEncoding,
) -> AsterBin { ) -> AsterBin {
let target_name = get_current_crate_info().name; let target_name = get_current_crates().remove(0).name;
let image_type = if linux_x86_legacy_boot { let image_type = if linux_x86_legacy_boot {
BzImageType::Legacy32 BzImageType::Legacy32
} else { } else {

View File

@ -16,7 +16,7 @@ use crate::{
scheme::{ActionChoice, BootProtocol}, scheme::{ActionChoice, BootProtocol},
Config, Config,
}, },
util::{get_current_crate_info, hard_link_or_copy}, util::{get_current_crates, hard_link_or_copy},
}; };
pub fn create_bootdev_image( pub fn create_bootdev_image(
@ -26,7 +26,7 @@ pub fn create_bootdev_image(
config: &Config, config: &Config,
action: ActionChoice, action: ActionChoice,
) -> AsterVmImage { ) -> AsterVmImage {
let target_name = get_current_crate_info().name; let target_name = get_current_crates().remove(0).name;
let iso_root = &target_dir.as_ref().join("iso_root"); let iso_root = &target_dir.as_ref().join("iso_root");
let action = match &action { let action = match &action {
ActionChoice::Run => &config.run, ActionChoice::Run => &config.run,
@ -108,7 +108,7 @@ fn generate_grub_cfg(
initramfs_path: Option<String>, initramfs_path: Option<String>,
protocol: &BootProtocol, protocol: &BootProtocol,
) -> String { ) -> String {
let target_name = get_current_crate_info().name; let target_name = get_current_crates().remove(0).name;
let grub_cfg = include_str!("grub.cfg.template").to_string(); let grub_cfg = include_str!("grub.cfg.template").to_string();
// Delete the first two lines that notes the file a template file. // Delete the first two lines that notes the file a template file.

View File

@ -29,7 +29,7 @@ use crate::{
}, },
error::Errno, error::Errno,
error_msg, error_msg,
util::{get_cargo_metadata, get_current_crate_info, get_target_directory}, util::{get_cargo_metadata, get_current_crates, get_target_directory},
}; };
pub fn execute_build_command(config: &Config, build_args: &BuildArgs) { pub fn execute_build_command(config: &Config, build_args: &BuildArgs) {
@ -41,8 +41,22 @@ pub fn execute_build_command(config: &Config, build_args: &BuildArgs) {
if !osdk_output_directory.exists() { if !osdk_output_directory.exists() {
std::fs::create_dir_all(&osdk_output_directory).unwrap(); std::fs::create_dir_all(&osdk_output_directory).unwrap();
} }
let target_info = get_current_crate_info();
let bundle_path = osdk_output_directory.join(target_info.name); let targets = get_current_crates();
let mut target_info = None;
for target in targets {
if target.is_kernel_crate {
target_info = Some(target);
break;
}
}
let target_info = target_info.unwrap_or_else(|| {
error_msg!("No kernel crate found in the current workspace");
process::exit(Errno::NoKernelCrate as _);
});
let bundle_path = osdk_output_directory.join(target_info.name.clone());
let action = if build_args.for_test { let action = if build_args.for_test {
ActionChoice::Test ActionChoice::Test
@ -71,8 +85,8 @@ pub fn create_base_and_cached_build(
let base_crate_path = osdk_output_directory.as_ref().join("base"); let base_crate_path = osdk_output_directory.as_ref().join("base");
new_base_crate( new_base_crate(
&base_crate_path, &base_crate_path,
&get_current_crate_info().name, &get_current_crates().remove(0).name,
get_current_crate_info().path, get_current_crates().remove(0).path,
false, false,
); );
let original_dir = std::env::current_dir().unwrap(); let original_dir = std::env::current_dir().unwrap();
@ -251,7 +265,7 @@ fn build_kernel_elf(
.as_ref() .as_ref()
.join(&target_os_string) .join(&target_os_string)
.join(profile_name_adapter(profile)) .join(profile_name_adapter(profile))
.join(get_current_crate_info().name); .join(get_current_crates().remove(0).name);
AsterBin::new( AsterBin::new(
aster_bin_path, aster_bin_path,
@ -262,7 +276,7 @@ fn build_kernel_elf(
has_multiboot_header: true, has_multiboot_header: true,
has_multiboot2_header: true, has_multiboot2_header: true,
}), }),
get_current_crate_info().version, get_current_crates().remove(0).version,
false, false,
) )
} }

View File

@ -3,7 +3,7 @@
use crate::{ use crate::{
cli::DebugArgs, cli::DebugArgs,
commands::util::bin_file_name, commands::util::bin_file_name,
util::{get_current_crate_info, get_target_directory}, util::{get_current_crates, get_target_directory},
}; };
use std::process::Command; use std::process::Command;
@ -12,7 +12,7 @@ pub fn execute_debug_command(_profile: &str, args: &DebugArgs) {
let file_path = get_target_directory() let file_path = get_target_directory()
.join("osdk") .join("osdk")
.join(get_current_crate_info().name) .join(get_current_crates().remove(0).name)
.join(bin_file_name()); .join(bin_file_name());
println!("Debugging {}", file_path.display()); println!("Debugging {}", file_path.display());

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_current_crate_info, get_target_directory}, util::{get_current_crates, get_target_directory},
}; };
use regex::Regex; use regex::Regex;
use std::{collections::HashMap, fs::File, io::Write, path::PathBuf, process::Command}; use std::{collections::HashMap, fs::File, io::Write, path::PathBuf, process::Command};
@ -45,7 +45,7 @@ fn do_parse_stack_traces(target_file: &PathBuf, args: &ProfileArgs) {
fn do_collect_stack_traces(args: &ProfileArgs) { fn do_collect_stack_traces(args: &ProfileArgs) {
let file_path = get_target_directory() let file_path = get_target_directory()
.join("osdk") .join("osdk")
.join(get_current_crate_info().name) .join(get_current_crates().remove(0).name)
.join(bin_file_name()); .join(bin_file_name());
let remote = &args.remote; let remote = &args.remote;

View File

@ -12,14 +12,27 @@ use crate::{
config::{scheme::ActionChoice, Config}, config::{scheme::ActionChoice, Config},
error::Errno, error::Errno,
error_msg, error_msg,
util::{get_current_crate_info, get_target_directory}, util::{get_current_crates, get_target_directory},
warn_msg, warn_msg,
}; };
pub fn execute_run_command(config: &Config, gdb_server_args: Option<&str>) { pub fn execute_run_command(config: &Config, gdb_server_args: Option<&str>) {
let cargo_target_directory = get_target_directory(); let cargo_target_directory = get_target_directory();
let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH); let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH);
let target_name = get_current_crate_info().name;
let targets = get_current_crates();
let mut target_name = None;
for target in targets {
if target.is_kernel_crate {
target_name = Some(target.name);
break;
}
}
let target_name = target_name.unwrap_or_else(|| {
error_msg!("No kernel crate found in the current workspace");
exit(Errno::NoKernelCrate as _);
});
let mut config = config.clone(); let mut config = config.clone();
@ -173,7 +186,7 @@ mod gdb {
mod vsc { mod vsc {
use crate::{ use crate::{
commands::util::bin_file_name, commands::util::bin_file_name,
util::{get_cargo_metadata, get_current_crate_info}, util::{get_cargo_metadata, get_current_crates},
}; };
use serde_json::{from_str, Value}; use serde_json::{from_str, Value};
use std::{ use std::{
@ -287,7 +300,7 @@ mod vsc {
) -> Result<(), std::io::Error> { ) -> Result<(), std::io::Error> {
let contents = include_str!("launch.json.template") let contents = include_str!("launch.json.template")
.replace("#PROFILE#", profile) .replace("#PROFILE#", profile)
.replace("#CRATE_NAME#", &get_current_crate_info().name) .replace("#CRATE_NAME#", &get_current_crates().remove(0).name)
.replace("#BIN_NAME#", &bin_file_name()) .replace("#BIN_NAME#", &bin_file_name())
.replace( .replace(
"#ADDR_PORT#", "#ADDR_PORT#",

View File

@ -9,21 +9,19 @@ use crate::{
config::{scheme::ActionChoice, Config}, config::{scheme::ActionChoice, Config},
error::Errno, error::Errno,
error_msg, error_msg,
util::{ util::{get_current_crates, get_target_directory},
get_cargo_metadata, get_current_crate_info, get_target_directory, parse_package_id_string,
},
}; };
pub fn execute_test_command(config: &Config, args: &TestArgs) { pub fn execute_test_command(config: &Config, args: &TestArgs) {
let crates = get_workspace_default_members(); let crates = get_current_crates();
for crate_path in crates { for crate_info in crates {
std::env::set_current_dir(crate_path).unwrap(); std::env::set_current_dir(crate_info.path).unwrap();
test_current_crate(config, args); test_current_crate(config, args);
} }
} }
pub fn test_current_crate(config: &Config, args: &TestArgs) { pub fn test_current_crate(config: &Config, args: &TestArgs) {
let current_crate = get_current_crate_info(); let current_crate = get_current_crates().remove(0);
let cargo_target_directory = get_target_directory(); let cargo_target_directory = get_target_directory();
let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH); let osdk_output_directory = cargo_target_directory.join(DEFAULT_TARGET_RELPATH);
// Use a different name for better separation and reusability of `run` and `test` // Use a different name for better separation and reusability of `run` and `test`
@ -87,7 +85,7 @@ pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?});
fs::write(&main_rs_path, main_rs_content).unwrap(); fs::write(&main_rs_path, main_rs_content).unwrap();
// Build the kernel with the given base crate // Build the kernel with the given base crate
let target_name = get_current_crate_info().name; let target_name = get_current_crates().remove(0).name;
let default_bundle_directory = osdk_output_directory.join(target_name); let default_bundle_directory = osdk_output_directory.join(target_name);
let original_dir = std::env::current_dir().unwrap(); let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&target_crate_dir).unwrap(); std::env::set_current_dir(&target_crate_dir).unwrap();
@ -104,20 +102,3 @@ pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?});
bundle.run(config, ActionChoice::Test); bundle.run(config, ActionChoice::Test);
} }
fn get_workspace_default_members() -> Vec<String> {
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
let default_members = metadata
.get("workspace_default_members")
.unwrap()
.as_array()
.unwrap();
default_members
.iter()
.map(|value| {
let default_member = value.as_str().unwrap();
let crate_info = parse_package_id_string(default_member);
crate_info.path
})
.collect()
}

View File

@ -2,7 +2,7 @@
use std::process::Command; use std::process::Command;
use crate::util::get_current_crate_info; use crate::util::get_current_crates;
pub const COMMON_CARGO_ARGS: &[&str] = &[ pub const COMMON_CARGO_ARGS: &[&str] = &[
"-Zbuild-std=core,alloc,compiler_builtins", "-Zbuild-std=core,alloc,compiler_builtins",
@ -23,7 +23,7 @@ pub fn profile_name_adapter(profile: &str) -> &str {
} }
pub fn bin_file_name() -> String { pub fn bin_file_name() -> String {
get_current_crate_info().name + "-osdk-bin" get_current_crates().remove(0).name + "-osdk-bin"
} }
pub(crate) fn is_tdx_enabled() -> bool { pub(crate) fn is_tdx_enabled() -> bool {

View File

@ -12,6 +12,7 @@ pub enum Errno {
BuildCrate = 7, BuildCrate = 7,
RunBundle = 8, RunBundle = 8,
BadCrateName = 9, BadCrateName = 9,
NoKernelCrate = 10,
} }
/// Print error message to console /// Print error message to console

View File

@ -1,11 +1,13 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use std::{ use std::{
collections::HashMap,
ffi::OsStr, ffi::OsStr,
fs::{self, File}, fs::{self, File},
io::{BufRead, BufReader, Result, Write}, io::{BufRead, BufReader, Result, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Command, process::Command,
sync::{LazyLock, Mutex},
}; };
use crate::{error::Errno, error_msg}; use crate::{error::Errno, error_msg};
@ -73,41 +75,76 @@ pub fn get_target_directory() -> PathBuf {
.into() .into()
} }
/// Information about an OSDK crate.
#[derive(Debug, Clone)]
pub struct CrateInfo { pub struct CrateInfo {
pub name: String, pub name: String,
pub version: String, pub version: String,
pub path: String, pub path: String,
/// Whether the crate is a kernel crate.
///
/// If a crate contains the `ostd::main` macro, it is a kernel crate (akin
/// to the binary crate in a normal Rust project, but it is a library from
/// the perspective of Cargo).
pub is_kernel_crate: bool,
} }
/// Retrieve the default member in the workspace. /// Gets the default crates in the current directory.
/// pub fn get_current_crates() -> Vec<CrateInfo> {
/// If there is only one kernel crate, return that crate; // To avoid the overhead of parsing the Cargo metadata and reading the
/// If there are multiple kernel crates or no kernel crates in the workspace, // source multiple times.
/// this function will exit with an error. static MEMOIZED: LazyLock<Mutex<HashMap<PathBuf, Vec<CrateInfo>>>> =
/// LazyLock::new(|| Mutex::new(HashMap::new()));
/// A crate is considered a kernel crate if it utilizes the `ostd::main` macro.
fn get_default_member(metadata: &serde_json::Value) -> &str { let current_dir = std::env::current_dir().unwrap();
let mut memoized = MEMOIZED.lock().unwrap();
if let Some(crates) = memoized.get(&current_dir) {
return crates.clone();
}
// If the current directory's crate info is not memoized, parse the Cargo
// metadata.
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap();
let packages = metadata.get("packages").unwrap().as_array().unwrap();
let default_members = metadata let default_members = metadata
.get("workspace_default_members") .get("workspace_default_members")
.unwrap() .unwrap()
.as_array() .as_array()
.unwrap(); .unwrap();
if default_members.len() == 1 { let mut result = Vec::new();
return default_members[0].as_str().unwrap();
}
let packages: Vec<_> = { for member in default_members {
let packages = metadata.get("packages").unwrap().as_array().unwrap(); let member_meta = packages
packages
.iter() .iter()
.filter(|package| { .find(|p| {
let id = package.get("id").unwrap(); let Some(p_id) = p.get("id") else {
if !default_members.contains(id) {
return false; return false;
};
p_id == member
})
.unwrap();
let is_kernel_crate = package_contains_ostd_main(member_meta);
let parsed_id = parse_package_id_string(member.as_str().unwrap());
result.push(CrateInfo {
name: parsed_id.name,
version: parsed_id.version,
path: parsed_id.path,
is_kernel_crate,
});
} }
memoized.insert(current_dir, result.clone());
result
}
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();
if targets.len() != 1 { if targets.len() != 1 {
@ -121,25 +158,10 @@ fn get_default_member(metadata: &serde_json::Value) -> &str {
syn::parse_file(&content).unwrap() syn::parse_file(&content).unwrap()
}; };
contains_ostd_main_macro(&file) file_contains_ostd_main_macro(&file)
})
.collect()
};
if packages.is_empty() {
error_msg!("OSDK requires there's at least one kernel package. Please navigate to the kernel package directory or the workspace root and run the command.");
std::process::exit(Errno::BuildCrate as _);
} }
if packages.len() >= 2 { fn file_contains_ostd_main_macro(file: &syn::File) -> bool {
error_msg!("OSDK requires there's at most one kernel package in the workspace. Please navigate to the kernel package directory and run the command.");
std::process::exit(Errno::BuildCrate as _);
}
packages[0].get("id").unwrap().as_str().unwrap()
}
fn contains_ostd_main_macro(file: &syn::File) -> bool {
for item in &file.items { for item in &file.items {
let syn::Item::Fn(item_fn) = item else { let syn::Item::Fn(item_fn) = item else {
continue; continue;
@ -156,14 +178,13 @@ fn contains_ostd_main_macro(file: &syn::File) -> bool {
false false
} }
pub fn get_current_crate_info() -> CrateInfo { struct ParsedID {
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>).unwrap(); pub name: String,
pub version: String,
let default_member = get_default_member(&metadata); pub path: String,
parse_package_id_string(default_member)
} }
pub fn parse_package_id_string(package_id: &str) -> CrateInfo { fn parse_package_id_string(package_id: &str) -> ParsedID {
// Prior to 202403 (Rust 1.77.1), the package id string here is in the form of // Prior to 202403 (Rust 1.77.1), the package id string here is in the form of
// "<crate_name> <crate_version> (path+file://<crate_path>)". // "<crate_name> <crate_version> (path+file://<crate_path>)".
// After that, it's // After that, it's
@ -173,7 +194,7 @@ pub fn parse_package_id_string(package_id: &str) -> CrateInfo {
// After 1.77.1 // After 1.77.1
if package_id.contains('@') { if package_id.contains('@') {
let package_id_segments = package_id.split(['#', '@']).collect::<Vec<&str>>(); let package_id_segments = package_id.split(['#', '@']).collect::<Vec<&str>>();
CrateInfo { ParsedID {
name: package_id_segments[1].to_string(), name: package_id_segments[1].to_string(),
version: package_id_segments[2].to_string(), version: package_id_segments[2].to_string(),
path: package_id_segments[0] path: package_id_segments[0]
@ -185,7 +206,7 @@ pub fn parse_package_id_string(package_id: &str) -> CrateInfo {
let path = package_id_segments[0] let path = package_id_segments[0]
.trim_start_matches("path+file://") .trim_start_matches("path+file://")
.to_string(); .to_string();
CrateInfo { ParsedID {
name: PathBuf::from(path.clone()) name: PathBuf::from(path.clone())
.file_name() .file_name()
.unwrap() .unwrap()
@ -199,7 +220,7 @@ pub fn parse_package_id_string(package_id: &str) -> CrateInfo {
} else { } else {
// Before 1.77.1 // Before 1.77.1
let default_member = package_id.split(' ').collect::<Vec<&str>>(); let default_member = package_id.split(' ').collect::<Vec<&str>>();
CrateInfo { ParsedID {
name: default_member[0].to_string(), name: default_member[0].to_string(),
version: default_member[1].to_string(), version: default_member[1].to_string(),
path: default_member[2] path: default_member[2]