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          # FIXME(ppc): SIGILL running tests, see
200          # https://github.com/rust-lang/libc/pull/4254#issuecomment-2636288713
201          # - target: powerpc-unknown-linux-gnu
202          # - target: powerpc-unknown-linux-gnu
203          #   env:
204          #     RUST_LIBC_UNSTABLE_GNU_FILE_OFFSET_BITS: 64
205          #   artifact-tag: offset-bits64
206    timeout-minutes: 25
207    env:
208      TARGET: ${{ matrix.target }}
209    steps:
210      - uses: actions/checkout@v4
211      - name: Setup Rust toolchain
212        run: ./ci/install-rust.sh
213      - uses: Swatinem/rust-cache@v2
214        with:
215          key: ${{ matrix.target }}
216
217      - name: Add matrix env variables to the environment
218        if: matrix.env
219        run: |
220          echo '${{ toJson(matrix.env) }}' |
221            jq -r 'to_entries | map("\(.key)=\(.value|tostring)") | .[]' >>$GITHUB_ENV
222        shell: bash
223
224      - name: Execute run-docker.sh
225        run: ./ci/run-docker.sh ${{ matrix.target }}
226
227      - name: Create CI artifacts
228        id: create_artifacts
229        if: always()
230        run: ./ci/create-artifacts.py
231      - uses: actions/upload-artifact@v4
232        if: always() && steps.create_artifacts.outcome == 'success'
233        with:
234          name: ${{ env.ARCHIVE_NAME }}-${{ matrix.target }}${{ matrix.artifact-tag && format('-{0}', matrix.artifact-tag) }}
235          path: ${{ env.ARCHIVE_PATH }}
236          retention-days: 5
237
238  test_tier2_vm:
239    name: Test tier2 VM
240    needs: [test_tier1, style_check]
241    runs-on: ubuntu-latest
242    strategy:
243      fail-fast: true
244      matrix:
245        target:
246          - x86_64-pc-solaris
247    timeout-minutes: 25
248    steps:
249      - uses: actions/checkout@v4
250      - name: test on Solaris
251        uses: vmactions/solaris-vm@v1.1.3
252        with:
253          release: "11.4-gcc"
254          usesh: true
255          mem: 4096
256          copyback: false
257          prepare: |
258            set -x
259            source <(curl -s https://raw.githubusercontent.com/psumbera/solaris-rust/refs/heads/main/sh.rust-web-install)
260            rustc --version
261            uname -a
262          run: |
263            export PATH=$HOME/.rust_solaris/bin:$PATH
264            ./ci/run.sh ${{ matrix.target }}
265
266  # One job that "summarizes" the success state of this pipeline. This can then be added to branch
267  # protection, rather than having to add each job separately.
268  success:
269    name: success
270    runs-on: ubuntu-24.04
271    needs:
272      - style_check
273      - test_tier1
274      - test_tier2
275      - test_tier2_vm
276      - verify_build
277      - clippy
278    # GitHub branch protection is exceedingly silly and treats "jobs skipped because a dependency
279    # failed" as success. So we have to do some contortions to ensure the job fails if any of its
280    # dependencies fails.
281    if: always() # make sure this is never "skipped"
282    steps:
283      # Manually check the status of all dependencies. `if: failure()` does not work.
284      - name: check if any dependency failed
285        run: jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
286