From 0a956961c6d4e05938485962fa4a63138ebe86e1 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Thu, 10 Sep 2026 11:42:26 +0200 Subject: [PATCH] ci: split the macOS jobs, and test only the host on the intel runner The macos-15-intel job was the critical path of CI at 1h21m44s. It ran gen-device, make test, make release, make tinygo-test, and make smoketest-quick one after the other. Split the work into four jobs, the same pattern as windows.yml. build-macos publishes the release tarball, stdlib-test-macos and smoke-test-macos download it, and test-macos restores the LLVM cache and runs at the same time as build-macos. The LLVM steps move to a new setup-llvm-macos composite action, with a save-cache input so that only build-macos writes the cache. Most of smoketest-quick is cross compilation, which gives the same result on every host. Add a smoketest-host target with the host dependent part plus a Cortex-M and a wasm canary, and use it on the intel runner. The arm64 runner keeps the full set. The intel runner still tests darwin/amd64 with make test, make tinygo-test, and smoketest-host. --- .github/actions/setup-llvm-macos/action.yml | 65 ++++++++ .github/workflows/build-macos.yml | 162 +++++++++++++------- make/smoketest.mk | 34 ++-- 3 files changed, 199 insertions(+), 62 deletions(-) create mode 100644 .github/actions/setup-llvm-macos/action.yml diff --git a/.github/actions/setup-llvm-macos/action.yml b/.github/actions/setup-llvm-macos/action.yml new file mode 100644 index 000000000..e92df6f1b --- /dev/null +++ b/.github/actions/setup-llvm-macos/action.yml @@ -0,0 +1,65 @@ +name: Set up LLVM on macOS +description: Restore or build LLVM + +inputs: + os: + description: Runner image name, used in the cache key + required: true + save-cache: + description: Save LLVM caches after a cache miss + required: false + default: "true" + +runs: + using: composite + steps: + - name: Restore LLVM source cache + uses: actions/cache/restore@v5 + id: cache-llvm-source + with: + key: llvm-source-22-${{ inputs.os }}-v1-${{ hashFiles('llvm-version.txt') }} + path: | + llvm-project/clang/lib/Headers + llvm-project/clang/include + llvm-project/compiler-rt + llvm-project/lld/include + llvm-project/llvm/include + - name: Download LLVM source + if: steps.cache-llvm-source.outputs.cache-hit != 'true' + shell: bash + run: make llvm-source + - name: Save LLVM source cache + uses: actions/cache/save@v5 + if: inputs.save-cache == 'true' && steps.cache-llvm-source.outputs.cache-hit != 'true' + with: + key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} + path: | + llvm-project/clang/lib/Headers + llvm-project/clang/include + llvm-project/compiler-rt + llvm-project/lld/include + llvm-project/llvm/include + - name: Restore LLVM build cache + uses: actions/cache/restore@v5 + id: cache-llvm-build + with: + key: llvm-build-22-${{ inputs.os }}-v1-${{ hashFiles('llvm-version.txt') }} + path: llvm-build + - name: Build LLVM + if: steps.cache-llvm-build.outputs.cache-hit != 'true' + shell: bash + run: | + # fetch LLVM source + rm -rf llvm-project + make llvm-source + # install dependencies + HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja + # build! + make llvm-build + find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; + - name: Save LLVM build cache + uses: actions/cache/save@v5 + if: inputs.save-cache == 'true' && steps.cache-llvm-build.outputs.cache-hit != 'true' + with: + key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} + path: llvm-build diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index b90f3a73e..08149c970 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -13,6 +13,8 @@ concurrency: jobs: build-macos: + # Build the release tarball. The jobs below download it, so that they do not + # have to build the compiler again. name: build-macos strategy: matrix: @@ -42,62 +44,13 @@ jobs: with: go-version: '1.27.1' cache: true - - name: Restore LLVM source cache - uses: actions/cache/restore@v5 - id: cache-llvm-source + - uses: ./.github/actions/setup-llvm-macos with: - key: llvm-source-22-${{ matrix.os }}-v1-${{ hashFiles('llvm-version.txt') }} - path: | - llvm-project/clang/lib/Headers - llvm-project/clang/include - llvm-project/compiler-rt - llvm-project/lld/include - llvm-project/llvm/include - - name: Download LLVM source - if: steps.cache-llvm-source.outputs.cache-hit != 'true' - run: make llvm-source - - name: Save LLVM source cache - uses: actions/cache/save@v5 - if: steps.cache-llvm-source.outputs.cache-hit != 'true' - with: - key: ${{ steps.cache-llvm-source.outputs.cache-primary-key }} - path: | - llvm-project/clang/lib/Headers - llvm-project/clang/include - llvm-project/compiler-rt - llvm-project/lld/include - llvm-project/llvm/include - - name: Restore LLVM build cache - uses: actions/cache/restore@v5 - id: cache-llvm-build - with: - key: llvm-build-22-${{ matrix.os }}-v1-${{ hashFiles('llvm-version.txt') }} - path: llvm-build - - name: Build LLVM - if: steps.cache-llvm-build.outputs.cache-hit != 'true' - run: | - # fetch LLVM source - rm -rf llvm-project - make llvm-source - # install dependencies - HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja - # build! - make llvm-build - find llvm-build -name CMakeFiles -prune -exec rm -r '{}' \; - - name: Save LLVM build cache - uses: actions/cache/save@v5 - if: steps.cache-llvm-build.outputs.cache-hit != 'true' - with: - key: ${{ steps.cache-llvm-build.outputs.cache-primary-key }} - path: llvm-build + os: ${{ matrix.os }} - name: make gen-device run: make -j3 gen-device - - name: Test TinyGo - run: make test GOTESTFLAGS="-only-current-os" - name: Build TinyGo release tarball run: make release -j3 - - name: Test stdlib packages - run: make tinygo-test - name: Make release artifact run: cp -p build/release.tar.gz build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz - name: Publish release artifact @@ -106,8 +59,113 @@ jobs: name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }} path: build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz archive: false + + test-macos: + # The compiler test suite. It needs LLVM, thus it cannot use the release + # tarball. It restores the LLVM cache and runs at the same time as + # build-macos. Only build-macos saves that cache. + name: test-macos + strategy: + matrix: + os: [macos-14, macos-15-intel] + runs-on: ${{ matrix.os }} + steps: + - name: Install Dependencies + run: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install qemu binaryen + - name: Checkout + uses: actions/checkout@v6 + with: + submodules: true + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: '1.27.1' + cache: true + - uses: ./.github/actions/setup-llvm-macos + with: + os: ${{ matrix.os }} + save-cache: "false" + - name: make gen-device + run: make -j3 gen-device + - name: Test TinyGo + run: make test GOTESTFLAGS="-only-current-os" + + stdlib-test-macos: + # Test the standard library, compiled and run on the host itself. + name: stdlib-test-macos + needs: build-macos + strategy: + matrix: + os: [macos-14, macos-15-intel] + include: + - os: macos-14 + goarch: arm64 + - os: macos-15-intel + goarch: amd64 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Extract TinyGo version + id: version + run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT" + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: '1.27.1' + cache: true + - name: Download TinyGo build + uses: actions/download-artifact@v8 + with: + name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }} + path: build/ + - name: Unpack TinyGo build + run: tar -xzf build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz -C build + - name: Test stdlib packages + run: make tinygo-test TINYGO=$(PWD)/build/tinygo/bin/tinygo + + smoke-test-macos: + # Cross compilation gives the same result on every host. Thus only one macOS + # runner does the full set, and the other one does the host dependent part + # plus a canary. See the smoketest-linux comment in linux.yml. + name: smoke-test-macos + needs: build-macos + strategy: + matrix: + os: [macos-14, macos-15-intel] + include: + - os: macos-14 + goarch: arm64 + smoketest: smoketest-quick + - os: macos-15-intel + goarch: amd64 + smoketest: smoketest-host + runs-on: ${{ matrix.os }} + steps: + - name: Install Dependencies + run: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install binaryen + - name: Checkout + uses: actions/checkout@v6 + - name: Extract TinyGo version + id: version + run: ./.github/workflows/tinygo-extract-version.sh | tee -a "$GITHUB_OUTPUT" + - name: Install Go + uses: actions/setup-go@v6 + with: + go-version: '1.27.1' + cache: true + - name: Download TinyGo build + uses: actions/download-artifact@v8 + with: + name: darwin-${{ matrix.goarch }}-double-zipped-${{ steps.version.outputs.version }} + path: build/ + - name: Unpack TinyGo build + run: tar -xzf build/tinygo${{ steps.version.outputs.version }}.darwin-${{ matrix.goarch }}.tar.gz -C build - name: Smoke tests - run: make smoketest-quick TINYGO=$(PWD)/build/tinygo + run: make ${{ matrix.smoketest }} TINYGO=$(PWD)/build/tinygo/bin/tinygo + test-macos-homebrew: name: homebrew-install runs-on: macos-latest diff --git a/make/smoketest.mk b/make/smoketest.mk index 9b540da94..0dadfb84b 100644 --- a/make/smoketest.mk +++ b/make/smoketest.mk @@ -606,16 +606,35 @@ ifneq ($(OS),Windows_NT) $(TINYGO) build -o $(SMOKE_OUT).elf -gc=leaking -scheduler=none examples/serial endif +# The host dependent part of the quick smoke test, plus a canary that shows cross +# compilation works from this host. Cross compilation gives the same result on +# every host, thus a second runner of the same OS only needs this part. +.PHONY: smoketest-host +smoketest-host: SMOKE_OUT = build/smoke/host +smoketest-host: testchdir | build/smoke + $(TINYGO) version + $(TINYGO) targets > /dev/null + # regression test for #2892 + cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log) +ifneq ($(OS),Windows_NT) + # TODO: this does not yet work on Windows. Somehow, unused functions are + # not garbage collected. + $(TINYGO) build -o $(SMOKE_OUT).elf -gc=leaking -scheduler=none examples/serial +endif + # canary, nrf52 Cortex-M4 + $(TINYGO) build -size short -o $(SMOKE_OUT).hex -target=pca10040 examples/blinky1 + @$(MD5SUM) $(SMOKE_OUT).hex +ifneq ($(WASM), 0) + # canary, wasm + $(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm examples/wasm/main +endif + # A representative board for each processor architecture. This answers the # question "can TinyGo build a binary for each architecture" at a fraction of # the cost of the full smoke test, which runs separately on Linux. .PHONY: smoketest-quick smoketest-quick: SMOKE_OUT = build/smoke/quick -smoketest-quick: testchdir | build/smoke - $(TINYGO) version - $(TINYGO) targets > /dev/null - # regression test for #2892 - cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log) +smoketest-quick: smoketest-host | build/smoke # nrf51, Cortex-M0 $(TINYGO) build -size short -o $(SMOKE_OUT).hex -target=microbit examples/microbit-blink @$(MD5SUM) $(SMOKE_OUT).hex @@ -692,8 +711,3 @@ ifneq ($(WASM), 0) # wasm without a host, so without any imports $(TINYGO) build -size short -o $(SMOKE_OUT).wasm -target=wasm-unknown examples/hello-wasm-unknown endif -ifneq ($(OS),Windows_NT) - # TODO: this does not yet work on Windows. Somehow, unused functions are - # not garbage collected. - $(TINYGO) build -o $(SMOKE_OUT).elf -gc=leaking -scheduler=none examples/serial -endif