Implement OSDK functionalities and opt-in OSDK for asterinas

This commit is contained in:
Zhang Junyang
2024-02-21 16:58:40 +08:00
committed by Tate, Hongliang Tian
parent bc9bce9dea
commit f97d0f1260
103 changed files with 1663 additions and 1295 deletions

View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: MPL-2.0
use linux_bzimage_builder::{make_bzimage, BzImageType};
use std::path::Path;
use std::process::Command;
use std::{
fs::OpenOptions,
io::{Seek, SeekFrom, Write},
};
use crate::bin::{AsterBin, AsterBinType, AsterBzImageMeta, AsterElfMeta};
use crate::config_manager::boot::BootProtocol;
use crate::utils::get_current_crate_info;
pub fn make_install_bzimage(
install_dir: impl AsRef<Path>,
aster_elf: &AsterBin,
protocol: &BootProtocol,
) -> AsterBin {
let target_name = get_current_crate_info().name;
let image_type = match protocol {
BootProtocol::LinuxLegacy32 => BzImageType::Legacy32,
BootProtocol::LinuxEfiHandover64 => BzImageType::Efi64,
_ => unreachable!(),
};
// Make the `bzImage`-compatible kernel image and place it in the boot directory.
let install_path = install_dir.as_ref().join(&target_name);
info!("Building bzImage");
make_bzimage(&install_path, image_type, &aster_elf.path);
AsterBin {
path: install_path,
typ: AsterBinType::BzImage(AsterBzImageMeta {
support_legacy32_boot: matches!(protocol, BootProtocol::LinuxLegacy32),
support_efi_boot: false,
support_efi_handover: matches!(protocol, BootProtocol::LinuxEfiHandover64),
}),
version: aster_elf.version.clone(),
sha256sum: "TODO".to_string(),
stripped: aster_elf.stripped,
}
}
pub fn strip_elf_for_qemu(install_dir: impl AsRef<Path>, elf: &AsterBin) -> AsterBin {
let stripped_elf_path = {
let elf_name = elf.path.file_name().unwrap().to_str().unwrap().to_string();
install_dir.as_ref().join(elf_name + ".stripped.elf")
};
// We use rust-strip to reduce the kernel image size.
let status = Command::new("rust-strip")
.arg(&elf.path)
.arg("-o")
.arg(stripped_elf_path.as_os_str())
.status();
match status {
Ok(status) => {
if !status.success() {
panic!("Failed to strip kernel elf.");
}
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => panic!(
"`rust-strip` command not found. Please
try `cargo install cargo-binutils` and then rerun."
),
_ => panic!("Strip kernel elf failed, err:{:#?}", err),
},
}
// Because QEMU denies a x86_64 multiboot ELF file (GRUB2 accept it, btw),
// modify `em_machine` to pretend to be an x86 (32-bit) ELF image,
//
// https://github.com/qemu/qemu/blob/950c4e6c94b15cd0d8b63891dddd7a8dbf458e6a/hw/i386/multiboot.c#L197
// Set EM_386 (0x0003) to em_machine.
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open(&stripped_elf_path)
.unwrap();
let bytes: [u8; 2] = [0x03, 0x00];
file.seek(SeekFrom::Start(18)).unwrap();
file.write_all(&bytes).unwrap();
file.flush().unwrap();
AsterBin {
path: stripped_elf_path,
typ: AsterBinType::Elf(AsterElfMeta {
has_linux_header: false,
has_pvh_header: false,
has_multiboot_header: true,
has_multiboot2_header: true,
}),
version: elf.version.clone(),
sha256sum: "TODO".to_string(),
stripped: true,
}
}

View File

@ -0,0 +1,14 @@
# This template file is used by the runner script to generate the acutal grub.cfg
# AUTOMATICALLY GENERATED FILE, DO NOT EDIT IF YOU KNOW WHAT YOU ARE DOING
# set debug=linux,efi,linuxefi
set timeout_style=#GRUB_TIMEOUT_STYLE#
set timeout=#GRUB_TIMEOUT#
menuentry 'asterinas' {
#GRUB_CMD_KERNEL# #KERNEL# #KERNEL_COMMAND_LINE#
#GRUB_CMD_INITRAMFS#
boot
}

