From 92a004c914123722a4154f96ed3a64314ba6ea55 Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Sat, 24 Jan 2026 17:46:58 +0800 Subject: [PATCH 1/6] [feat] refactor according to rules and prepare to release --- .github/workflows/ci.yml | 106 ++++++++++------- .github/workflows/release-check.yml | 72 ++++++++++++ CHANGELOG.md | 38 ++++++ Cargo.toml | 30 +++-- README.md | 5 +- axvisor_api_proc/Cargo.toml | 11 +- axvisor_api_proc/src/lib.rs | 138 ++++++++++++++++++++-- src/arch.rs | 109 ++++++++++++++++- src/host.rs | 51 +++++++- src/memory.rs | 160 ++++++++++++++++++++++++- src/time.rs | 174 +++++++++++++++++++++++++--- src/vmm.rs | 168 +++++++++++++++++++++++++-- 12 files changed, 964 insertions(+), 98 deletions(-) create mode 100644 .github/workflows/release-check.yml create mode 100644 CHANGELOG.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d66dd5..37dcbf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,57 +1,83 @@ name: CI -on: [push, pull_request] +on: + push: + branches: [main, dev, master] + pull_request: + branches: [main, dev, master] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 jobs: - ci: + # Quick checks: format and clippy + quick-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + components: rustfmt, clippy + - name: Check rust version + run: rustc --version --verbose + - name: Check code format + run: cargo fmt --all -- --check + - name: Clippy (x86_64-unknown-linux-gnu) + run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default + + # Build and test for multiple targets + build-and-test: runs-on: ubuntu-latest + needs: quick-check strategy: fail-fast: false matrix: rust-toolchain: [nightly-2025-05-20, nightly] - targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] + targets: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + - riscv64gc-unknown-none-elf + - aarch64-unknown-none-softfloat steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ matrix.rust-toolchain }} + components: rust-src, clippy + targets: ${{ matrix.targets }} + - name: Check rust version + run: rustc --version --verbose + - name: Clippy + run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default + - name: Build + run: cargo build --target ${{ matrix.targets }} --all-features + - name: Unit test + if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} + run: cargo test --target ${{ matrix.targets }} -- --nocapture + # Documentation build and deploy doc: runs-on: ubuntu-latest - strategy: - fail-fast: false + needs: quick-check permissions: contents: write env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + - name: Build docs + run: | + cargo doc --no-deps --all-features + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + - name: Deploy to Github Pages + if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml new file mode 100644 index 0000000..8d406e2 --- /dev/null +++ b/.github/workflows/release-check.yml @@ -0,0 +1,72 @@ +name: Release Check + +on: + push: + tags: + - "v*" + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" + +jobs: + release-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + components: rust-src, clippy, rustfmt + targets: x86_64-unknown-linux-gnu,x86_64-unknown-none,riscv64gc-unknown-none-elf,aarch64-unknown-none-softfloat + + - name: Check rust version + run: rustc --version --verbose + + # Verify version consistency between git tag and Cargo.toml + - name: Verify version consistency + run: | + TAG_VERSION=${GITHUB_REF#refs/tags/v} + CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "Error: Git tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed: $TAG_VERSION" + + # Format check + - name: Check code format + run: cargo fmt --all -- --check + + # Clippy check + - name: Clippy + run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default + + # Build for all supported targets + - name: Build (x86_64-unknown-linux-gnu) + run: cargo build --target x86_64-unknown-linux-gnu --all-features + - name: Build (x86_64-unknown-none) + run: cargo build --target x86_64-unknown-none --all-features + - name: Build (riscv64gc-unknown-none-elf) + run: cargo build --target riscv64gc-unknown-none-elf --all-features + - name: Build (aarch64-unknown-none-softfloat) + run: cargo build --target aarch64-unknown-none-softfloat --all-features + + # Run tests + - name: Unit test + run: cargo test --all-features -- --nocapture + + # Doc test + - name: Doc test + run: cargo test --doc --all-features + + # Build documentation + - name: Build docs + run: cargo doc --no-deps --all-features + + # Dry run publish check + - name: Publish check (axvisor_api_proc) + run: cargo publish --dry-run -p axvisor_api_proc + - name: Publish check (axvisor_api) + run: cargo publish --dry-run -p axvisor_api diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c6e8c00 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.2.0] - 2026-01-24 + +### Added + +- Added `api_def` and `api_impl` procedural macros for defining and implementing APIs. +- Added `arch` module with architecture-specific APIs for AArch64 GIC operations. +- Added `host` module with host system APIs. +- Added `memory` module with memory allocation and address translation APIs. +- Added `time` module with time and timer APIs. +- Added `vmm` module with virtual machine management APIs. +- Added `PhysFrame` type alias for automatic frame deallocation. +- Added comprehensive documentation and examples in crate-level docs. +- Added docs.rs configuration for multi-target documentation. + +### Changed + +- Improved Cargo.toml with complete metadata fields. +- Enhanced CI configuration with documentation checks. + +## [0.1.0] - Initial Release + +### Added + +- Initial implementation of the axvisor_api crate. +- Basic API definition and implementation framework using `crate_interface`. + +[Unreleased]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.2.0...HEAD +[0.2.0]: https://github.com/arceos-hypervisor/axvisor_api/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/arceos-hypervisor/axvisor_api/releases/tag/v0.1.0 diff --git a/Cargo.toml b/Cargo.toml index b21e2f6..d7cd7d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] resolver = "2" -members = [ - "axvisor_api_proc", -] +members = ["axvisor_api_proc"] [workspace.package] authors = ["Su Mingxian "] @@ -12,9 +10,11 @@ repository = "https://github.com/arceos-hypervisor/axvisor_api" [package] name = "axvisor_api" -description = "Basic API for components of the Hypervisor on ArceOS" version = "0.2.0" -keywords = ["axvisor", "api", "embedded"] +description = "Basic API for components of the Hypervisor on ArceOS" +documentation = "https://docs.rs/axvisor_api" +readme = "README.md" +keywords = ["axvisor", "api", "embedded", "hypervisor"] categories = ["embedded", "no-std"] authors.workspace = true edition.workspace = true @@ -22,8 +22,24 @@ license.workspace = true repository.workspace = true [dependencies] -axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0"} +# === 核心组件 === +# 过程宏定义 +axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" } +# === 第三方库 === +# 地址空间抽象 +axaddrspace = "0.1.0" +# 接口定义工具 crate_interface = "0.2" +# 物理/虚拟地址类型 memory_addr = "0.4" -axaddrspace = "0.1.0" + +[package.metadata.docs.rs] +all-features = true +default-target = "x86_64-unknown-linux-gnu" +targets = [ + "x86_64-unknown-linux-gnu", + "x86_64-unknown-none", + "riscv64gc-unknown-none-elf", + "aarch64-unknown-none-softfloat", +] diff --git a/README.md b/README.md index 05303f1..a3494b4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # axvisor\_api (Experimental Next-Generation Axvisor API) + [![CI](https://github.com/arceos-hypervisor/axvisor_api/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/arceos-hypervisor/axvisor_api/actions/workflows/ci.yml) -[![License](https://img.shields.io/badge/License-MIT%20OR%20Apache--2.0-blue.svg)] +[![Crates.io](https://img.shields.io/crates/v/axvisor_api)](https://crates.io/crates/axvisor_api) +[![Docs.rs](https://docs.rs/axvisor_api/badge.svg)](https://docs.rs/axvisor_api) +[![License](https://img.shields.io/badge/License-GPL--3.0--or--later%20OR%20Apache--2.0%20OR%20MulanPSL--2.0-blue.svg)](LICENSE.Apache2) \> [中文README](README.zh-cn.md) < diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index f080e76..5ab1ff9 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -2,6 +2,8 @@ name = "axvisor_api_proc" version = "0.2.0" description = "Procedural macros for the `axvisor_api` crate" +documentation = "https://docs.rs/axvisor_api_proc" +readme = "README.md" keywords = ["axvisor", "api", "embedded", "macros"] categories = ["development-tools::procedural-macro-helpers"] authors.workspace = true @@ -9,12 +11,15 @@ edition.workspace = true license.workspace = true repository.workspace = true - [lib] proc-macro = true [dependencies] -syn = { version = "2.0", features = ["full"] } -quote = "1.0" +# 过程宏辅助工具 proc-macro2 = "1.0" +# crate 名称查找 proc-macro-crate = "3.1.0" +# 代码生成 +quote = "1.0" +# Rust 语法解析 +syn = { version = "2.0", features = ["full"] } diff --git a/axvisor_api_proc/src/lib.rs b/axvisor_api_proc/src/lib.rs index b8f1c90..3b1a875 100644 --- a/axvisor_api_proc/src/lib.rs +++ b/axvisor_api_proc/src/lib.rs @@ -1,3 +1,45 @@ +//! Procedural macros for the `axvisor_api` crate. +//! +//! This crate provides the procedural macros used to define and implement +//! AxVisor API interfaces. These macros are built on top of the +//! `crate_interface` crate and provide a convenient way to create +//! link-time-resolved API interfaces. +//! +//! # Macros +//! +//! - [`api_def`] - Define an API interface trait. +//! - [`api_impl`] - Implement an API interface. +//! +//! # Usage +//! +//! This crate is re-exported by `axvisor_api` and should not be used directly. +//! Instead, use the macros through `axvisor_api`: +//! +//! ```rust,ignore +//! use axvisor_api::{api_def, api_impl}; +//! +//! #[api_def] +//! pub trait MyApiIf { +//! fn my_function() -> u32; +//! } +//! +//! struct MyApiImpl; +//! +//! #[api_impl] +//! impl MyApiIf for MyApiImpl { +//! fn my_function() -> u32 { +//! 42 +//! } +//! } +//! ``` +//! +//! # How It Works +//! +//! The macros use `crate_interface` under the hood, which leverages Rust's +//! link-time symbol resolution to connect API definitions with their +//! implementations. This allows for a cleaner API without explicit generic +//! parameters. + use proc_macro::TokenStream as TokenStream1; use proc_macro_crate::{FoundCrate, crate_name}; use proc_macro2::{Span, TokenStream}; @@ -5,6 +47,11 @@ use quote::{quote, quote_spanned}; use syn::{Ident, spanned::Spanned}; /// Find the path to the `axvisor_api` crate. +/// +/// This function determines the correct path to use when referring to +/// `axvisor_api` from within the generated code, handling both the case +/// where we're inside the `axvisor_api` crate itself and when we're in +/// an external crate. fn axvisor_api_crate() -> TokenStream { match crate_name("axvisor_api") { Ok(FoundCrate::Itself) => quote! { crate }, @@ -16,29 +63,68 @@ fn axvisor_api_crate() -> TokenStream { } } -/// The namespace used for AxVisor APIs when calling `crate_interface` macros. +/// Get the namespace identifier used for AxVisor APIs. +/// +/// All AxVisor APIs share a common namespace to avoid conflicts with other +/// uses of `crate_interface`. fn axvisor_api_namespace() -> Ident { const AXVISOR_API_NS: &str = "AxVisorApi"; Ident::new(AXVISOR_API_NS, Span::call_site()) } +/// Macro to assert that an attribute has no arguments. macro_rules! assert_empty_attr { ($attr:expr) => { if !$attr.is_empty() { return (quote_spanned! { TokenStream::from($attr).span() => compile_error!("This attribute does not accept any arguments") - }).into(); + }) + .into(); } }; } /// Define an AxVisor API interface. /// -/// This macro is applied to a trait definition. It generates the necessary -/// boilerplate code to register the trait as an AxVisor API interface. No -/// arguments are accepted. +/// This attribute macro is applied to a trait definition to register it as +/// an AxVisor API interface. It generates caller functions for each method +/// in the trait, allowing the API to be called as regular functions. +/// +/// # Usage +/// +/// ```rust,ignore +/// use axvisor_api::api_def; +/// +/// #[api_def] +/// pub trait MyApiIf { +/// /// Get a value. +/// fn get_value() -> u32; +/// +/// /// Set a value. +/// fn set_value(value: u32); +/// } +/// +/// // After the macro expansion, you can call: +/// // my_module::get_value() +/// // my_module::set_value(42) +/// ``` +/// +/// # Generated Code +/// +/// The macro generates: +/// 1. The original trait definition with `crate_interface::def_interface` +/// attribute. +/// 2. Free-standing caller functions for each trait method at the same +/// module level. +/// +/// # Attributes +/// +/// This macro does not accept any arguments. /// -/// This macro uses `crate_interface::def_interface` internally. +/// # Implementation +/// +/// This macro uses `crate_interface::def_interface` internally with the +/// `gen_caller` option to generate the caller functions. #[proc_macro_attribute] pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { assert_empty_attr!(attr); @@ -56,10 +142,42 @@ pub fn api_def(attr: TokenStream1, input: TokenStream1) -> TokenStream1 { /// Implement an AxVisor API interface. /// -/// This macro is applied to an `impl` block that implements a trait previously -/// defined with `api_def`. It generates the necessary boilerplate code to -/// register the implementation as an AxVisor API implementation. No arguments -/// are accepted. +/// This attribute macro is applied to an `impl` block that implements a +/// trait previously defined with [`api_def`]. It registers the implementation +/// so that calls to the API functions are resolved to this implementation +/// at link time. +/// +/// # Usage +/// +/// ```rust,ignore +/// use axvisor_api::{api_def, api_impl}; +/// +/// #[api_def] +/// pub trait MyApiIf { +/// fn get_value() -> u32; +/// } +/// +/// struct MyApiImpl; +/// +/// #[api_impl] +/// impl MyApiIf for MyApiImpl { +/// fn get_value() -> u32 { +/// 42 +/// } +/// } +/// ``` +/// +/// # Requirements +/// +/// - The implemented trait must have been defined with [`api_def`]. +/// - The implementing type should be an empty struct (marker type). +/// - Only one implementation per API trait is allowed in the final binary. +/// +/// # Attributes +/// +/// This macro does not accept any arguments. +/// +/// # Implementation /// /// This macro uses `crate_interface::impl_interface` internally. #[proc_macro_attribute] diff --git a/src/arch.rs b/src/arch.rs index c11b030..4332142 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -1,25 +1,122 @@ -//! Architecture-specific APIs. +//! Architecture-specific APIs for the AxVisor hypervisor. +//! +//! This module provides APIs that are specific to certain CPU architectures. +//! Currently, it mainly contains AArch64 GIC (Generic Interrupt Controller) +//! related operations. +//! +//! # Supported Architectures +//! +//! - **AArch64**: GIC distributor and redistributor operations for virtual +//! interrupt injection and GIC initialization. +//! +//! # Usage +//! +//! The API functions in this module are conditionally compiled based on the +//! target architecture. On non-AArch64 platforms, this module is essentially +//! empty. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct ArchIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::arch::ArchIf for ArchIfImpl { +//! // Implement the required functions... +//! } +//! ``` use super::{memory::PhysAddr, vmm::InterruptVector}; /// The API trait for architecture-specific functionalities. +/// +/// This trait defines the interface for architecture-specific operations +/// required by the hypervisor. Implementations should be provided by the +/// host system or HAL layer. #[crate::api_def] pub trait ArchIf { - /// Inject a virtual interrupt to the current virtual CPU. + /// Inject a virtual interrupt to the current virtual CPU using hardware + /// virtualization support. + /// + /// This function uses the GIC virtualization interface to directly inject + /// an interrupt into the guest without causing a VM exit. + /// + /// # Arguments + /// + /// * `vector` - The interrupt vector number to inject. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms with GICv2/v3 + /// virtualization extensions. #[cfg(target_arch = "aarch64")] fn hardware_inject_virtual_interrupt(vector: InterruptVector); - /// Get the TYPER register of the GIC distributor. Used in virtual GIC initialization. + /// Read the TYPER (Type Register) of the GIC distributor. + /// + /// The TYPER register provides information about the GIC implementation, + /// including the maximum number of SPIs supported and the number of + /// implemented CPU interfaces. + /// + /// # Returns + /// + /// The 32-bit value of the GICD_TYPER register. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn read_vgicd_typer() -> u32; - /// Get the IIDR register of the GIC distributor. Used in virtual GIC initialization. + + /// Read the IIDR (Implementer Identification Register) of the GIC + /// distributor. + /// + /// The IIDR register provides identification information about the GIC + /// implementation, including the implementer, revision, and variant. + /// + /// # Returns + /// + /// The 32-bit value of the GICD_IIDR register. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn read_vgicd_iidr() -> u32; - /// Get the base address of the GIC distributor in the host system. + /// Get the base physical address of the GIC distributor in the host system. + /// + /// The GIC distributor is responsible for interrupt prioritization and + /// distribution to CPU interfaces. + /// + /// # Returns + /// + /// The physical address of the GICD (GIC Distributor) registers. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms. #[cfg(target_arch = "aarch64")] fn get_host_gicd_base() -> PhysAddr; - /// Get the base address of the GIC redistributor in the host system. + + /// Get the base physical address of the GIC redistributor in the host + /// system. + /// + /// The GIC redistributor (GICv3+) handles per-PE (Processing Element) + /// interrupt configuration and provides the LPI (Locality-specific + /// Peripheral Interrupt) configuration interface. + /// + /// # Returns + /// + /// The physical address of the GICR (GIC Redistributor) registers. + /// + /// # Platform Support + /// + /// This function is only available on AArch64 platforms with GICv3 or + /// later. #[cfg(target_arch = "aarch64")] fn get_host_gicr_base() -> PhysAddr; } diff --git a/src/host.rs b/src/host.rs index 5d27eab..cee440c 100644 --- a/src/host.rs +++ b/src/host.rs @@ -1,8 +1,55 @@ -//! Host system related APIs. +//! Host system related APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for querying information about the host system +//! on which the hypervisor is running. +//! +//! # Overview +//! +//! The host system APIs provide essential information about the underlying +//! hardware that the hypervisor needs to manage virtual machines effectively. +//! +//! # Available APIs +//! +//! - [`get_host_cpu_num`] - Get the total number of CPUs in the host system. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct HostIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::host::HostIf for HostIfImpl { +//! fn get_host_cpu_num() -> usize { +//! // Return the number of CPUs from your platform +//! 4 +//! } +//! } +//! ``` /// The API trait for host system functionalities. +/// +/// This trait defines the interface for querying host system information. +/// Implementations should be provided by the host system or HAL layer. #[crate::api_def] pub trait HostIf { - /// Get the total number of cpus in the host system. + /// Get the total number of CPUs (logical processors) in the host system. + /// + /// This function returns the number of CPUs available to the hypervisor, + /// which is typically the same as the number of physical or logical + /// processors in the system. + /// + /// # Returns + /// + /// The number of CPUs in the host system. + /// + /// # Example + /// + /// ```rust,ignore + /// let cpu_count = axvisor_api::host::get_host_cpu_num(); + /// println!("Host has {} CPUs", cpu_count); + /// ``` fn get_host_cpu_num() -> usize; } diff --git a/src/memory.rs b/src/memory.rs index cc19682..9d74bf3 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,26 +1,160 @@ -//! Memory allocation and address translation APIs. +//! Memory allocation and address translation APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for physical memory management, including frame +//! allocation/deallocation and physical-virtual address translation. +//! +//! # Overview +//! +//! The memory APIs are fundamental to the hypervisor's operation, enabling: +//! - Physical frame allocation for guest memory +//! - Contiguous frame allocation for DMA and other hardware requirements +//! - Address translation between physical and virtual addresses +//! +//! # Re-exports +//! +//! This module re-exports [`PhysAddr`] and [`VirtAddr`] from the `memory_addr` +//! crate for convenience. +//! +//! # Types +//! +//! - [`PhysFrame`] - A physical frame that is automatically deallocated when +//! dropped. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct MemoryIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::memory::MemoryIf for MemoryIfImpl { +//! fn alloc_frame() -> Option { +//! // Allocate a physical frame from your allocator +//! } +//! // ... implement other functions +//! } +//! ``` pub use memory_addr::{PhysAddr, VirtAddr}; /// The API trait for memory allocation and address translation functionalities. +/// +/// This trait defines the core memory management interface required by the +/// hypervisor. Implementations should be provided by the host system or HAL +/// layer. #[crate::api_def] pub trait MemoryIf { - /// Allocate a frame. + /// Allocate a single physical frame (4KB page). + /// + /// # Returns + /// + /// - `Some(PhysAddr)` - The physical address of the allocated frame. + /// - `None` - If allocation fails (e.g., out of memory). + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::memory::{alloc_frame, dealloc_frame}; + /// + /// if let Some(frame) = alloc_frame() { + /// // Use the frame... + /// dealloc_frame(frame); + /// } + /// ``` fn alloc_frame() -> Option; - /// Allocate a number of contiguous frames, with a specified alignment. + + /// Allocate a number of contiguous physical frames with a specified + /// alignment. + /// + /// This function is useful for allocating memory for DMA buffers or other + /// hardware that requires contiguous physical memory. + /// + /// # Arguments + /// + /// * `num_frames` - The number of contiguous frames to allocate. + /// * `frame_align_pow2` - The alignment requirement as a power of 2 + /// (e.g., 0 for 4KB alignment, 1 for 8KB alignment). + /// + /// # Returns + /// + /// - `Some(PhysAddr)` - The physical address of the first allocated frame. + /// - `None` - If allocation fails. fn alloc_contiguous_frames(num_frames: usize, frame_align_pow2: usize) -> Option; - /// Deallocate a frame allocated previously by [`alloc_frame`]. + + /// Deallocate a frame previously allocated by [`alloc_frame`]. + /// + /// # Arguments + /// + /// * `addr` - The physical address of the frame to deallocate. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - The address was previously returned by [`alloc_frame`]. + /// - The frame has not been deallocated yet. + /// - No references to the frame's memory exist after deallocation. fn dealloc_frame(addr: PhysAddr); - /// Deallocate a number of contiguous frames allocated previously by + + /// Deallocate contiguous frames previously allocated by /// [`alloc_contiguous_frames`]. + /// + /// # Arguments + /// + /// * `first_addr` - The physical address of the first frame. + /// * `num_frames` - The number of frames to deallocate. + /// + /// # Safety + /// + /// The caller must ensure that: + /// - The address and count match a previous [`alloc_contiguous_frames`] + /// call. + /// - The frames have not been deallocated yet. + /// - No references to the frames' memory exist after deallocation. fn dealloc_contiguous_frames(first_addr: PhysAddr, num_frames: usize); + /// Convert a physical address to a virtual address. + /// + /// This function performs the physical-to-virtual address translation + /// based on the host's memory mapping. + /// + /// # Arguments + /// + /// * `addr` - The physical address to convert. + /// + /// # Returns + /// + /// The corresponding virtual address. + /// + /// # Panics + /// + /// May panic if the physical address is not mapped. fn phys_to_virt(addr: PhysAddr) -> VirtAddr; + /// Convert a virtual address to a physical address. + /// + /// This function performs the virtual-to-physical address translation + /// based on the host's memory mapping. + /// + /// # Arguments + /// + /// * `addr` - The virtual address to convert. + /// + /// # Returns + /// + /// The corresponding physical address. + /// + /// # Panics + /// + /// May panic if the virtual address is not mapped. fn virt_to_phys(addr: VirtAddr) -> PhysAddr; } /// [`AxMmHal`](axaddrspace::AxMmHal) implementation by axvisor_api. +/// +/// This struct provides an implementation of the `AxMmHal` trait from the +/// `axaddrspace` crate, delegating to the axvisor_api memory functions. #[doc(hidden)] pub struct AxMmHalApiImpl; @@ -43,4 +177,20 @@ impl axaddrspace::AxMmHal for AxMmHalApiImpl { } /// A physical frame which will be automatically deallocated when dropped. +/// +/// This type alias provides a convenient RAII wrapper around physical frame +/// allocation. When a `PhysFrame` is dropped, it automatically deallocates +/// the underlying physical memory. +/// +/// # Example +/// +/// ```rust,ignore +/// use axvisor_api::memory::PhysFrame; +/// +/// fn allocate_guest_memory() -> Option { +/// PhysFrame::alloc() +/// } +/// +/// // The frame will be automatically deallocated when it goes out of scope +/// ``` pub type PhysFrame = axaddrspace::PhysFrame; diff --git a/src/time.rs b/src/time.rs index 1e8129f..65a40ee 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,53 +1,201 @@ -//! Time and timer APIs. +//! Time and timer APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for time measurement and timer management, +//! which are essential for implementing virtual timers and time-related +//! virtualization features. +//! +//! # Overview +//! +//! The time APIs provide: +//! - Current time and tick count queries +//! - Conversion between ticks, nanoseconds, and duration +//! - Timer registration and cancellation +//! +//! # Types +//! +//! - [`TimeValue`] - A time value represented as [`Duration`]. +//! - [`Nanos`] - Nanoseconds count (u64). +//! - [`Ticks`] - Tick count (u64). +//! - [`CancelToken`] - Token used to cancel a registered timer. +//! +//! # Helper Functions +//! +//! In addition to the core API trait, this module provides helper functions: +//! - [`current_time_nanos`] - Get the current time in nanoseconds. +//! - [`current_time`] - Get the current time as a [`TimeValue`]. +//! - [`ticks_to_time`] - Convert ticks to [`TimeValue`]. +//! - [`time_to_ticks`] - Convert [`TimeValue`] to ticks. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct TimeIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::time::TimeIf for TimeIfImpl { +//! fn current_ticks() -> Ticks { +//! // Read the hardware timer counter +//! } +//! // ... implement other functions +//! } +//! ``` extern crate alloc; use alloc::boxed::Box; use core::time::Duration; -/// Time value. +/// Time value type. +/// +/// Represents a point in time or a duration as a [`Duration`]. pub type TimeValue = Duration; -/// Nanoseconds count. + +/// Nanoseconds count type. +/// +/// Used for high-precision time measurements in nanoseconds. pub type Nanos = u64; -/// Tick count. + +/// Tick count type. +/// +/// Represents the raw hardware timer counter value. pub type Ticks = u64; -/// Cancel token, used to cancel a scheduled timer event. + +/// Cancel token type for timer cancellation. +/// +/// This token is returned when registering a timer and can be used to cancel +/// the timer before it fires. pub type CancelToken = usize; /// The API trait for time and timer functionalities. +/// +/// This trait defines the core time management interface required by the +/// hypervisor. Implementations should be provided by the host system or HAL +/// layer. #[crate::api_def] pub trait TimeIf { - /// Get the current tick count. + /// Get the current tick count from the hardware timer. + /// + /// The tick count is a monotonically increasing counter that can be + /// converted to time using [`ticks_to_nanos`]. + /// + /// # Returns + /// + /// The current hardware timer counter value. fn current_ticks() -> Ticks; - /// Convert ticks to nanoseconds. + + /// Convert a tick count to nanoseconds. + /// + /// # Arguments + /// + /// * `ticks` - The tick count to convert. + /// + /// # Returns + /// + /// The equivalent time in nanoseconds. fn ticks_to_nanos(ticks: Ticks) -> Nanos; - /// Convert nanoseconds to ticks. + + /// Convert nanoseconds to a tick count. + /// + /// # Arguments + /// + /// * `nanos` - The nanoseconds to convert. + /// + /// # Returns + /// + /// The equivalent tick count. fn nanos_to_ticks(nanos: Nanos) -> Ticks; - /// Register a timer. + + /// Register a timer that will fire at the specified deadline. + /// + /// When the deadline is reached, the callback function will be called + /// with the actual time at which it was invoked. + /// + /// # Arguments + /// + /// * `deadline` - The time at which the timer should fire. + /// * `callback` - The function to call when the timer fires. It receives + /// the actual time as an argument. + /// + /// # Returns + /// + /// A [`CancelToken`] that can be used to cancel the timer with + /// [`cancel_timer`]. + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::time::{register_timer, current_time, TimeValue}; + /// use core::time::Duration; + /// + /// let deadline = current_time() + Duration::from_millis(100); + /// let token = register_timer(deadline, Box::new(|actual_time| { + /// println!("Timer fired at {:?}", actual_time); + /// })); + /// ``` fn register_timer( deadline: TimeValue, callback: Box, ) -> CancelToken; - /// Cancel a timer. + + /// Cancel a previously registered timer. + /// + /// If the timer has already fired, this function has no effect. + /// + /// # Arguments + /// + /// * `token` - The cancel token returned by [`register_timer`]. fn cancel_timer(token: CancelToken); } /// Get the current time in nanoseconds. +/// +/// This is a convenience function that combines [`current_ticks`] and +/// [`ticks_to_nanos`]. +/// +/// # Returns +/// +/// The current time in nanoseconds since an unspecified epoch. pub fn current_time_nanos() -> Nanos { ticks_to_nanos(current_ticks()) } -/// Get the current time. +/// Get the current time as a [`TimeValue`]. +/// +/// This is a convenience function that returns the current time as a +/// [`Duration`]. +/// +/// # Returns +/// +/// The current time as a [`TimeValue`] (Duration). pub fn current_time() -> TimeValue { Duration::from_nanos(current_time_nanos()) } -/// Convert ticks to time. +/// Convert ticks to a [`TimeValue`]. +/// +/// # Arguments +/// +/// * `ticks` - The tick count to convert. +/// +/// # Returns +/// +/// The equivalent time as a [`TimeValue`] (Duration). pub fn ticks_to_time(ticks: Ticks) -> TimeValue { Duration::from_nanos(ticks_to_nanos(ticks)) } -/// Convert time to ticks. +/// Convert a [`TimeValue`] to ticks. +/// +/// # Arguments +/// +/// * `time` - The time value to convert. +/// +/// # Returns +/// +/// The equivalent tick count. pub fn time_to_ticks(time: TimeValue) -> Ticks { nanos_to_ticks(time.as_nanos() as Nanos) } diff --git a/src/vmm.rs b/src/vmm.rs index 5d3ee25..621b063 100644 --- a/src/vmm.rs +++ b/src/vmm.rs @@ -1,37 +1,183 @@ -//! Virtual machine management APIs. +//! Virtual machine management APIs for the AxVisor hypervisor. +//! +//! This module provides APIs for managing virtual machines (VMs) and virtual +//! CPUs (vCPUs), including querying VM/vCPU information and interrupt +//! injection. +//! +//! # Overview +//! +//! The VMM (Virtual Machine Monitor) APIs enable: +//! - Querying the current VM and vCPU context +//! - Getting information about VMs and their vCPUs +//! - Injecting interrupts into virtual CPUs +//! - Timer expiration notifications +//! +//! # Types +//! +//! - [`VMId`] - Virtual machine identifier. +//! - [`VCpuId`] - Virtual CPU identifier. +//! - [`InterruptVector`] - Interrupt vector number. +//! +//! # Helper Functions +//! +//! In addition to the core API trait, this module provides helper functions: +//! - [`current_vm_vcpu_num`] - Get the vCPU count of the current VM. +//! - [`current_vm_active_vcpus`] - Get the active vCPU mask of the current VM. +//! +//! # Implementation +//! +//! To implement these APIs, use the [`api_impl`](crate::api_impl) attribute +//! macro on an impl block: +//! +//! ```rust,ignore +//! struct VmmIfImpl; +//! +//! #[axvisor_api::api_impl] +//! impl axvisor_api::vmm::VmmIf for VmmIfImpl { +//! fn current_vm_id() -> VMId { +//! // Return the current VM's ID +//! } +//! // ... implement other functions +//! } +//! ``` -/// Virtual machine ID. +/// Virtual machine identifier type. +/// +/// Each virtual machine is assigned a unique identifier that can be used +/// to reference it in API calls. pub type VMId = usize; -/// Virtual CPU ID. + +/// Virtual CPU identifier type. +/// +/// Each vCPU within a VM is assigned a unique identifier (0-indexed). pub type VCpuId = usize; -/// Interrupt vector. + +/// Interrupt vector type. +/// +/// Represents the interrupt vector number to be injected into a guest. pub type InterruptVector = u8; /// The API trait for virtual machine management functionalities. +/// +/// This trait defines the core VM management interface required by the +/// hypervisor components. Implementations should be provided by the VMM +/// layer. #[crate::api_def] pub trait VmmIf { - /// Get the ID of the current virtual machine. + /// Get the identifier of the current virtual machine. + /// + /// This function returns the VM ID of the VM that the calling context + /// belongs to. + /// + /// # Returns + /// + /// The current VM's identifier. fn current_vm_id() -> VMId; - /// Get the ID of the current virtual CPU. + + /// Get the identifier of the current virtual CPU. + /// + /// This function returns the vCPU ID within the current VM context. + /// + /// # Returns + /// + /// The current vCPU's identifier (0-indexed within the VM). fn current_vcpu_id() -> VCpuId; + /// Get the number of virtual CPUs in a virtual machine. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine to query. + /// + /// # Returns + /// + /// - `Some(count)` - The number of vCPUs in the specified VM. + /// - `None` - If the VM ID is invalid. fn vcpu_num(vm_id: VMId) -> Option; - /// Get the mask of active virtual CPUs in a virtual machine. + + /// Get the bitmask of active virtual CPUs in a virtual machine. + /// + /// Each bit in the returned value represents a vCPU, where bit N is set + /// if vCPU N is active (online and running). + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine to query. + /// + /// # Returns + /// + /// - `Some(mask)` - The active vCPU bitmask for the specified VM. + /// - `None` - If the VM ID is invalid. fn active_vcpus(vm_id: VMId) -> Option; - /// Inject an interrupt to a virtual CPU. + + /// Inject an interrupt into a specific virtual CPU. + /// + /// This function queues an interrupt to be delivered to the specified + /// vCPU when it is next scheduled. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the target virtual machine. + /// * `vcpu_id` - The identifier of the target virtual CPU. + /// * `vector` - The interrupt vector to inject. + /// + /// # Example + /// + /// ```rust,ignore + /// use axvisor_api::vmm::{inject_interrupt, current_vm_id}; + /// + /// // Inject timer interrupt (vector 0x20) to vCPU 0 of the current VM + /// inject_interrupt(current_vm_id(), 0, 0x20); + /// ``` fn inject_interrupt(vm_id: VMId, vcpu_id: VCpuId, vector: InterruptVector); - /// Notify that a virtual CPU timer has expired. + + /// Notify that a virtual CPU's timer has expired. + /// + /// This function is called when a vCPU's virtual timer expires and needs + /// to be handled. + /// + /// # Arguments + /// + /// * `vm_id` - The identifier of the virtual machine. + /// * `vcpu_id` - The identifier of the virtual CPU whose timer expired. + /// + /// # Note /// - /// TODO: determine whether we can skip this function. + /// This API may be revised in future versions as the timer virtualization + /// design evolves. fn notify_vcpu_timer_expired(vm_id: VMId, vcpu_id: VCpuId); } /// Get the number of virtual CPUs in the current virtual machine. +/// +/// This is a convenience function that combines [`current_vm_id`] and +/// [`vcpu_num`]. +/// +/// # Returns +/// +/// The number of vCPUs in the current VM. +/// +/// # Panics +/// +/// Panics if called outside of a valid VM context (when [`current_vm_id`] +/// returns an invalid ID). pub fn current_vm_vcpu_num() -> usize { vcpu_num(current_vm_id()).unwrap() } -/// Get the mask of active virtual CPUs in the current virtual machine. +/// Get the bitmask of active virtual CPUs in the current virtual machine. +/// +/// This is a convenience function that combines [`current_vm_id`] and +/// [`active_vcpus`]. +/// +/// # Returns +/// +/// The active vCPU bitmask for the current VM. +/// +/// # Panics +/// +/// Panics if called outside of a valid VM context (when [`current_vm_id`] +/// returns an invalid ID). pub fn current_vm_active_vcpus() -> usize { active_vcpus(current_vm_id()).unwrap() } From 1753883f79ed3cb82f59a0b51945f4ce6b75b0a7 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 28 Jan 2026 17:17:34 +0800 Subject: [PATCH 2/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d7cd7d4..4d72443 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" } # === 第三方库 === # 地址空间抽象 axaddrspace = "0.1.0" -# 接口定义工具 +# Interface definition tools crate_interface = "0.2" # 物理/虚拟地址类型 memory_addr = "0.4" From 91346b827b5e109464df2118cd3792f3527f4ca2 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 28 Jan 2026 17:17:45 +0800 Subject: [PATCH 3/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d72443..a7ae5fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,16 +22,16 @@ license.workspace = true repository.workspace = true [dependencies] -# === 核心组件 === -# 过程宏定义 +# === Core Components === +# Procedural macro definitions axvisor_api_proc = { path = "axvisor_api_proc", version = "0.2.0" } -# === 第三方库 === -# 地址空间抽象 +# === Third-party Libraries === +# Address space abstraction axaddrspace = "0.1.0" # Interface definition tools crate_interface = "0.2" -# 物理/虚拟地址类型 +# Physical/virtual address types memory_addr = "0.4" [package.metadata.docs.rs] From 73ee99877e37f40dd1f601dbd2056d0b4a463fbf Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 28 Jan 2026 17:18:07 +0800 Subject: [PATCH 4/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- axvisor_api_proc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index 5ab1ff9..670dab4 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] # 过程宏辅助工具 proc-macro2 = "1.0" -# crate 名称查找 +# Crate name lookup proc-macro-crate = "3.1.0" # 代码生成 quote = "1.0" From 27563e7ff079f7a54255e2fb0d690d74c0dcffe1 Mon Sep 17 00:00:00 2001 From: Su Mingxian Date: Wed, 28 Jan 2026 17:18:15 +0800 Subject: [PATCH 5/6] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- axvisor_api_proc/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index 670dab4..2d05c2b 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -15,11 +15,11 @@ repository.workspace = true proc-macro = true [dependencies] -# 过程宏辅助工具 +# Procedural macro helper utilities proc-macro2 = "1.0" -# Crate name lookup +# Crate name resolution proc-macro-crate = "3.1.0" -# 代码生成 +# Code generation quote = "1.0" -# Rust 语法解析 +# Rust syntax parsing syn = { version = "2.0", features = ["full"] } From 65ae95fa61a727fd731ac51c8f2503a0dddb19a8 Mon Sep 17 00:00:00 2001 From: hky1999 <976929993@qq.com> Date: Wed, 28 Jan 2026 22:18:37 +0800 Subject: [PATCH 6/6] [feat] modify github workflows --- .github/workflows/check.yml | 45 +++++++ .github/workflows/ci.yml | 83 ------------- .github/workflows/deploy.yml | 68 +++++++++++ .github/workflows/release-check.yml | 72 ------------ .github/workflows/release.yml | 174 ++++++++++++++++++++++++++++ .github/workflows/test.yml | 23 ++++ axvisor_api_proc/Cargo.toml | 2 +- 7 files changed, 311 insertions(+), 156 deletions(-) create mode 100644 .github/workflows/check.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/release-check.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..a6ee3b0 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,45 @@ +name: Quality Checks + +on: + workflow_call: + +jobs: + check: + name: Quality Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-none + - riscv64gc-unknown-none-elf + - aarch64-unknown-none-softfloat + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + components: rust-src, clippy, rustfmt + targets: ${{ matrix.target }} + + - name: Check rust version + run: rustc --version --verbose + + - name: Check code format + run: cargo fmt --all -- --check + + - name: Build + run: cargo build --target ${{ matrix.target }} --all-features + + - name: Run clippy + run: cargo clippy --target ${{ matrix.target }} --all-features -- -D warnings -A clippy::new_without_default + + - name: Build documentation + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 37dcbf7..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: CI - -on: - push: - branches: [main, dev, master] - pull_request: - branches: [main, dev, master] - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - -jobs: - # Quick checks: format and clippy - quick-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - components: rustfmt, clippy - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy (x86_64-unknown-linux-gnu) - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default - - # Build and test for multiple targets - build-and-test: - runs-on: ubuntu-latest - needs: quick-check - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly-2025-05-20, nightly] - targets: - - x86_64-unknown-linux-gnu - - x86_64-unknown-none - - riscv64gc-unknown-none-elf - - aarch64-unknown-none-softfloat - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture - - # Documentation build and deploy - doc: - runs-on: ubuntu-latest - needs: quick-check - permissions: - contents: write - env: - RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - - name: Build docs - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..d438641 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,68 @@ +name: Deploy + +on: + push: + branches: + - '**' + tags-ignore: + - 'v*' + - 'v*-pre.*' + pull_request: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + quality-check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + + build-doc: + name: Build documentation + runs-on: ubuntu-latest + needs: quality-check + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + - name: Build docs + env: + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + run: | + cargo doc --no-deps --all-features + printf '' > target/doc/index.html + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + + deploy-doc: + name: Deploy to GitHub Pages + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch) + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release-check.yml b/.github/workflows/release-check.yml deleted file mode 100644 index 8d406e2..0000000 --- a/.github/workflows/release-check.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Release Check - -on: - push: - tags: - - "v*" - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - RUSTDOCFLAGS: "-D rustdoc::broken_intra_doc_links -D missing-docs" - -jobs: - release-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: nightly-2025-05-20 - components: rust-src, clippy, rustfmt - targets: x86_64-unknown-linux-gnu,x86_64-unknown-none,riscv64gc-unknown-none-elf,aarch64-unknown-none-softfloat - - - name: Check rust version - run: rustc --version --verbose - - # Verify version consistency between git tag and Cargo.toml - - name: Verify version consistency - run: | - TAG_VERSION=${GITHUB_REF#refs/tags/v} - CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') - if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then - echo "Error: Git tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)" - exit 1 - fi - echo "Version check passed: $TAG_VERSION" - - # Format check - - name: Check code format - run: cargo fmt --all -- --check - - # Clippy check - - name: Clippy - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::new_without_default - - # Build for all supported targets - - name: Build (x86_64-unknown-linux-gnu) - run: cargo build --target x86_64-unknown-linux-gnu --all-features - - name: Build (x86_64-unknown-none) - run: cargo build --target x86_64-unknown-none --all-features - - name: Build (riscv64gc-unknown-none-elf) - run: cargo build --target riscv64gc-unknown-none-elf --all-features - - name: Build (aarch64-unknown-none-softfloat) - run: cargo build --target aarch64-unknown-none-softfloat --all-features - - # Run tests - - name: Unit test - run: cargo test --all-features -- --nocapture - - # Doc test - - name: Doc test - run: cargo test --doc --all-features - - # Build documentation - - name: Build docs - run: cargo doc --no-deps --all-features - - # Dry run publish check - - name: Publish check (axvisor_api_proc) - run: cargo publish --dry-run -p axvisor_api_proc - - name: Publish check (axvisor_api) - run: cargo publish --dry-run -p axvisor_api diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c92d84f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,174 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + - 'v*.*.*-pre.*' + +permissions: + contents: write + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + +jobs: + check: + uses: ./.github/workflows/check.yml + + test: + uses: ./.github/workflows/test.yml + needs: check + + create-release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml using cargo metadata + CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + draft: false + prerelease: ${{ contains(github.ref_name, '-pre.') }} + body: | + ## ${{ github.ref_name }} + + - [Documentation](https://docs.rs/axvisor_api) + - [crates.io](https://crates.io/crates/axvisor_api) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + needs: check + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate tag and branch (HEAD-based) + shell: bash + run: | + set -e + + TAG="${{ github.ref_name }}" + TAG_COMMIT=$(git rev-list -n 1 "$TAG") + + git fetch origin main dev + + MAIN_HEAD=$(git rev-parse origin/main) + DEV_HEAD=$(git rev-parse origin/dev) + + echo "Tag: $TAG" + echo "Tag commit: $TAG_COMMIT" + echo "main HEAD: $MAIN_HEAD" + echo "dev HEAD: $DEV_HEAD" + + if [[ "$TAG" == *-pre.* ]]; then + if [ "$TAG_COMMIT" != "$DEV_HEAD" ]; then + echo "❌ prerelease tag must be created from dev HEAD" + exit 1 + fi + echo "✅ prerelease tag validated on dev" + else + if [ "$TAG_COMMIT" != "$MAIN_HEAD" ]; then + echo "❌ stable release tag must be created from main HEAD" + exit 1 + fi + echo "✅ stable release tag validated on main" + fi + + - name: Verify version consistency + run: | + # Extract version from git tag (remove 'v' prefix) + TAG_VERSION="${{ github.ref_name }}" + TAG_VERSION="${TAG_VERSION#v}" + # Extract version from Cargo.toml using cargo metadata + CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "axvisor_api") | .version') + echo "Git tag version: $TAG_VERSION" + echo "Cargo.toml version: $CARGO_VERSION" + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "ERROR: Version mismatch! Tag version ($TAG_VERSION) != Cargo.toml version ($CARGO_VERSION)" + exit 1 + fi + echo "Version check passed!" + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + # Workspace crates must be published in dependency order + - name: Dry run publish (axvisor_api_proc) + run: cargo publish --dry-run -p axvisor_api_proc + + - name: Dry run publish (axvisor_api) + run: cargo publish --dry-run -p axvisor_api + + - name: Publish axvisor_api_proc to crates.io + run: cargo publish -p axvisor_api_proc --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Wait for crates.io to index the proc-macro crate + - name: Wait for crates.io indexing + run: sleep 30 + + - name: Publish axvisor_api to crates.io + run: cargo publish -p axvisor_api --token ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d8abfaf --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + workflow_call: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2025-05-20 + + - name: Run tests + run: cargo test --all-features -- --nocapture + + - name: Run doc tests + run: cargo test --doc --all-features diff --git a/axvisor_api_proc/Cargo.toml b/axvisor_api_proc/Cargo.toml index 5ab1ff9..e84f3d9 100644 --- a/axvisor_api_proc/Cargo.toml +++ b/axvisor_api_proc/Cargo.toml @@ -22,4 +22,4 @@ proc-macro-crate = "3.1.0" # 代码生成 quote = "1.0" # Rust 语法解析 -syn = { version = "2.0", features = ["full"] } +syn = { version = "2.0", features = ["full"] } \ No newline at end of file