Skip to content

Commit 9aac86a

Browse files
committed
Rewrite the build system with CMake
This commit is an attempt to provide a concrete path forward on #425. I personally think it's pretty important to get the ability to have more architectures here but at the same time I also think it's important to to take this as an opportunity to refactor and improve the build system of this repository. To that end this represents my attempt to improve the status quo. This removes the old `Makefile` and replaces it with a CMake-based system to build all these projects. Overall this is intended to be a "no functional change" intended sort of refactoring. Changing build systems inevitably causes issues, however, so this change additionally has a very high likelihood of needing follow-up fixes. At a high enough level this commit introduces two major changes to how this repository is built: 1. The `make`-based system (the root `Makefile`) is replaced with CMake. This additionally updates tests to use CMake. 2. A single "build" is split into either building a toolchain or building a sysroot. This enables builds to only build one or the other as necessary. The first change, using CMake, is due to the fact that using `make` on Windows basically is not pleasant coupled with the fact that more advanced logic, such as changing flags, compilers, etc, is much easier with a CMake-based system. The second change is intended to cover the use case of #425 in addition to refactoring the current build. Throughout this change I have intentionally not tried to keep a 1:1 correspondance with behaviors in the old `Makefile` because much of this PR is intended to address shortcomings in the old build system. A list of changes, improvements, etc, made here are: * CMake provides a much nicer portability story to Windows than `make`. This is moving towards the direction of not needing `bash`, for example, to build an SDK. Currently `wasi-libc` still requires this, but that's now the only "hard" dependency. * The set of targets built can now be configured for smaller builds and/or debugging just a single target. All WASI targets are still built by default but it's much easier to add/remove them. * Different targets are now able to be built in parallel as opposed to the unconditional serial-nature of the `Makefile`. * Use of `ninja` is no longer required and separate build systems can be used if desired. * The sysroot and the toolchain can now be built with different CMake build profiles. For example the `Makefile` hardcoded `MinSizeRel` and `RelWithDebInfo` and this can now be much more easily customized by the SDK builder. * Tarballs are now more consistently produced and named. For a tarball of the name `foo.tar.gz` it's guaranteed that there's a single folder `foo` created when unpacking the tarball. * The macOS binaries are no longer hybrid x64/arm64 binaries which greatly inflates the size of the SDK. There's now a separate build for each architecture. * CI now produces arm64-linux binaries. The sysroot is not built on the arm64-linux builder and the sysroot from the x86_64-linux builder is used instead. * Tests are almost ready to execute on Windows, there's just a few minor issues related to exit statuses and probably line endings which need to be worked out. Will require someone with a Windows checkout, however. * Tests are now integrated into CMake. This means that the wasm binaries are able to be built in parallel and the tests are additionally executed in parallel with `ctest`. It is possible to build/run a single test. Tests no longer place all of their output in the source tree. * Out-of-tree builds are now possible and the build/installation directories can both be customized. * CI configuration of Windows/macOS/Linux is much more uniform by having everything in one build matrix instead of separate matrices. * Linux builds are exclusively done in docker containers in CI now. CI no longer produces two Linux builds only for one to be discarded when artifacts are published. * Windows 32-bit builds are no longer produced in CI since it's expected that everyone actually wants the 64-bit ones instead. * Use of `ccache` is now automatically enabled if it's detected on the system. * Many preexisting shell scripts are now translated to CMake one way or another. * There's no longer a separate build script for how to build wasi-sdk in docker and outside of docker which needs to be kept in sync, everything funnels through the same script. * The `docker/Dockerfile` build of wasi-sdk now uses the actual toolchain built from CI and additionally doesn't duplicate various CMake-based configuration files. Overall one thing I want to additionally point out is that I'm not CMake expert. I suspect there's lots of little stylistic and such improvements that can be made.
1 parent 8827cdf commit 9aac86a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1045
-875
lines changed

.github/workflows/main.yml

+124-116
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,39 @@ on:
1111

