From 86b4ad7128d8e923ed4067b9d1326551acad9437 Mon Sep 17 00:00:00 2001
From: Yuekai Jia <equation618@gmail.com>
Date: Fri, 19 Jul 2024 19:10:41 +0800
Subject: [PATCH] Add build and test CI

---
 .../workflows/actions/setup-musl/action.yml   |  38 ++++
 .../workflows/actions/setup-qemu/action.yml   |  46 +++++
 .github/workflows/build.yml                   | 166 ++++++++++++++++++
 .github/workflows/test.yml                    |  33 ++++
 4 files changed, 283 insertions(+)
 create mode 100644 .github/workflows/actions/setup-musl/action.yml
 create mode 100644 .github/workflows/actions/setup-qemu/action.yml
 create mode 100644 .github/workflows/test.yml

diff --git a/.github/workflows/actions/setup-musl/action.yml b/.github/workflows/actions/setup-musl/action.yml
new file mode 100644
index 0000000..b8aee36
--- /dev/null
+++ b/.github/workflows/actions/setup-musl/action.yml
@@ -0,0 +1,38 @@
+name: Download musl toolchain
+
+inputs:
+  arch:
+    description: 'Architecture'
+    required: true
+    type: string
+
+runs:
+  using: "composite"
+  steps:
+  - name: Cache musl
+    id: cache-musl
+    uses: actions/cache/restore@v3
+    with:
+      path: ${{ inputs.arch }}-linux-musl-cross
+      key: ${{ inputs.arch }}-linux-musl-cross
+  - name: Download musl toolchain
+    if: steps.cache-musl.outputs.cache-hit != 'true'
+    shell: bash
+    run: |
+      MUSL_PATH=${{ inputs.arch }}-linux-musl-cross
+      wget https://musl.cc/${MUSL_PATH}.tgz
+      tar -xf ${MUSL_PATH}.tgz
+  - uses: actions/cache/save@v3
+    if: steps.cache-musl.outputs.cache-hit != 'true'
+    with:
+      path: ${{ inputs.arch }}-linux-musl-cross
+      key: ${{ inputs.arch }}-linux-musl-cross
+
+  - name: Add to PATH environment variable
+    shell: bash
+    run: |
+      echo "$PWD/${{ inputs.arch }}-linux-musl-cross/bin" >> $GITHUB_PATH
+  - name: Verify installation
+    shell: bash
+    run: |
+      ${{ inputs.arch }}-linux-musl-gcc --version
diff --git a/.github/workflows/actions/setup-qemu/action.yml b/.github/workflows/actions/setup-qemu/action.yml
new file mode 100644
index 0000000..0f8bc4d
--- /dev/null
+++ b/.github/workflows/actions/setup-qemu/action.yml
@@ -0,0 +1,46 @@
+name: Download and build QEMU
+
+inputs:
+  qemu-version:
+    description: 'QEMU version'
+    required: true
+    type: string
+
+runs:
+  using: "composite"
+  steps:
+  - name: Cache QEMU
+    id: cache-qemu
+    uses: actions/cache/restore@v3
+    with:
+      path: qemu_build
+      key: qemu-${{ inputs.qemu-version }}-slirp-1
+  - name: Download and build QEMU
+    if: steps.cache-qemu.outputs.cache-hit != 'true'
+    env:
+      QEMU_PATH: qemu-${{ inputs.qemu-version }}
+      PREFIX: ${{ github.workspace }}/qemu_build
+    shell: bash
+    run: |
+      sudo apt-get update && sudo apt-get install -y ninja-build libslirp-dev
+      wget https://download.qemu.org/$QEMU_PATH.tar.xz && tar -xJf $QEMU_PATH.tar.xz
+      cd $QEMU_PATH \
+        && ./configure --prefix=$PREFIX --target-list=x86_64-softmmu,riscv64-softmmu,aarch64-softmmu --enable-slirp \
+        && make -j > /dev/null 2>&1 \
+        && make install
+  - uses: actions/cache/save@v3
+    if: steps.cache-qemu.outputs.cache-hit != 'true'
+    with:
+      path: qemu_build
+      key: qemu-${{ inputs.qemu-version }}-slirp-1
+
+  - name: Install QEMU
+    shell: bash
+    run: |
+      echo "$PWD/qemu_build/bin" >> $GITHUB_PATH
+  - name: Verify installation
+    shell: bash
+    run: |
+      qemu-system-x86_64 --version
+      qemu-system-aarch64 --version
+      qemu-system-riscv64 --version
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e69de29..37382ef 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -0,0 +1,166 @@
+name: Build CI
+
+on: [push, pull_request]
+
+jobs:
+  clippy:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        rust-toolchain: [nightly]
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@nightly
+      with:
+        toolchain: ${{ matrix.rust-toolchain }}
+        components: rust-src, clippy, rustfmt
+    - name: Check rust version
+      run: rustc --version --verbose
+    - name: Check code format
+      run: cargo fmt --all -- --check
+    - name: Clippy
+      run: cargo clippy --all-features
+
+  build-rust-apps:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        arch: [x86_64, riscv64, aarch64]
+        rust-toolchain: [nightly]
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust-toolchain }}
+        components: rust-src, llvm-tools
+        targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat
+    - uses: Swatinem/rust-cache@v2
+    - run: cargo install cargo-binutils
+    - name: Build rust/helloworld
+      run: make ARCH=${{ matrix.arch }} A=rust/helloworld
+    - name: Build rust/memtest
+      run: make ARCH=${{ matrix.arch }} A=rust/memtest
+    - name: Build rust/exception
+      run: make ARCH=${{ matrix.arch }} A=rust/exception
+    - name: Build rust/display
+      run: make ARCH=${{ matrix.arch }} A=rust/display
+    - name: Build rust/task/yield
+      run: make ARCH=${{ matrix.arch }} A=rust/task/yield
+    - name: Build rust/task/parallel
+      run: make ARCH=${{ matrix.arch }} A=rust/task/parallel
+    - name: Build rust/task/sleep
+      run: make ARCH=${{ matrix.arch }} A=rust/task/sleep
+    - name: Build rust/task/priority
+      run: make ARCH=${{ matrix.arch }} A=rust/task/priority
+    - name: Build rust/task/tls
+      run: make ARCH=${{ matrix.arch }} A=rust/task/tls
+    - name: Build rust/fs/shell
+      run: make ARCH=${{ matrix.arch }} A=rust/fs/shell
+    - name: Build rust/net/echoserver
+      run: make ARCH=${{ matrix.arch }} A=rust/net/echoserver
+    - name: Build rust/net/httpclient
+      run: make ARCH=${{ matrix.arch }} A=rust/net/httpclient
+    - name: Build rust/net/httpserver
+      run: make ARCH=${{ matrix.arch }} A=rust/net/httpserver
+    - name: Build rust/net/udpserver
+      run: make ARCH=${{ matrix.arch }} A=rust/net/udpserver
+
+  build-c-apps:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        arch: [x86_64, riscv64, aarch64]
+        rust-toolchain: [nightly]
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust-toolchain }}
+        components: rust-src, llvm-tools
+        targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat
+    - uses: ./.github/workflows/actions/setup-musl
+      with:
+        arch: ${{ matrix.arch }}
+    - name: Build c/helloworld
+      run: make ARCH=${{ matrix.arch }} A=c/helloworld
+    - name: Build c/memtest
+      run: make ARCH=${{ matrix.arch }} A=c/memtest
+    - name: Build c/sqlite3
+      run: make ARCH=${{ matrix.arch }} A=c/sqlite3
+    - name: Build c/httpclient
+      run: make ARCH=${{ matrix.arch }} A=c/httpclient
+    - name: Build c/httpserver
+      run: make ARCH=${{ matrix.arch }} A=c/httpserver
+    - name: Build c/udpserver
+      run: make ARCH=${{ matrix.arch }} A=c/udpserver
+    - name: Build c/iperf
+      run: make ARCH=${{ matrix.arch }} A=c/iperf
+    - name: Build c/redis
+      run: make ARCH=${{ matrix.arch }} A=c/redis SMP=4
+
+  build-for-other-platforms:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        rust-toolchain: [nightly]
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust-toolchain }}
+        components: rust-src, llvm-tools
+        targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat
+    - uses: Swatinem/rust-cache@v2
+    - run: cargo install cargo-binutils
+    - uses: ./.github/workflows/actions/setup-musl
+      with:
+        arch: x86_64
+
+    - name: Build helloworld for x86_64-pc-oslab
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=x86_64-pc-oslab A=rust/helloworld
+    - name: Build net/httpserver for x86_64-pc-oslab
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=x86_64-pc-oslab A=rust/net/httpserver FEATURES=driver-ixgbe
+    - name: Build c/iperf for x86_64-pc-oslab
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=x86_64-pc-oslab A=c/iperf FEATURES=driver-ixgbe,driver-ramdisk
+    - name: Build c/redis for x86_64-pc-oslab
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=x86_64-pc-oslab A=c/redis FEATURES=driver-ixgbe,driver-ramdisk SMP=4
+
+    - name: Build helloworld for aarch64-raspi4
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=aarch64-raspi4 A=rust/helloworld
+    - name: Build fs/shell for aarch64-raspi4
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=aarch64-raspi4 A=rust/fs/shell FEATURES=driver-bcm2835-sdhci
+
+    - name: Build helloworld for aarch64-bsta1000b
+      continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
+      run: make PLATFORM=aarch64-bsta1000b A=rust/helloworld
+
+  build-apps-for-std:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        arch: [x86_64]
+        rust-toolchain: [nightly, nightly-2024-05-02]
+    env:
+      RUSTUP_TOOLCHAIN: ${{ matrix.rust-toolchain }}
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: ${{ matrix.rust-toolchain }}
+    - name: Build apps for std
+      run: cargo build --all --exclude arceos-display
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..48f4625
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,33 @@
+name: Test CI
+
+on: [push, pull_request]
+
+env:
+  qemu-version: 8.2.0
+
+jobs:
+  app-test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        arch: [x86_64, riscv64, aarch64]
+    steps:
+    - uses: actions/checkout@v4
+    - uses: dtolnay/rust-toolchain@stable
+      with:
+        toolchain: nightly
+        components: rust-src
+    - uses: Swatinem/rust-cache@v2
+    - run: cargo install cargo-binutils
+    - uses: ./.github/workflows/actions/setup-qemu
+      with:
+        qemu-version: ${{ env.qemu-version }}
+    - uses: ./.github/workflows/actions/setup-musl
+      with:
+        arch: ${{ matrix.arch }}
+    - name: Run app tests
+      run: |
+        make disk_img
+        make test ARCH=${{ matrix.arch }}