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:
| Target | Purpose | Test Coverage |
|---|---|---|
| x86_64-unknown-linux-gnu | Standard Linux development and unit testing | Full (includingcargo test) |
| x86_64-unknown-none | Bare metal x86_64 hypervisor environments | Build and lint only |
| riscv64gc-unknown-none-elf | RISC-V hypervisor platforms | Build and lint only |
| aarch64-unknown-none-softfloat | ARM64 embedded hypervisor systems | Build 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 Type | Command | Purpose |
|---|---|---|
| Code Formatting | cargo fmt --all -- --check | Ensures consistent code style across the codebase |
| Clippy Linting | cargo clippy --target $TARGET --all-features -- -A clippy::new_without_default | Catches common mistakes and enforces Rust best practices |
| Documentation | RUSTDOCFLAGS="-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
| Stage | Validation | Blocking |
|---|---|---|
| Pull Request | All CI checks must pass | Yes |
| Format Check | cargo fmtvalidation | Yes |
| Lint Check | Clippy on all targets | Yes |
| Build Check | Compilation on all targets | Yes |
| Unit Tests | Tests on Linux target only | Yes |
| Documentation | Doc generation and link validation | Yes 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)