1212
jobs:
1313
build:
14-
name: Native Build
14+
name: Build ${{ matrix.artifact }}
1515
runs-on: ${{ matrix.os }}
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
os:
20-
- ubuntu-latest
21-
- macos-latest
19+
include:
20+
- artifact: x86_64-linux
21+
os: ubuntu-latest
22+
23+
- artifact: arm64-linux
24+
os: ubuntu-latest
25+
rust_target: aarch64-unknown-linux-gnu
26+
27+
- artifact: arm64-macos
28+
os: macos-latest
29+
llvm_cmake_flags: -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_OSX_ARCHITECTURES=arm64
30+
rust_target: aarch64-apple-darwin
31+
32+
- artifact: x86_64-macos
33+
os: macos-latest
34+
llvm_cmake_flags: -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_OSX_ARCHITECTURES=x86_64
35+
rust_target: x86_64-apple-darwin
36+
skip_sysroot: 1
37+
38+
- artifact: x86_64-windows
39+
os: windows-latest
40+
# TODO: tests are pretty close to passing on Windows but need some
41+
# final tweaks, namely testing the exit code doesn't work since
42+
# exit codes are different on Windows and the `mmap.c` tests seems
43+
# to have issues probably with line endings. Needs someone with a
44+
# Windows checkout tot test further.
45+
skip_tests: 1
2246
steps:
23-
- uses: actions/cache@v4
24-
with:
25-
path: ~/.cache/ccache
26-
# Bump the prefix number to evict all previous caches and
27-
# enforce a clean build, in the unlikely case that some
28-
# weird build error occur and ccache becomes a potential
29-
# suspect.
30-
key: 0-cache-ubuntu-latest-${{ github.run_id }}
31-
restore-keys: |
32-
0-cache-ubuntu-latest
33-
if: matrix.os == 'ubuntu-latest'
34-
- uses: actions/cache@v4
35-
with:
36-
path: ~/Library/Caches/ccache
37-
key: 0-cache-macos-latest-${{ github.run_id }}
38-
restore-keys: |
39-
0-cache-macos-latest
40-
if: matrix.os == 'macos-latest'
41-
- name: Setup `wasmtime` for tests
42-
uses: bytecodealliance/actions/wasmtime/setup@v1
43-
with:
44-
version: "18.0.2"
4547
- uses: actions/checkout@v4
4648
with:
4749
fetch-depth: 0
@@ -53,108 +55,124 @@ jobs:
5355
# Server does not allow request for unadvertised object" in the
5456
# future.
5557
- run: git submodule update --init --depth 32 --jobs 3
58+
59+
# Persist ccache-based caches across builds. This directory is configured
60+
# via the CCACHE_DIR env var below for ccache to use.
61+
#
62+
# Bump the prefix number to evict all previous caches and enforce a clean
63+
# build, in the unlikely case that some weird build error occur and ccache
64+
# becomes a potential suspect.
65+
- uses: actions/cache@v4
66+
id: cache-restore
67+
with:
68+
path: ${{ runner.tool_cache }}/ccache
69+
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
70+
restore-keys: |
71+
0-cache-${{ matrix.artifact }}-
72+
- run: |
73+
mkdir -p '${{ runner.tool_cache }}/ccache'
74+
echo 'CCACHE_DIR=${{ runner.tool_cache }}/ccache' >> $GITHUB_ENV
75+
shell: bash
76+
77+
# Configure CMake flags for `ci/build.sh` as necessary for each
78+
# matrix entry.
79+
- run: echo WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS=${{ matrix.llvm_cmake_flags }} >> $GITHUB_ENV
80+
if: matrix.llvm_cmake_flags != ''
81+
shell: bash
82+
- run: |
83+
cmake_args=-DWASI_SDK_ARTIFACT=${{ matrix.artifact }}
84+
if [ "${{ matrix.rust_target }}" != "" ]; then
85+
rustup target add ${{ matrix.rust_target }}
86+
cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}"
87+
fi
88+
echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV
89+
shell: bash
90+
- run: echo WASI_SDK_CI_SKIP_SYSROOT=1 >> $GITHUB_ENV
91+
if: matrix.skip_sysroot != ''
92+
- run: echo WASI_SDK_CI_SKIP_TESTS=1 >> $GITHUB_ENV
93+
if: matrix.skip_tests != ''
94+
95+
# Add some extra installed software on each runner as necessary.
96+
- name: Setup `wasmtime` for tests
97+
uses: bytecodealliance/actions/wasmtime/setup@v1
98+
with:
99+
version: "18.0.2"
56100
- name: Install ccache, ninja (macOS)
57101
run: brew install ccache ninja
58-
if: matrix.os == 'macos-latest'
102+
if: runner.os == 'macOS'
103+
- name: Install ccache, ninja (Windows)
104+
run: choco install ccache ninja
105+
if: runner.os == 'Windows'
59106
- name: Install ccache, ninja (Linux)
60-
run: sudo apt install ccache ninja-build
61-
if: matrix.os == 'ubuntu-latest'
62-
- name: Build
63-
run: NINJA_FLAGS=-v make package LLVM_CMAKE_FLAGS=-DLLVM_CCACHE_BUILD=ON
107+
run: sudo apt install ccache
108+
if: runner.os == 'Linux'
109+
110+
- name: Build and test (macOS)
111+
run: ./ci/build.sh
112+
if: runner.os == 'macOS'
113+
114+
- name: Build and test (Linux)
115+
run: ./ci/docker-build.sh ${{ matrix.artifact }}
116+
if: runner.os == 'Linux'
117+
118+
# Use a shorter build directory than the default on Windows to avoid
119+
# hitting path length and command line length limits.
120+
- name: Build and test (Windows)
121+
run: ./ci/build.sh C:/wasi-sdk
64122
shell: bash
65-
- name: Run the testsuite
66-
run: NINJA_FLAGS=-v make check RUNTIME=wasmtime
123+
if: runner.os == 'Windows'
124+
125+
# Upload the `dist` folder from the build as the artifacts for this
126+
# runner.
67127
- name: Upload artifacts
68128
uses: actions/upload-artifact@v4
69129
with:
70-
# Upload the dist folder. Give it a name according to the OS it was built for.
71-
name: ${{ format( 'dist-{0}', matrix.os) }}
72-
path: dist
130+
name: ${{ format( 'dist-{0}', matrix.artifact) }}
131+
path: build/dist
73132

