Skip to content

Commit e29a3fe

Browse files
authored
Rewrite the build system with CMake (#429)
* 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. * Fix skipping tests on windows * Fetch a full depth in the finalize workflow too * Fix multi-arch docker build * Fix assembling of sysroot * Fix script syntax * Clean up the merge script slightly * Remove Pat's email * Move configuration of CMAKE_EXECUTABLE_SUFFIX * Remove redundant sysroot option * Fix comment in testcase.sh * Update new p2 cmake files * Remove now-duplicate wasi-sdk-p2.cmake
1 parent 873163a commit e29a3fe

Some content is hidden

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

46 files changed

+1067
-906
lines changed

.github/workflows/main.yml

+126-112
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,130 @@ 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+
shell: bash
92+
if: matrix.skip_sysroot != ''
93+
- run: echo WASI_SDK_CI_SKIP_TESTS=1 >> $GITHUB_ENV
94+
shell: bash
95+
if: matrix.skip_tests != ''
96+
97+
# Add some extra installed software on each runner as necessary.
98+
- name: Setup `wasmtime` for tests
99+
uses: bytecodealliance/actions/wasmtime/setup@v1
100+
with:
101+
version: "18.0.2"
56102
- name: Install ccache, ninja (macOS)
57103
run: brew install ccache ninja
58-
if: matrix.os == 'macos-latest'
104+
if: runner.os == 'macOS'
105+
- name: Install ccache, ninja (Windows)
106+
run: choco install ccache ninja
107+
if: runner.os == 'Windows'
59108
- 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
109+
run: sudo apt install ccache
110+
if: runner.os == 'Linux'
111+
112+
- name: Build and test (macOS)
113+
run: ./ci/build.sh
114+
if: runner.os == 'macOS'
115+
116+
- name: Build and test (Linux)
117+
run: ./ci/docker-build.sh ${{ matrix.artifact }}
118+
if: runner.os == 'Linux'
119+
120+
# Use a shorter build directory than the default on Windows to avoid
121+
# hitting path length and command line length limits.
122+
- name: Build and test (Windows)
123+
run: ./ci/build.sh C:/wasi-sdk
64124
shell: bash
65-
- name: Run the testsuite
66-
run: NINJA_FLAGS=-v make check RUNTIME=wasmtime
125+
if: runner.os == 'Windows'
126+
127+
# Upload the `dist` folder from the build as the artifacts for this
128+
# runner.
67129
- name: Upload artifacts
68130
uses: actions/upload-artifact@v4
69131
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
132+
name: ${{ format( 'dist-{0}', matrix.artifact) }}
133+
path: build/dist
73134

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
135+
# Help debug ccache issues by showing what happened.
136+
- if: always()
137+
name: Show ccache statistics
138+
run: ccache --show-stats
139+
140+
# Always save a cache, even if the build failed. This ensures that if
141+
# live-debugging via CI the build gets to pick up where it left off last
142+
# time instead of having to recreate everything each time a failure
143+
# happens.
144+
- if: always() && steps.cache-restore.outputs.cache-hit != 'true'
145+
uses: actions/cache/save@v4
125146
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
147+
path: ${{ runner.tool_cache }}/ccache
148+
key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }}
129149

130-
dockerbuild:
131-
name: Docker Build
150+
# Once all of the above matrix entries have completed this job will run and
151+
# assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot
152+
# artifacts.
153+
finalize:
154+
name: Finalize wasi-sdk artifacts
155+
needs: build
132156
runs-on: ubuntu-latest
133157
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-
141158
- uses: actions/checkout@v4
142159
with:
143160
fetch-depth: 0
144161
- run: git fetch --tags --force
145162
name: Force-fetch tags to work around actions/checkout#290
146163

147-
- run: git submodule update --init --depth 32 --jobs 3
164+
# Download all artifacts from all platforms in `build`, merge them into
165+
# final wasi-sdk-* artifacts, and then upload them.
166+
- uses: actions/download-artifact@v4
167+
- run: ./ci/merge-artifacts.sh
168+
- uses: actions/upload-artifact@v4
169+
with:
170+
name: release-artifacts
171+
path: dist
148172

173+
# Use the `wasi-sdk-*` artifacts just created to create a docker image
174+
# with a toolchain pre-installed.
149175
- uses: docker/login-action@v2
150176
with:
151177
registry: ghcr.io
152178
username: ${{ github.actor }}
153179
password: ${{ secrets.GITHUB_TOKEN }}
154-
155180
- uses: docker/setup-qemu-action@v2
156181
- uses: docker/setup-buildx-action@v2
157-
158182
- uses: docker/metadata-action@v4
159183
id: meta
160184
with:
@@ -165,16 +189,6 @@ jobs:
165189
type=ref,event=tag
166190
type=ref,event=pr
167191
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-
178192
- name: Build and push wasi-sdk docker image
179193
uses: docker/build-push-action@v3
180194
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)