1name: CI
2
3on:
4  merge_group:
5  pull_request:
6    branches:
7      - libc-0.2
8
9env:
10  CARGO_TERM_COLOR: always
11  CARGO_TERM_VERBOSE: true
12  LIBC_CI: 1
13  RUSTDOCFLAGS: -Dwarnings
14  RUSTFLAGS: -Dwarnings
15  RUST_BACKTRACE: full
16
17defaults:
18  run:
19    shell: bash
20
21jobs:
22  style_check:
23    name: Style check
24    runs-on: ubuntu-24.04
25    timeout-minutes: 10
26    steps:
27      - uses: actions/checkout@v4
28      - name: Setup Rust toolchain
29        run: ./ci/install-rust.sh && rustup component add rustfmt
30      - name: Check style
31        run: ./ci/style.sh
32
33  clippy:
34    name: Clippy on ${{ matrix.os }}
35    strategy:
36      matrix:
37        os: [ubuntu-24.04, macos-14, windows-2022]
38    runs-on: ${{ matrix.os }}
39    timeout-minutes: 10
40    steps:
41      - uses: actions/checkout@v4
42      - run: rustup update stable --no-self-update
43      - uses: Swatinem/rust-cache@v2
44      # Here we use the latest stable Rust toolchain already installed by GitHub
45      # Ideally we should run it for every target, but we cannot rely on unstable toolchains
46      # due to Clippy not being consistent between them.
47      - run: cargo clippy --workspace --exclude libc-test --exclude ctest-test --all-targets -- -D warnings
48
49  # This runs `cargo build --target ...` for all T1 and T2 targets`
50  verify_build:
51    name: Verify build
52    strategy:
53      matrix:
54        toolchain: [stable, nightly, 1.63.0]
55        os: [ubuntu-24.04, macos-14, windows-2022]
56        include:
57          - toolchain: beta
58            os: ubuntu-24.04
59    runs-on: ${{ matrix.os }}
60    timeout-minutes: 25
61    env:
62      TOOLCHAIN: ${{ matrix.toolchain }}
63    steps:
64      - uses: actions/checkout@v4
65      # Remove `-Dwarnings` at the MSRV since lints may be different or buffier
66      - name: Update RUSTFLAGS
67        run: |
68          set -eux
69          [ "${{ matrix.toolchain }}" = "1.63.0" ] && echo 'RUSTFLAGS=' >> "$GITHUB_ENV" || true
70
71      - name: Setup Rust toolchain
72        run: ./ci/install-rust.sh
73
74      # FIXME(ci): These `du` statements are temporary for debugging cache
75      - name: Target size before restoring cache
76        run: du -sh target | sort -k 2 || true
77      - uses: Swatinem/rust-cache@v2
78        with:
79          key: ${{ matrix.os }}-${{ matrix.toolchain }}
80      - name: Target size after restoring cache
81        run: du -sh target | sort -k 2 || true
82
83      - name: Execute build.sh
84        run: ./ci/verify-build.sh
85      - name: Target size after job completion
86        run: du -sh target | sort -k 2
87
88  test_tier1:
89    name: Test tier1
90    strategy:
91      matrix:
92        include:
93          - target: i686-unknown-linux-gnu
94            docker: true
95            os: ubuntu-24.04
96          - target: i686-unknown-linux-gnu
97            docker: true
98            os: ubuntu-24.04
99            artifact-tag: offset-bits64
100            env:
101              RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS: 64
102          - target: x86_64-unknown-linux-gnu
103            docker: true
104            os: ubuntu-24.04
105          - target: aarch64-apple-darwin
106            os: macos-14
107          - target: x86_64-pc-windows-gnu
108            os: windows-2022
109            env:
110              ARCH_BITS: 64
111              ARCH: x86_64
112          - target: x86_64-pc-windows-msvc
113            os: windows-2022
114          # FIXME: It currently causes segfaults.
115          #- target: i686-pc-windows-gnu
116          #  env:
117          #    ARCH_BITS: 32
118          #    ARCH: i686
119          - target: i686-pc-windows-msvc
120            os: windows-2022
121    runs-on: ${{ matrix.os }}
122    timeout-minutes: 25
123    env:
124      TARGET: ${{ matrix.target }}
125    steps:
126      - uses: actions/checkout@v4
127      - name: Setup Rust toolchain
128        run: ./ci/install-rust.sh
129      - uses: Swatinem/rust-cache@v2
130        with:
131          key: ${{ matrix.target }}
132
133      - name: Add matrix env variables to the environment
134        if: matrix.env
135        run: |
136          echo '${{ toJson(matrix.env) }}' |
137            jq -r 'to_entries | map("\(.key)=\(.value|tostring)") | .[]' >>$GITHUB_ENV
138        shell: bash
139
140      - name: Run natively
141        if: "!matrix.docker"
142        run: ./ci/run.sh ${{ matrix.target }}
143      - name: Run in Docker
144        if: "matrix.docker"
145        run: ./ci/run-docker.sh ${{ matrix.target }}
146
147      - name: Create CI artifacts
148        id: create_artifacts
149        if: always()
150        run: ./ci/create-artifacts.py
151      - uses: actions/upload-artifact@v4
152        if: always() && steps.create_artifacts.outcome == 'success'
153        with:
154          name: ${{ env.ARCHIVE_NAME }}-${{ matrix.target }}${{ matrix.artifact-tag && format('-{0}', matrix.artifact-tag) }}
155          path: ${{ env.ARCHIVE_PATH }}
156          retention-days: 5
157
158  test_tier2:
159    name: Test tier2
160    needs: [test_tier1, style_check]
161    runs-on: ubuntu-24.04
162    strategy:
163      fail-fast: true
164      max-parallel: 12
165      matrix:
166        target:
167          # FIXME(sparc): this takes much longer to run than any other job, put
168          # it first to make sure it gets a head start.
169          - sparc64-unknown-linux-gnu
170          - aarch64-linux-android
171          - aarch64-unknown-linux-gnu
172          - aarch64-unknown-linux-musl
173          - arm-linux-androideabi
174          - arm-unknown-linux-musleabihf
175          - i686-linux-android
176          - i686-unknown-linux-musl
177          - loongarch64-unknown-linux-gnu
178          - loongarch64-unknown-linux-musl
179          - powerpc64-unknown-linux-gnu
180          - powerpc64le-unknown-linux-gnu
181          - riscv64gc-unknown-linux-gnu
182          - s390x-unknown-linux-gnu
183          - wasm32-unknown-emscripten
184          - wasm32-wasip1
185          - wasm32-wasip2
186          - x86_64-linux-android
187          # FIXME: Exec format error (os error 8)
188          # - x86_64-unknown-linux-gnux32
189          - x86_64-unknown-linux-musl
190          # FIXME: It seems some items in `src/unix/mod.rs`
191          # aren't defined on redox actually.
192          # - x86_64-unknown-redox
193        include:
194          - target: arm-unknown-linux-gnueabihf
195          - target: arm-unknown-linux-gnueabihf
196            env:
197              RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS: 64
198            artifact-tag: offset-bits64
199          - target: aarch64-unknown-linux-musl
200            env:
201              RUST_LIBC_UNSTABLE_MUSL_V1_2_3: 1
202          - target: arm-unknown-linux-musleabihf
203            env:
204              RUST_LIBC_UNSTABLE_MUSL_V1_2_3: 1
205          - target: i686-unknown-linux-musl
206            env:
207              RUST_LIBC_UNSTABLE_MUSL_V1_2_3: 1
208          - target: loongarch64-unknown-linux-musl
209            env:
210              RUST_LIBC_UNSTABLE_MUSL_V1_2_3: 1
211          # FIXME(ppc): SIGILL running tests, see
212          # https://github.com/rust-lang/libc/pull/4254#issuecomment-2636288713
213          # - target: powerpc-unknown-linux-gnu
214          # - target: powerpc-unknown-linux-gnu
215          #   env:
216          #     RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS: 64
217          #   artifact-tag: offset-bits64
218    timeout-minutes: 25
219    env:
220      TARGET: ${{ matrix.target }}
221    steps:
222      - uses: actions/checkout@v4
223      - name: Setup Rust toolchain
224        run: ./ci/install-rust.sh
225      - uses: Swatinem/rust-cache@v2
226        with:
227          key: ${{ matrix.target }}
228
229      - name: Add matrix env variables to the environment
230        if: matrix.env
231        run: |
232          echo '${{ toJson(matrix.env) }}' |
233            jq -r 'to_entries | map("\(.key)=\(.value|tostring)") | .[]' >>$GITHUB_ENV
234        shell: bash
235
236      - name: Execute run-docker.sh
237        run: ./ci/run-docker.sh ${{ matrix.target }}
238
239      - name: Create CI artifacts
240        id: create_artifacts
241        if: always()
242        run: ./ci/create-artifacts.py
243      - uses: actions/upload-artifact@v4
244        if: always() && steps.create_artifacts.outcome == 'success'
245        with:
246          name: ${{ env.ARCHIVE_NAME }}-${{ matrix.target }}${{ matrix.artifact-tag && format('-{0}', matrix.artifact-tag) }}
247          path: ${{ env.ARCHIVE_PATH }}
248          retention-days: 5
249
250  test_tier2_vm:
251    name: Test tier2 VM
252    needs: [test_tier1, style_check]
253    runs-on: ubuntu-latest
254    strategy:
255      fail-fast: true
256      matrix:
257        target:
258          - x86_64-pc-solaris
259    timeout-minutes: 25
260    steps:
261      - uses: actions/checkout@v4
262      - name: test on Solaris
263        uses: vmactions/solaris-vm@v1.1.3
264        with:
265          release: "11.4-gcc"
266          usesh: true
267          mem: 4096
268          copyback: false
269          prepare: |
270            set -x
271            source <(curl -s https://raw.githubusercontent.com/psumbera/solaris-rust/refs/heads/main/sh.rust-web-install)
272            rustc --version
273            uname -a
274          run: |
275            export PATH=$HOME/.rust_solaris/bin:$PATH
276            ./ci/run.sh ${{ matrix.target }}
277
278  # One job that "summarizes" the success state of this pipeline. This can then be added to branch
279  # protection, rather than having to add each job separately.
280  success:
281    name: success
282    runs-on: ubuntu-24.04
283    needs:
284      - style_check
285      - test_tier1
286      - test_tier2
287      - test_tier2_vm
288      - verify_build
289      - clippy
290    # GitHub branch protection is exceedingly silly and treats "jobs skipped because a dependency
291    # failed" as success. So we have to do some contortions to ensure the job fails if any of its
292    # dependencies fails.
293    if: always() # make sure this is never "skipped"
294    steps:
295      # Manually check the status of all dependencies. `if: failure()` does not work.
296      - name: check if any dependency failed
297        run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
298