74-
winbuild:
75-
name: Windows Build
76-
runs-on: windows-latest
77-
strategy:
78-
fail-fast: false
79-
matrix:
80-
include:
81-
- arch: x64
82-
sys: clang64
83-
env: clang-x86_64
84-
- arch: x86
85-
sys: clang32
86-
env: clang-i686
87-
steps:
88-
- uses: actions/cache@v4
89-
with:
90-
path: ~/AppData/Local/ccache
91-
key: 0-${{ format( 'cache-windows-latest-{0}', matrix.arch) }}-${{ github.run_id }}
92-
restore-keys: |
93-
0-${{ format( 'cache-windows-latest-{0}', matrix.arch) }}
94-
- uses: msys2/setup-msys2@v2
95-
with:
96-
install: >-
97-
base-devel
98-
git
99-
mingw-w64-${{ matrix.env }}-ccache
100-
mingw-w64-${{ matrix.env }}-cmake
101-
mingw-w64-${{ matrix.env }}-ninja
102-
mingw-w64-${{ matrix.env }}-toolchain
103-
msystem: ${{ matrix.sys }}
104-
update: true
105-
release: false
106-
path-type: inherit
107-
- uses: actions/checkout@v4
108-
with:
109-
fetch-depth: 0
110-
- run: git fetch --tags --force
111-
name: Force-fetch tags to work around actions/checkout#290
112-
- run: git submodule update --init --depth 32 --jobs 3
113-
- name: Build
114-
shell: msys2 {0}
115-
run: |
116-
make package LLVM_CMAKE_FLAGS=-DLLVM_CCACHE_BUILD=ON
117-
make check
118-
- name: Does it work sans msys2?
119-
run: |
120-
C:\wasi-sdk\bin\clang.exe --version
121-
C:\wasi-sdk\bin\llvm-ar.exe --version
122-
C:\wasi-sdk\bin\wasm-ld.exe --version
123-
- name: Upload artifacts
124-
uses: actions/upload-artifact@v4
133+
# Help debug ccache issues by showing what happened.
134+
- if: always()
135+
name: Show ccache statistics
136+
run: ccache --show-stats
137+
138+
# Always save a cache, even if the build failed. This ensures that if
139+
# live-debugging via CI the build gets to pick up where it left off last
140+
# time instead of having to recreate everything each time a failure
141+
# happens.
142+
- if: always() && steps.cache-restore.outputs.cache-hit != 'true'
143+
uses: actions/cache/save@v4
125144
with:
126-
# Upload the dist folder. Give it a name according to the OS it was built for.
127-
name: ${{ format( 'dist-windows-latest-{0}', matrix.arch) }}
128-
path: dist
145+
path: ${{ runner.tool_cache }}/ccache
146+
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
129147

