Build System and CI

Relevant source files

This page documents the automated build system and continuous integration (CI) pipeline for the axdevice_crates repository. It covers the multi-architecture build strategy, code quality enforcement, testing procedures, and documentation generation workflow that ensures the codebase maintains compatibility across different target platforms in the no_std embedded environment.

For information about implementing new devices within this build system, see Implementing New Devices. For details about the core architecture that this build system validates, see Core Architecture.

CI Pipeline Overview

The repository uses GitHub Actions to implement a comprehensive CI pipeline that validates code across multiple architectures and maintains documentation quality. The pipeline consists of two primary jobs: ci for code validation and doc for documentation generation.

CI Job Architecture

flowchart TD
subgraph subGraph2["Build Steps"]
    CHECKOUT["actions/checkout@v4"]
    TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    VERSION["rustc --version --verbose"]
    FMT["cargo fmt --all -- --check"]
    CLIPPY["cargo clippy --target TARGET --all-features"]
    BUILD["cargo build --target TARGET --all-features"]
    TEST["cargo test --target x86_64-unknown-linux-gnu"]
end
subgraph subGraph1["CI Job Matrix"]
    RUST["rust-toolchain: nightly"]
    T1["x86_64-unknown-linux-gnu"]
    T2["x86_64-unknown-none"]
    T3["riscv64gc-unknown-none-elf"]
    T4["aarch64-unknown-none-softfloat"]
end
subgraph subGraph0["GitHub Actions Triggers"]
    PUSH["push events"]
    PR["pull_request events"]
end

BUILD --> TEST
CHECKOUT --> TOOLCHAIN
CLIPPY --> BUILD
FMT --> CLIPPY
PR --> RUST
PUSH --> RUST
RUST --> T1
RUST --> T2
RUST --> T3
RUST --> T4
T1 --> CHECKOUT
T2 --> CHECKOUT
T3 --> CHECKOUT
T4 --> CHECKOUT
TOOLCHAIN --> VERSION
VERSION --> FMT

Sources: .github/workflows/ci.yml(L1 - L31) 

Documentation Job Pipeline

flowchart TD
subgraph subGraph2["Deployment Conditions"]
    MAIN_BRANCH["github.ref == default-branch"]
    GH_PAGES["branch: gh-pages"]
    DOC_FOLDER["folder: target/doc"]
end
subgraph subGraph1["Environment Configuration"]
    RUSTDOCFLAGS["-D rustdoc::broken_intra_doc_links -D missing-docs"]
    DEFAULT_BRANCH["refs/heads/default_branch"]
    PERMISSIONS["contents: write"]
end
subgraph subGraph0["Documentation Generation"]
    DOC_CHECKOUT["actions/checkout@v4"]
    DOC_TOOLCHAIN["dtolnay/rust-toolchain@nightly"]
    DOC_BUILD["cargo doc --no-deps --all-features"]
    INDEX_GEN["Generate target/doc/index.html redirect"]
    PAGES_DEPLOY["JamesIves/github-pages-deploy-action@v4"]
end

DEFAULT_BRANCH --> MAIN_BRANCH
DOC_BUILD --> INDEX_GEN
DOC_CHECKOUT --> DOC_TOOLCHAIN
DOC_TOOLCHAIN --> DOC_BUILD
INDEX_GEN --> MAIN_BRANCH
MAIN_BRANCH --> PAGES_DEPLOY
PAGES_DEPLOY --> DOC_FOLDER
PAGES_DEPLOY --> GH_PAGES
PERMISSIONS --> PAGES_DEPLOY
RUSTDOCFLAGS --> DOC_BUILD

Sources: .github/workflows/ci.yml(L32 - L56) 

Multi-Architecture Build Strategy

The CI pipeline validates compatibility across four distinct target architectures, each serving different use cases in the hypervisor ecosystem:

TargetPurposeTest Coverage
x86_64-unknown-linux-gnuStandard Linux development and unit testingFull (includingcargo test)
x86_64-unknown-noneBare metal x86_64 hypervisor environmentsBuild and lint only
riscv64gc-unknown-none-elfRISC-V hypervisor platformsBuild and lint only
aarch64-unknown-none-softfloatARM64 embedded hypervisor systemsBuild and lint only

Build Matrix Configuration

The build matrix uses a fail-fast: false strategy to ensure all target architectures are tested independently, preventing early termination when one target fails.

flowchart TD
subgraph subGraph1["Build Validation Steps"]
    VERSION_CHECK["rustc --version --verbose"]
    FORMAT_CHECK["cargo fmt --all -- --check"]
    LINT_CHECK["cargo clippy --target TARGET --all-features"]
    BUILD_CHECK["cargo build --target TARGET --all-features"]
    UNIT_TEST["cargo test (x86_64-linux only)"]
end
subgraph subGraph0["Rust Toolchain Setup"]
    NIGHTLY["nightly toolchain"]
    COMPONENTS["rust-src, clippy, rustfmt"]
    TARGETS["matrix.targets"]
end

BUILD_CHECK --> UNIT_TEST
COMPONENTS --> FORMAT_CHECK
COMPONENTS --> LINT_CHECK
FORMAT_CHECK --> LINT_CHECK
LINT_CHECK --> BUILD_CHECK
NIGHTLY --> VERSION_CHECK
TARGETS --> BUILD_CHECK
TARGETS --> LINT_CHECK
VERSION_CHECK --> FORMAT_CHECK

Sources: .github/workflows/ci.yml(L8 - L19)  .github/workflows/ci.yml(L25 - L30) 

Code Quality Enforcement

The CI pipeline enforces multiple layers of code quality validation before any changes can be merged.

Formatting and Linting