View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: MPL-2.0
use std::fs;
use std::path::{Path, PathBuf};
use crate::bin::AsterBin;
use crate::config_manager::{boot::BootProtocol, BuildConfig};
use crate::utils::get_current_crate_info;
use crate::vm_image::{AsterGrubIsoImageMeta, AsterVmImage, AsterVmImageType};
use super::bin::make_install_bzimage;
pub fn create_bootdev_image(
target_dir: impl AsRef<Path>,
aster_bin: &AsterBin,
initramfs_path: Option<impl AsRef<Path>>,
config: &BuildConfig,
) -> AsterVmImage {
let target_name = get_current_crate_info().name;
let iso_root = &target_dir.as_ref().join("iso_root");
let protocol = &config.manifest.boot.protocol;
// Clear or make the iso dir.
if iso_root.exists() {
fs::remove_dir_all(&iso_root).unwrap();
}
fs::create_dir_all(iso_root.join("boot").join("grub")).unwrap();
// Copy the initramfs to the boot directory.
if let Some(init_path) = &initramfs_path {
fs::copy(
init_path.as_ref().to_str().unwrap(),
iso_root.join("boot").join("initramfs.cpio.gz"),
)
.unwrap();
}
// Make the kernel image and place it in the boot directory.
match protocol {
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => {
make_install_bzimage(&iso_root.join("boot"), aster_bin, protocol);
}
BootProtocol::Multiboot | BootProtocol::Multiboot2 => {
// Copy the kernel image to the boot directory.
let target_path = iso_root.join("boot").join(&target_name);
fs::copy(&aster_bin.path, &target_path).unwrap();
}
};
// Write the grub.cfg file
let initramfs_in_image = if initramfs_path.is_some() {
Some("/boot/initramfs.cpio.gz".to_string())
} else {
None
};
let grub_cfg = generate_grub_cfg(
&config.manifest.kcmd_args.join(" "),
true,
initramfs_in_image,
protocol,
);
let grub_cfg_path = iso_root.join("boot").join("grub").join("grub.cfg");
fs::write(&grub_cfg_path, grub_cfg).unwrap();
// Make the boot device CDROM image using `grub-mkrescue`.
let iso_path = &target_dir.as_ref().join(target_name.to_string() + ".iso");
let grub_mkrescue_bin = &config.manifest.boot.grub_mkrescue.clone().unwrap();
let mut grub_mkrescue_cmd = std::process::Command::new(grub_mkrescue_bin.as_os_str());
grub_mkrescue_cmd
.arg(iso_root.as_os_str())
.arg("-o")
.arg(iso_path);
if !grub_mkrescue_cmd.status().unwrap().success() {
panic!("Failed to run {:#?}.", grub_mkrescue_cmd);
}
AsterVmImage {
path: iso_path.clone(),
typ: AsterVmImageType::GrubIso(AsterGrubIsoImageMeta {
grub_version: get_grub_mkrescue_version(grub_mkrescue_bin),
}),
aster_version: aster_bin.version.clone(),
sha256sum: "TODO".to_string(),
}
}
fn generate_grub_cfg(
kcmdline: &str,
skip_grub_menu: bool,
initramfs_path: Option<String>,
protocol: &BootProtocol,
) -> String {
let target_name = get_current_crate_info().name;
let grub_cfg = include_str!("grub.cfg.template").to_string();
// Delete the first two lines that notes the file a template file.
let grub_cfg = grub_cfg.lines().skip(2).collect::<Vec<&str>>().join("\n");
// Set the timout style and timeout.
let grub_cfg = grub_cfg
.replace(
"#GRUB_TIMEOUT_STYLE#",
if skip_grub_menu { "hidden" } else { "menu" },
)
.replace("#GRUB_TIMEOUT#", if skip_grub_menu { "0" } else { "1" });
// Replace all occurrences of "#KERNEL_COMMAND_LINE#" with the desired value.
let grub_cfg = grub_cfg.replace("#KERNEL_COMMAND_LINE#", kcmdline);
// Replace the grub commands according to the protocol selected.
let aster_bin_path_on_device = PathBuf::from("/boot")
.join(&target_name)
.into_os_string()
.into_string()
.unwrap();
let grub_cfg = match protocol {
BootProtocol::Multiboot => grub_cfg
.replace("#GRUB_CMD_KERNEL#", "multiboot")
.replace("#KERNEL#", &aster_bin_path_on_device)
.replace(
"#GRUB_CMD_INITRAMFS#",
&if let Some(p) = &initramfs_path {
"module --nounzip ".to_owned() + p
} else {
"".to_owned()
},
),
BootProtocol::Multiboot2 => grub_cfg
.replace("#GRUB_CMD_KERNEL#", "multiboot2")
.replace("#KERNEL#", &aster_bin_path_on_device)
.replace(
"#GRUB_CMD_INITRAMFS#",
&if let Some(p) = &initramfs_path {
"module2 --nounzip ".to_owned() + p
} else {
"".to_owned()
},
),
BootProtocol::LinuxLegacy32 | BootProtocol::LinuxEfiHandover64 => grub_cfg
.replace("#GRUB_CMD_KERNEL#", "linux")
.replace("#KERNEL#", &aster_bin_path_on_device)
.replace(
"#GRUB_CMD_INITRAMFS#",
&if let Some(p) = &initramfs_path {
"initrd ".to_owned() + p
} else {
"".to_owned()
},
),
};
grub_cfg
}
fn get_grub_mkrescue_version(grub_mkrescue: &PathBuf) -> String {
let mut cmd = std::process::Command::new(grub_mkrescue);
cmd.arg("--version");
let output = cmd.output().unwrap();
String::from_utf8(output.stdout).unwrap()
}