130-
dockerbuild:
131-
name: Docker Build
148+
# Once all of the above matrix entries have completed this job will run and
149+
# assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot
150+
# artifacts.
151+
finalize:
152+
name: Finalize wasi-sdk artifacts
153+
needs: build
132154
runs-on: ubuntu-latest
133155
steps:
134-
- uses: actions/cache@v4
135-
with:
136-
path: ~/.ccache
137-
key: 0-cache-ubuntu-bionic-${{ github.run_id }}
138-
restore-keys: |
139-
0-cache-ubuntu-bionic
140-
141156
- uses: actions/checkout@v4
142-
with:
143-
fetch-depth: 0
144-
- run: git fetch --tags --force
145-
name: Force-fetch tags to work around actions/checkout#290
146157

147-
- run: git submodule update --init --depth 32 --jobs 3
158+
# Download all artifacts from all platforms in `build`, merge them into
159+
# final wasi-sdk-* artifacts, and then upload them.
160+
- uses: actions/download-artifact@v4
161+
- run: ./ci/merge-artifacts.sh
162+
- uses: actions/upload-artifact@v4
163+
with:
164+
name: release-artifacts
165+
path: dist
148166

167+
# Use the `wasi-sdk-*` artifacts just created to create a docker image
168+
# with a toolchain pre-installed.
149169
- uses: docker/login-action@v2
150170
with:
151171
registry: ghcr.io
152172
username: ${{ github.actor }}
153173
password: ${{ secrets.GITHUB_TOKEN }}
154-
155174
- uses: docker/setup-qemu-action@v2
156175
- uses: docker/setup-buildx-action@v2
157-
158176
- uses: docker/metadata-action@v4
159177
id: meta
160178
with:
@@ -165,16 +183,6 @@ jobs:
165183
type=ref,event=tag
166184
type=ref,event=pr
167185
type=sha
168-
169-
- name: Run docker_build script
170-
run: ./docker_build.sh
171-
- name: Upload artifacts
172-
uses: actions/upload-artifact@v4
173-
with:
174-
# Upload the dist folder. Give it a name according to the OS it was built for.
175-
name: dist-ubuntu-bionic
176-
path: dist
177-
178186
- name: Build and push wasi-sdk docker image
179187
uses: docker/build-push-action@v3
180188
with:

CMakeLists.txt

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Build logic for building both a toolchain and a sysroot for WASI.
2+
#
3+
# This top level `CMakeLists.txt` file can be used either to build a clang
4+
# toolchain or a WASI sysroot. Note that this can't be done at the same time.
5+
# A toolchain build requires a compiler for the target architecture. A
6+
# WASI sysroot build requires this previous compiler and must be runnable on
7+
# the host.
8+
9+
cmake_minimum_required(VERSION 3.26)
10+
project(wasi-sdk)
11+
include(ExternalProject)
12+
13+
set(WASI_SDK_TARGETS "wasm32-wasi;wasm32-wasip1;wasm32-wasip2;wasm32-wasip1-threads;wasm32-wasi-threads"
14+
CACHE STRING "List of WASI targets to build")
15+
option(WASI_SDK_BUILD_TOOLCHAIN "Build a toolchain instead of the sysroot" OFF)
16+
17+
set(llvm_proj_dir ${CMAKE_CURRENT_SOURCE_DIR}/src/llvm-project)
18+
set(wasi_libc ${CMAKE_CURRENT_SOURCE_DIR}/src/wasi-libc)
19+
20+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
21+
include(wasi-sdk-enable-ccache)
22+
23+
find_program(PYTHON python3 python REQUIRED)
24+
25+
# Set some variables based on the `version.py` script
26+
set(version_script ${CMAKE_CURRENT_SOURCE_DIR}/version.py)
27+
execute_process(
28+
COMMAND ${PYTHON} ${version_script} llvm-major --llvm-dir=${llvm_proj_dir}
29+
OUTPUT_VARIABLE clang_version
30+
OUTPUT_STRIP_TRAILING_WHITESPACE)
31+
execute_process(
32+
COMMAND ${PYTHON} ${version_script}
33+
OUTPUT_VARIABLE wasi_sdk_version
34+
OUTPUT_STRIP_TRAILING_WHITESPACE)
35+
36+
message(STATUS "wasi-sdk toolchain LLVM version is ${clang_version}")
37+
message(STATUS "wasi-sdk version is ${wasi_sdk_version}")
38+
39+
# Only include one version of the build logic as pulling in both isn't
40+
# supported at this time.
41+
if(WASI_SDK_BUILD_TOOLCHAIN)
42+
include(wasi-sdk-toolchain)
43+
else()
44+
include(wasi-sdk-sysroot)
45+
endif()

Dockerfile

-39
This file was deleted.

0 commit comments

Comments
 (0)