Check TypeCommandPurpose
Code Formattingcargo fmt --all -- --checkEnsures consistent code style across the codebase
Clippy Lintingcargo clippy --target $TARGET --all-features -- -A clippy::new_without_defaultCatches common mistakes and enforces Rust best practices
DocumentationRUSTDOCFLAGS="-D rustdoc::broken_intra_doc_links -D missing-docs"Enforces complete documentation with valid links

The Clippy configuration specifically allows the clippy::new_without_default lint, indicating that the codebase may contain new() methods without corresponding Default implementations, which is common in no_std environments.

Sources: .github/workflows/ci.yml(L22 - L25)  .github/workflows/ci.yml(L40) 

Testing Strategy

The testing approach is pragmatic, recognizing the constraints of no_std embedded development:

Unit Testing Limitations

flowchart TD
subgraph subGraph2["Quality Assurance"]
    CLIPPY_ALL["Clippy runs on all targets"]
    FORMAT_ALL["Format check runs on all targets"]
    COMPILE_ALL["Compilation check on all targets"]
end
subgraph subGraph1["Test Execution"]
    LINUX_ONLY["Unit tests run only on x86_64-unknown-linux-gnu"]
    TEST_CMD["cargo test --target x86_64-unknown-linux-gnu -- --nocapture"]
    BUILD_VALIDATION["Build validation on all targets"]
end
subgraph subGraph0["Testing Constraints"]
    NOSTD["no_std environment"]
    EMBEDDED["Embedded targets"]
    LIMITED["Limited std library access"]
end

BUILD_VALIDATION --> COMPILE_ALL
CLIPPY_ALL --> FORMAT_ALL
COMPILE_ALL --> CLIPPY_ALL
EMBEDDED --> BUILD_VALIDATION
LIMITED --> LINUX_ONLY
LINUX_ONLY --> TEST_CMD
NOSTD --> LINUX_ONLY

Unit tests execute only on x86_64-unknown-linux-gnu because the other targets are bare metal environments that lack the standard library infrastructure required for Rust's test framework. However, the build validation ensures that the code compiles correctly for all target architectures.

Sources: .github/workflows/ci.yml(L28 - L30) 

Documentation Generation and Deployment

The documentation system automatically generates and deploys API documentation using a dedicated job that runs in parallel with the main CI validation.

Documentation Build Process

The documentation generation uses strict flags to ensure high-quality documentation:

flowchart TD
subgraph Deployment["Deployment"]
    BRANCH_CHECK["github.ref == default-branch"]
    DEPLOY_ACTION["JamesIves/github-pages-deploy-action@v4"]
    SINGLE_COMMIT["single-commit: true"]
    GH_PAGES_BRANCH["branch: gh-pages"]
    DOC_FOLDER["folder: target/doc"]
end
subgraph subGraph1["Build Process"]
    DOC_CMD["cargo doc --no-deps --all-features"]
    INDEX_CREATE["printf redirect to generated docs"]
    TREE_PARSE["cargo tree | head -1 | cut -d' ' -f1"]
end
subgraph subGraph0["Documentation Flags"]
    BROKEN_LINKS["-D rustdoc::broken_intra_doc_links"]
    MISSING_DOCS["-D missing-docs"]
    RUSTDOCFLAGS["RUSTDOCFLAGS environment"]
end

BRANCH_CHECK --> DEPLOY_ACTION
BROKEN_LINKS --> RUSTDOCFLAGS
DEPLOY_ACTION --> DOC_FOLDER
DEPLOY_ACTION --> GH_PAGES_BRANCH
DEPLOY_ACTION --> SINGLE_COMMIT
DOC_CMD --> TREE_PARSE
INDEX_CREATE --> BRANCH_CHECK
MISSING_DOCS --> RUSTDOCFLAGS
RUSTDOCFLAGS --> DOC_CMD
TREE_PARSE --> INDEX_CREATE

The --no-deps flag ensures that only the crate's own documentation is generated, not its dependencies. The automatic index.html generation creates a redirect to the main crate documentation.

Sources: .github/workflows/ci.yml(L40)  .github/workflows/ci.yml(L44 - L48)  .github/workflows/ci.yml(L49 - L55) 

Development Workflow Integration

The CI system is designed to support the typical development workflow for embedded hypervisor development:

Branch Protection and Quality Gates

StageValidationBlocking
Pull RequestAll CI checks must passYes
Format Checkcargo fmtvalidationYes
Lint CheckClippy on all targetsYes
Build CheckCompilation on all targetsYes
Unit TestsTests on Linux target onlyYes
DocumentationDoc generation and link validationYes for main branch

Error Handling Strategy

The documentation job uses conditional error handling with continue-on-error set based on branch and event type, allowing documentation builds to fail on non-main branches and non-pull-request events without blocking the overall workflow.

flowchart TD
subgraph subGraph1["Build Outcomes"]
    MAIN_BRANCH_STRICT["Main branch: strict documentation"]
    PR_STRICT["Pull requests: strict documentation"]
    OTHER_LENIENT["Other contexts: lenient documentation"]
end
subgraph subGraph0["Error Handling Logic"]
    BRANCH_CHECK["github.ref != default-branch"]
    EVENT_CHECK["github.event_name != pull_request"]
    CONTINUE_ERROR["continue-on-error: true"]
    STRICT_MODE["continue-on-error: false"]
end

BRANCH_CHECK --> CONTINUE_ERROR
CONTINUE_ERROR --> OTHER_LENIENT
EVENT_CHECK --> CONTINUE_ERROR
STRICT_MODE --> MAIN_BRANCH_STRICT
STRICT_MODE --> PR_STRICT

This approach ensures that documentation quality is enforced where it matters most (main branch and pull requests) while allowing experimental branches to have temporary documentation issues.

Sources: .github/workflows/ci.yml(L45)  .github/workflows/ci.yml(L39)