View File

@ -0,0 +1,134 @@
// SPDX-License-Identifier: MPL-2.0
mod bin;
mod grub;
use std::path::{Path, PathBuf};
use std::process;
use std::str::FromStr;
use bin::strip_elf_for_qemu;
use crate::base_crate::new_base_crate;
use crate::bin::{AsterBin, AsterBinType, AsterElfMeta};
use crate::bundle::{Bundle, BundleManifest};
use crate::cli::CargoArgs;
use crate::config_manager::{qemu::QemuMachine, BuildConfig};
use crate::utils::{get_current_crate_info, get_target_directory};
use crate::{error::Errno, error_msg};
use super::utils::{cargo, COMMON_CARGO_ARGS, DEFAULT_TARGET_RELPATH};
pub fn execute_build_command(config: &BuildConfig) {
let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH);
if !osdk_target_directory.exists() {
std::fs::create_dir_all(&osdk_target_directory).unwrap();
}
let target_info = get_current_crate_info();
let bundle_path = osdk_target_directory.join(&target_info.name);
let _bundle = create_base_and_build(&bundle_path, &osdk_target_directory, &config);
}
pub fn create_base_and_build(
bundle_path: impl AsRef<Path>,
osdk_target_directory: impl AsRef<Path>,
config: &BuildConfig,
) -> Bundle {
let base_crate_path = osdk_target_directory.as_ref().join("base");
new_base_crate(
&base_crate_path,
&get_current_crate_info().name,
&get_current_crate_info().path,
);
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&base_crate_path).unwrap();
let bundle = do_build(&bundle_path, &osdk_target_directory, &config);
std::env::set_current_dir(&original_dir).unwrap();
bundle
}
pub fn do_build(
bundle_path: impl AsRef<Path>,
osdk_target_directory: impl AsRef<Path>,
config: &BuildConfig,
) -> Bundle {
if let Some(ref initramfs) = config.manifest.initramfs {
if !initramfs.exists() {
error_msg!("initramfs file not found: {}", initramfs.display());
process::exit(Errno::BuildCrate as _);
}
};
let mut bundle = Bundle::new(
BundleManifest {
kcmd_args: config.manifest.kcmd_args.clone(),
initramfs: config.manifest.initramfs.clone(),
aster_bin: None,
vm_image: None,
boot: config.manifest.boot.clone(),
qemu: config.manifest.qemu.clone(),
cargo_args: config.cargo_args.clone(),
},
&bundle_path,
);
info!("Building kernel ELF");
let aster_elf = build_kernel_elf(&config.cargo_args);
if matches!(config.manifest.qemu.machine, QemuMachine::Microvm) {
let stripped_elf = strip_elf_for_qemu(&osdk_target_directory, &aster_elf);
bundle.add_aster_bin(&stripped_elf);
}
// TODO: A boot device is required if we use GRUB. Actually you can boot
// a multiboot kernel with Q35 machine directly without a bootloader.
// We are currently ignoring this case.
if matches!(config.manifest.qemu.machine, QemuMachine::Q35) {
info!("Building boot device image");
let bootdev_image = grub::create_bootdev_image(
&osdk_target_directory,
&aster_elf,
config.manifest.initramfs.as_ref(),
&config,
);
bundle.add_vm_image(&bootdev_image);
}
bundle
}
fn build_kernel_elf(args: &CargoArgs) -> AsterBin {
let target_directory = get_target_directory();
let target_json_path = PathBuf::from_str("x86_64-custom.json").unwrap();
let mut command = cargo();
command.arg("build").arg("--target").arg(&target_json_path);
command.args(COMMON_CARGO_ARGS);
command.arg("--profile=".to_string() + &args.profile);
let status = command.status().unwrap();
if !status.success() {
error_msg!("Cargo build failed");
process::exit(Errno::ExecuteCommand as _);
}
let aster_bin_path = PathBuf::from(target_directory)
.join(target_json_path.file_stem().unwrap().to_str().unwrap());
let aster_bin_path = if args.profile == "dev" {
aster_bin_path.join("debug")
} else {
aster_bin_path.join(&args.profile)
}
.join(get_current_crate_info().name);
AsterBin {
path: aster_bin_path,
typ: AsterBinType::Elf(AsterElfMeta {
has_linux_header: false,
has_pvh_header: false,
has_multiboot_header: true,
has_multiboot2_header: true,
}),
version: get_current_crate_info().version,
sha256sum: "TODO".to_string(),
stripped: false,
}
}

