diff --git a/.github/workflows/osdk_test.yml b/.github/workflows/osdk_test.yml index 7c00439d1..81a0e6a90 100644 --- a/.github/workflows/osdk_test.yml +++ b/.github/workflows/osdk_test.yml @@ -32,8 +32,9 @@ jobs: id: unit_test run: cd osdk && cargo build && RUSTUP_HOME=/root/.rustup cargo test - # Test OSDK in the same environment as described in the OSDK User Guide in the Asterinas Book. - osdk_doc_env_test: + # Test OSDK in the same environment + # as described in the OSDK User Guide in the Asterinas Book. + osdk-doc-env-test: runs-on: ubuntu-latest timeout-minutes: 15 container: asterinas/osdk:0.4.0 @@ -44,6 +45,10 @@ jobs: - name: Unit test id: unit_test + # Github's actions/checkout@v4 will result in a new user (not root) + # and thus not using the Rust environment we set up in the container. + # So the RUSTUP_HOME needs to be set here. + # This only breaks when we invoke Cargo in the integration test of OSDK + # since the OSDK toolchain is not nightly. run: cd osdk && cargo build && RUSTUP_HOME=/root/.rustup cargo test - # TODO: test the demos in Asterinas Book diff --git a/Makefile b/Makefile index c67a02a70..588cdff4e 100644 --- a/Makefile +++ b/Makefile @@ -185,4 +185,5 @@ check: $(CARGO_OSDK) clean: @cargo clean @cd docs && mdbook clean - @make --no-print-directory -C regression clean \ No newline at end of file + @make --no-print-directory -C regression clean + @rm -f $(CARGO_OSDK) diff --git a/docs/src/osdk/guide/work-in-workspace.md b/docs/src/osdk/guide/work-in-workspace.md index 79bc942ab..5e89ec5bc 100644 --- a/docs/src/osdk/guide/work-in-workspace.md +++ b/docs/src/osdk/guide/work-in-workspace.md @@ -17,11 +17,7 @@ touch Cargo.toml Then, add the following content to `Cargo.toml`: -```toml -[workspace] -members = [] -resolver = "2" -``` +{{#include ../../../../osdk/tests/examples_in_book/work_in_workspace_templates/Cargo.toml}} ## Creating a kernel project and a library project @@ -60,31 +56,17 @@ Next, add the following function to `mymodule/src/lib.rs`. This function will calculate the available memory after booting: -```rust -pub fn available_memory() -> usize { - let regions = aster_frame::boot::memory_regions(); - regions.iter().map(|region| region.len()).sum() -} -``` +{{#include ../../../../osdk/tests/examples_in_book/work_in_workspace_templates/mymodule/src/lib.rs}} Then, add a dependency on `mymodule` to `myos/Cargo.toml`: -```toml -[dependencies] -mymodule = { path = "../mymodule" } -``` +{{#include ../../../../osdk/tests/examples_in_book/work_in_workspace_templates/myos/Cargo.toml}} In `myos/src/lib.rs`, -modify the main function as follows. -This function will call the function from `mymodule`: +modify the file content as follows. +This main function will call the function from `mymodule`: -```rust -#[aster_main] -fn kernel_main() { - let avail_mem_as_mb = mymodule::available_memory() / 1_000_000; - println!("The available memory is {} MB", avail_mem_as_mb); -} -``` +{{#include ../../../../osdk/tests/examples_in_book/work_in_workspace_templates/myos/src/lib.rs}} ## Building and Running the kernel diff --git a/osdk/Cargo.lock b/osdk/Cargo.lock index cefcc85e6..b1de10ea4 100644 --- a/osdk/Cargo.lock +++ b/osdk/Cargo.lock @@ -128,7 +128,7 @@ dependencies = [ [[package]] name = "cargo-osdk" -version = "0.4.0" +version = "0.4.1" dependencies = [ "assert_cmd", "clap", diff --git a/osdk/src/util.rs b/osdk/src/util.rs index 04a32e372..0a237b555 100644 --- a/osdk/src/util.rs +++ b/osdk/src/util.rs @@ -81,12 +81,12 @@ pub struct CrateInfo { pub path: String, } -/// Retrieve the default member in the workspace. -/// -/// If there is only one kernel crate, return that crate; -/// If there are multiple kernel crates or no kernel crates in the workspace, -/// this function will exit with an error. -/// +/// Retrieve the default member in the workspace. +/// +/// If there is only one kernel crate, return that crate; +/// If there are multiple kernel crates or no kernel crates in the workspace, +/// this function will exit with an error. +/// /// A crate is considered a kernel crate if it utilizes the `aster_main` macro. fn get_default_member(metadata: &serde_json::Value) -> &str { let default_members = metadata diff --git a/osdk/tests/bin.rs b/osdk/tests/bin.rs index 2fa7e512e..464932333 100644 --- a/osdk/tests/bin.rs +++ b/osdk/tests/bin.rs @@ -6,5 +6,6 @@ mod cli; mod commands; +mod examples_in_book; mod integration; mod util; diff --git a/osdk/tests/examples_in_book/create_os_projects.rs b/osdk/tests/examples_in_book/create_os_projects.rs new file mode 100644 index 000000000..3c31dab45 --- /dev/null +++ b/osdk/tests/examples_in_book/create_os_projects.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::{fs, path::PathBuf}; + +use crate::util::cargo_osdk; + +#[test] +fn create_a_kernel_project() { + let workdir = "/tmp"; + let kernel = "my_foo_os"; + + let kernel_path = PathBuf::from(workdir).join(kernel); + + if kernel_path.exists() { + fs::remove_dir_all(&kernel_path).unwrap(); + } + + cargo_osdk(&["new", "--kernel", kernel]) + .current_dir(workdir) + .unwrap(); + + assert!(kernel_path.is_dir()); + assert!(kernel_path.join("Cargo.toml").is_file()); + assert!(kernel_path.join("rust-toolchain.toml").is_file()); + + fs::remove_dir_all(&kernel_path).unwrap(); +} + +#[test] +fn create_a_library_project() { + let workdir = "/tmp"; + let module = "my_foo_module"; + + let module_path = PathBuf::from(workdir).join(module); + if module_path.exists() { + fs::remove_dir_all(&module_path).unwrap(); + } + + cargo_osdk(&["new", module]).current_dir(workdir).unwrap(); + + assert!(module_path.is_dir()); + assert!(module_path.join("Cargo.toml").is_file()); + assert!(module_path.join("rust-toolchain.toml").is_file()); + + fs::remove_dir_all(&module_path).unwrap(); +} diff --git a/osdk/tests/examples_in_book/mod.rs b/osdk/tests/examples_in_book/mod.rs new file mode 100644 index 000000000..2fa4e61ac --- /dev/null +++ b/osdk/tests/examples_in_book/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MPL-2.0 + +//! This module contains the demos in OSDK section in the Asterinas Book. + +mod create_os_projects; +mod test_and_run_projects; +mod work_in_workspace; diff --git a/osdk/tests/examples_in_book/test_and_run_projects.rs b/osdk/tests/examples_in_book/test_and_run_projects.rs new file mode 100644 index 000000000..19a0118e1 --- /dev/null +++ b/osdk/tests/examples_in_book/test_and_run_projects.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::{fs, path::PathBuf}; + +use crate::util::cargo_osdk; + +#[test] +fn create_and_run_kernel() { + let work_dir = "/tmp"; + let os_name = "myos"; + + let os_dir = PathBuf::from(work_dir).join(os_name); + + if os_dir.exists() { + fs::remove_dir_all(&os_dir).unwrap(); + } + + let mut command = cargo_osdk(&["new", "--kernel", os_name]); + command.current_dir(work_dir); + command.ok().unwrap(); + + let mut command = cargo_osdk(&["build"]); + command.current_dir(&os_dir); + command.ok().unwrap(); + + let mut command = cargo_osdk(&["run"]); + command.current_dir(&os_dir); + let output = command.output().unwrap(); + + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + assert!(stdout.contains("Hello world from guest kernel!")); + + fs::remove_dir_all(&os_dir).unwrap(); +} + +#[test] +fn create_and_test_library() { + let work_dir = "/tmp"; + let module_name = "mymodule"; + + let module_dir = PathBuf::from(work_dir).join(module_name); + + if module_dir.exists() { + fs::remove_dir_all(&module_dir).unwrap(); + } + + let mut command = cargo_osdk(&["new", module_name]); + command.current_dir(work_dir); + command.ok().unwrap(); + + let mut command = cargo_osdk(&["test"]); + command.current_dir(&module_dir); + command.output().unwrap(); + + fs::remove_dir_all(&module_dir).unwrap(); +} diff --git a/osdk/tests/examples_in_book/work_in_workspace.rs b/osdk/tests/examples_in_book/work_in_workspace.rs new file mode 100644 index 000000000..c3467b2d8 --- /dev/null +++ b/osdk/tests/examples_in_book/work_in_workspace.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MPL-2.0 + +use std::{ + env, + fs::{self, OpenOptions}, + io::Write, + path::PathBuf, +}; + +use crate::util::cargo_osdk; + +#[test] +fn work_in_workspace() { + let workdir = "/tmp"; + let workspace_name = "myworkspace"; + + // Create workspace and its manifest + let workspace_dir = PathBuf::from(workdir).join(workspace_name); + if workspace_dir.is_dir() { + fs::remove_dir_all(&workspace_dir).unwrap(); + } + + fs::create_dir_all(&workspace_dir).unwrap(); + env::set_current_dir(&workspace_dir).unwrap(); + + let workspace_toml = include_str!("work_in_workspace_templates/Cargo.toml"); + fs::write(workspace_dir.join("Cargo.toml"), workspace_toml).unwrap(); + + // Create a kernel project and a library project + let kernel = "myos"; + let module = "mymodule"; + cargo_osdk(&["new", "--kernel", kernel]).ok().unwrap(); + cargo_osdk(&["new", module]).ok().unwrap(); + + // Add a test function to mymodule/src/lib.rs + let module_src_path = workspace_dir.join(module).join("src").join("lib.rs"); + assert!(module_src_path.is_file()); + let mut module_src_file = OpenOptions::new() + .append(true) + .open(&module_src_path) + .unwrap(); + module_src_file + .write_all(include_bytes!("work_in_workspace_templates/mymodule/src/lib.rs")) + .unwrap(); + module_src_file.flush().unwrap(); + + // Add dependency to myos/Cargo.toml + let kernel_manifest_path = workspace_dir.join(kernel).join("Cargo.toml"); + assert!(kernel_manifest_path.is_file()); + let mut kernel_manifest_file = OpenOptions::new() + .append(true) + .open(&kernel_manifest_path) + .unwrap(); + kernel_manifest_file + .write_all(include_bytes!("work_in_workspace_templates/myos/Cargo.toml")) + .unwrap(); + kernel_manifest_file.flush().unwrap(); + + // Add the content to myos/src/lib.rs + let kernel_src_path = workspace_dir.join(kernel).join("src").join("lib.rs"); + assert!(kernel_src_path.is_file()); + fs::write(&kernel_src_path, include_str!("work_in_workspace_templates/myos/src/lib.rs")).unwrap(); + + // Run subcommand build & run + cargo_osdk(&["build"]).ok().unwrap(); + let output = cargo_osdk(&["run"]).output().unwrap(); + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + assert!(stdout.contains("The available memory is")); + + // Run subcommand test + cargo_osdk(&["test"]).output().unwrap(); + + // Remove the directory + fs::remove_dir_all(&workspace_dir).unwrap(); +} diff --git a/osdk/tests/examples_in_book/work_in_workspace_templates/Cargo.toml b/osdk/tests/examples_in_book/work_in_workspace_templates/Cargo.toml new file mode 100644 index 000000000..9bc2f769f --- /dev/null +++ b/osdk/tests/examples_in_book/work_in_workspace_templates/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = [] +resolver = "2" diff --git a/osdk/tests/examples_in_book/work_in_workspace_templates/mymodule/src/lib.rs b/osdk/tests/examples_in_book/work_in_workspace_templates/mymodule/src/lib.rs new file mode 100644 index 000000000..a31673dbe --- /dev/null +++ b/osdk/tests/examples_in_book/work_in_workspace_templates/mymodule/src/lib.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub fn available_memory() -> usize { + let regions = aster_frame::boot::memory_regions(); + regions.iter().map(|region| region.len()).sum() +} diff --git a/osdk/tests/examples_in_book/work_in_workspace_templates/myos/Cargo.toml b/osdk/tests/examples_in_book/work_in_workspace_templates/myos/Cargo.toml new file mode 100644 index 000000000..40eaabcf5 --- /dev/null +++ b/osdk/tests/examples_in_book/work_in_workspace_templates/myos/Cargo.toml @@ -0,0 +1,2 @@ +[dependencies] +mymodule = { path = "../mymodule" } diff --git a/osdk/tests/examples_in_book/work_in_workspace_templates/myos/src/lib.rs b/osdk/tests/examples_in_book/work_in_workspace_templates/myos/src/lib.rs new file mode 100644 index 000000000..f3dfef69b --- /dev/null +++ b/osdk/tests/examples_in_book/work_in_workspace_templates/myos/src/lib.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MPL-2.0 + +#![no_std] +#![forbid(unsafe_code)] + +use aster_frame::prelude::*; + +#[aster_main] +fn kernel_main() { + let avail_mem_as_mb = mymodule::available_memory() / 1_000_000; + println!("The available memory is {} MB", avail_mem_as_mb); +} diff --git a/osdk/tools/Dockerfile.ubuntu22.04 b/osdk/tools/Dockerfile.ubuntu22.04 index ba7306b5d..35777291e 100644 --- a/osdk/tools/Dockerfile.ubuntu22.04 +++ b/osdk/tools/Dockerfile.ubuntu22.04 @@ -3,6 +3,15 @@ # This image is for the OSDK GitHub CI. # The environment is consistent with the one # described in the OSDK User Guide section of the Asterinas Book. +# +# TODO: We should build the Asterinas image based on the OSDK image +# since Asterinas is managed by OSDK itself. +# However, currently, these two images have different contents. +# The main distinction is that +# QEMU, grub, and OVMF in the OSDK image are installed via apt, +# while these tools in the Asterinas image are built from source. +# Some boot methods in Asterinas only function properly +# when using the tools that are built from source. FROM ubuntu:22.04