View File

@ -2,17 +2,17 @@
use std::process;
use crate::commands::utils::create_target_json;
use crate::error::Errno;
use crate::error_msg;
use super::utils::{cargo, COMMON_CARGO_ARGS};
use crate::{
commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata,
};
pub fn execute_check_command() {
let target_json_path = {
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>);
let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap();
create_target_json(target_directory)
};
let target_json_path = create_target_json();
let mut command = cargo();
command.arg("check").arg("--target").arg(target_json_path);

View File

@ -4,19 +4,15 @@ use std::process;
use super::utils::{cargo, COMMON_CARGO_ARGS};
use crate::{
commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata,
error_msg, commands::utils::create_target_json, error::Errno, error_msg, utils::get_cargo_metadata,
};
pub fn execute_clippy_command() {
let target_json_path = {
let metadata = get_cargo_metadata(None::<&str>, None::<&[&str]>);
let target_directory = metadata.get("target_directory").unwrap().as_str().unwrap();
create_target_json(target_directory)
};
let target_json_path = create_target_json();
let mut command = cargo();
command.arg("clippy").arg("-h");
info!("[Running] cargo clippy -h");
info!("Running `cargo clippy -h`");
let output = command.output().unwrap();
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
@ -28,6 +24,7 @@ pub fn execute_clippy_command() {
let mut command = cargo();
command.arg("clippy").arg("--target").arg(target_json_path);
command.args(COMMON_CARGO_ARGS);
// TODO: Add support for custom clippy args using OSDK commandline rather than hardcode it.
command.args(["--", "-D", "warnings"]);
let status = command.status().unwrap();
if !status.success() {

View File

@ -2,11 +2,15 @@
//! This module contains subcommands of cargo-osdk.
mod build;
mod check;
mod clippy;
mod new;
mod run;
mod test;
mod utils;
pub use self::{
check::execute_check_command, clippy::execute_clippy_command, new::execute_new_command,
run::execute_run_command, test::execute_test_command,
};

View File

@ -0,0 +1,22 @@
#![no_std]
#![no_main]
#![forbid(unsafe_code)]
#[macro_use]
extern crate ktest;
use aster_frame::prelude::*;
#[aster_main]
fn kernel_main() -> ! {
println!("Hello world from guest kernel!");
loop {}
}
#[cfg(ktest)]
mod test {
#[ktest]
fn trivial_test() {
assert_eq!(1 + 1, 2);
}
}

View File

@ -1,5 +1,9 @@
#![no_std]
#[macro_use]
extern crate ktest;
extern crate aster_frame;
#[cfg(ktest)]
mod tests {
#[ktest]
@ -7,4 +11,4 @@ mod tests {
let memory_regions = aster_frame::boot::memory_regions();
assert!(!memory_regions.is_empty());
}
}
}

View File

@ -1,17 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
use std::{fs, path::PathBuf, process, str::FromStr};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fs, process};
use std::ffi::OsStr;
use crate::{
cli::NewArgs,
error::Errno,
error_msg,
utils::{cargo_new_lib, get_cargo_metadata, ASTER_FRAME_DEP},
};
use crate::cli::NewArgs;
use crate::error::Errno;
use crate::error_msg;
use crate::utils::{cargo_new_lib, get_cargo_metadata, ASTER_FRAME_DEP, KTEST_DEP};
pub fn execute_new_command(args: &NewArgs) {
cargo_new_lib(&args.crate_name);
let cargo_metadata = get_cargo_metadata(Some(&args.crate_name), None::<&[&str]>);
let cargo_metadata = get_cargo_metadata(Some(&args.crate_name), None::<&[&str]>).unwrap();
add_manifest_dependencies(&cargo_metadata, &args.crate_name);
create_osdk_manifest(&cargo_metadata);
if args.kernel {
@ -22,6 +23,14 @@ pub fn execute_new_command(args: &NewArgs) {
add_rust_toolchain(&cargo_metadata);
}
/// OSDK assumes that the toolchain used by the kernel should be same same as the toolchain
/// specified in the asterinas workspace.
macro_rules! aster_rust_toolchain {
() => {
include_str!("../../../../rust-toolchain.toml")
};
}
fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &str) {
let mainfest_path = get_manifest_path(cargo_metadata, crate_name);
@ -34,7 +43,15 @@ fn add_manifest_dependencies(cargo_metadata: &serde_json::Value, crate_name: &st
let aster_frame_dep = toml::Table::from_str(ASTER_FRAME_DEP).unwrap();
dependencies.as_table_mut().unwrap().extend(aster_frame_dep);
let ktest_dep = toml::Table::from_str(KTEST_DEP).unwrap();
dependencies.as_table_mut().unwrap().extend(ktest_dep);
// If we created a workspace by `osdk new`, we should exclude the `base` crate from the workspace.
if get_cargo_metadata::<&Path, &OsStr>(None, None).is_none() {
let exclude = toml::Table::from_str(r#"exclude = ["target/osdk/base"]"#).unwrap();
manifest.insert("workspace".to_string(), toml::Value::Table(exclude));
}
let content = toml::to_string(&manifest).unwrap();
fs::write(mainfest_path, content).unwrap();
}
@ -51,20 +68,37 @@ fn create_osdk_manifest(cargo_metadata: &serde_json::Value) {
}
// Create `OSDK.toml` for the workspace
fs::write(osdk_manifest_path, "").unwrap();
// FIXME: we need ovmf for grub-efi, the user may not have it.
// The apt OVMF repo installs to `/usr/share/OVMF`
fs::write(osdk_manifest_path, r#"
[boot]
ovmf = "/usr/share/OVMF"
[qemu]
machine = "q35"
args = [
"--no-reboot",
"-m 2G",
"-nographic",
"-serial chardev:mux",
"-monitor chardev:mux",
"-chardev stdio,id=mux,mux=on,signal=off",
"-display none",
"-device isa-debug-exit,iobase=0xf4,iosize=0x04",
]
"#).unwrap();
}
/// Write the default content of `src/kernel.rs`, with contents in provided template.
fn write_kernel_template(cargo_metadata: &serde_json::Value, crate_name: &str) {
let src_path = get_src_path(cargo_metadata, crate_name);
let contents = include_str!("template/kernel.template");
let contents = include_str!("kernel.template");
fs::write(src_path, contents).unwrap();
}
/// Write the default content of `src/lib.rs`, with contents in provided template.
fn write_library_template(cargo_metadata: &serde_json::Value, crate_name: &str) {
let src_path = get_src_path(cargo_metadata, crate_name);
let contents = include_str!("template/lib.template");
let contents = include_str!("lib.template");
fs::write(src_path, contents).unwrap();
}
@ -83,7 +117,7 @@ fn add_rust_toolchain(cargo_metadata: &serde_json::Value) {
return;
}
let contents = include_str!("template/rust-toolchain.toml.template");
let contents = aster_rust_toolchain!();
fs::write(rust_toolchain_path, contents).unwrap();
}
@ -134,7 +168,7 @@ fn get_package_metadata<'a>(
fn check_rust_toolchain(toolchain: &toml::Table) {
let expected = {
let contents = include_str!("template/rust-toolchain.toml.template");
let contents = aster_rust_toolchain!();
toml::Table::from_str(contents).unwrap()
};

27
osdk/src/commands/run.rs Normal file
View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: MPL-2.0
use crate::config_manager::{BuildConfig, RunConfig};
use crate::utils::{get_current_crate_info, get_target_directory};
use super::build::create_base_and_build;
use super::utils::DEFAULT_TARGET_RELPATH;
pub fn execute_run_command(config: &RunConfig) {
let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH);
let target_name = get_current_crate_info().name;
let default_bundle_directory = osdk_target_directory.join(target_name);
let required_build_config = BuildConfig {
manifest: config.manifest.clone(),
cargo_args: config.cargo_args.clone(),
};
// TODO: Check if the bundle is already built and compatible with the run configuration.
let bundle = create_base_and_build(
&default_bundle_directory,
&osdk_target_directory,
&required_build_config,
);
bundle.run(&config);
}

View File

@ -1,9 +0,0 @@
#![no_std]
#![no_main]
use aster_frame::prelude::*;
#[aster_main]
fn kernel_main() {
println!("Hello world from guest kernel!");
}

View File

@ -1,3 +0,0 @@
[toolchain]
channel = "nightly-2023-12-01"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]

View File

@ -1,16 +0,0 @@
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
"code-model": "kernel",
"cpu": "x86-64",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"disable-redzone": true,
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
}

70
osdk/src/commands/test.rs Normal file
View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: MPL-2.0
use std::fs;
use crate::base_crate::new_base_crate;
use crate::config_manager::{BuildConfig, RunConfig, TestConfig};
use crate::utils::{get_current_crate_info, get_target_directory};
use super::build::do_build;
use super::utils::DEFAULT_TARGET_RELPATH;
pub fn execute_test_command(config: &TestConfig) {
let current_crate = get_current_crate_info();
let osdk_target_directory = get_target_directory().join(DEFAULT_TARGET_RELPATH);
let target_crate_dir = osdk_target_directory.join("base");
new_base_crate(&target_crate_dir, &current_crate.name, &current_crate.path);
let main_rs_path = target_crate_dir.join("src").join("main.rs");
let ktest_test_whitelist = match &config.test_name {
Some(name) => format!(r#"Some(&["{}"])"#, name),
None => format!(r#"None"#),
};
let mut ktest_crate_whitelist = vec![current_crate.name];
if let Some(name) = &config.test_name {
ktest_crate_whitelist.push(name.clone());
}
let ktest_static_var = format!(
r#"
#[no_mangle]
pub static KTEST_TEST_WHITELIST: Option<&[&str]> = {};
#[no_mangle]
pub static KTEST_CRATE_WHITELIST: Option<&[&str]> = Some(&{:#?});
"#,
ktest_test_whitelist, ktest_crate_whitelist,
);
// Append the ktest static variable to the main.rs file
let mut main_rs_content = fs::read_to_string(&main_rs_path).unwrap();
main_rs_content.push_str(&ktest_static_var);
fs::write(&main_rs_path, main_rs_content).unwrap();
// Build the kernel with the given base crate
let target_name = get_current_crate_info().name;
let default_bundle_directory = osdk_target_directory.join(target_name);
let required_build_config = BuildConfig {
manifest: config.manifest.clone(),
cargo_args: config.cargo_args.clone(),
};
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&target_crate_dir).unwrap();
// Add `--cfg ktest` to RUSTFLAGS
std::env::set_var("RUSTFLAGS", "--cfg ktest");
let bundle = do_build(
&default_bundle_directory,
&osdk_target_directory,
&required_build_config,
);
std::env::remove_var("RUSTFLAGS");
std::env::set_current_dir(&original_dir).unwrap();
let required_run_config = RunConfig {
manifest: required_build_config.manifest.clone(),
cargo_args: required_build_config.cargo_args.clone(),
};
bundle.run(&required_run_config);
}

View File

@ -6,17 +6,21 @@ use std::{
process::Command,
};
use crate::utils::get_target_directory;
pub const COMMON_CARGO_ARGS: &[&str] = &[
"-Zbuild-std=core,alloc,compiler_builtins",
"-Zbuild-std-features=compiler-builtins-mem",
];
pub const DEFAULT_TARGET_RELPATH: &str = "osdk";
pub fn cargo() -> Command {
Command::new("cargo")
}
pub fn create_target_json(target_directory: impl AsRef<Path>) -> PathBuf {
let target_osdk_dir = PathBuf::from(target_directory.as_ref()).join("osdk");
pub fn create_target_json() -> PathBuf {
let target_osdk_dir = get_target_directory().join(DEFAULT_TARGET_RELPATH);
fs::create_dir_all(&target_osdk_dir).unwrap();
let target_json_path = target_osdk_dir.join("x86_64-custom.json");
@ -24,7 +28,7 @@ pub fn create_target_json(target_directory: impl AsRef<Path>) -> PathBuf {
return target_json_path;
}
let contents = include_str!("template/x86_64-custom.json.template");
let contents = include_str!("../base_crate/x86_64-custom.json.template");
fs::write(&target_json_path, contents).unwrap();
target_json_path