From 56780c26917647900bda67018a22c962bce2333a Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 1 May 2022 23:07:07 +0200 Subject: [PATCH] ci: build Linux binary in Alpine container This makes it easier to move the TinyGo compiler between Linux versions because it doesn't depend on any system libraries anymore. For example, binaries should be able to run on old Linux versions and on distributions without glibc (such as Alpine Linux). --- .github/workflows/linux.yml | 42 +++++++++++++++++++++++-------------- Makefile | 21 +++++++++++++++++-- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index cb800d1a..d910d7bb 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -14,19 +14,25 @@ concurrency: jobs: build-linux: # Build Linux binaries, ready for release. - # This intentionally uses an older Linux image, so that we compile against - # an older glibc version and therefore are compatible with a wide range of - # Linux distributions. - runs-on: ubuntu-18.04 + # This runs inside an Alpine Linux container so we can more easily create a + # statically linked binary. + runs-on: ubuntu-latest + container: + image: alpine:3.16 steps: + - name: Install apk dependencies + # tar: needed for actions/cache@v2 + # git+openssh: needed for checkout (I think?) + # gcompat: needed for go binary + # ruby: needed to install fpm + run: apk add tar git openssh gcompat make g++ ruby + - name: Work around CVE-2022-24765 + # We're not on a multi-user machine, so this is safe. + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Checkout uses: actions/checkout@v2 with: submodules: true - - name: Install apt dependencies - run: | - sudo apt-get install --no-install-recommends \ - ninja-build - name: Install Go uses: actions/setup-go@v2 with: @@ -34,7 +40,7 @@ jobs: - name: Cache Go uses: actions/cache@v2 with: - key: go-cache-linux-v1-${{ hashFiles('go.mod') }} + key: go-cache-linux-alpine-v1-${{ hashFiles('go.mod') }} path: | ~/.cache/go-build ~/go/pkg/mod @@ -42,7 +48,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-source with: - key: llvm-source-14-linux-v2 + key: llvm-source-14-linux-alpine-v1 path: | llvm-project/clang/lib/Headers llvm-project/clang/include @@ -56,7 +62,7 @@ jobs: uses: actions/cache@v2 id: cache-llvm-build with: - key: llvm-build-14-linux-v1 + key: llvm-build-14-linux-alpine-v1 path: llvm-build - name: Build LLVM if: steps.cache-llvm-build.outputs.cache-hit != 'true' @@ -64,6 +70,8 @@ jobs: # fetch LLVM source rm -rf llvm-project make llvm-source + # install dependencies + apk add cmake samurai python3 # build! make llvm-build # Remove unnecessary object files (to reduce cache size). @@ -72,26 +80,28 @@ jobs: uses: actions/cache@v2 id: cache-binaryen with: - key: binaryen-linux-v1 + key: binaryen-linux-alpine-v1 path: build/wasm-opt - name: Build Binaryen if: steps.cache-binaryen.outputs.cache-hit != 'true' - run: make binaryen + run: | + apk add cmake samurai python3 + make binaryen STATIC=1 - name: Cache wasi-libc uses: actions/cache@v2 id: cache-wasi-libc with: - key: wasi-libc-sysroot-linux-asserts-v5 + key: wasi-libc-sysroot-linux-alpine-v1 path: lib/wasi-libc/sysroot - name: Build wasi-libc if: steps.cache-wasi-libc.outputs.cache-hit != 'true' run: make wasi-libc - name: Install fpm run: | - sudo gem install --no-document fpm + gem install --no-document fpm - name: Build TinyGo release run: | - make release deb -j3 + make release deb -j3 STATIC=1 cp -p build/release.tar.gz /tmp/tinygo.linux-amd64.tar.gz cp -p build/release.deb /tmp/tinygo_amd64.deb - name: Publish release artifact diff --git a/Makefile b/Makefile index 4c11e99a..88e12672 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,23 @@ else LLVM_OPTION += '-DLLVM_ENABLE_ASSERTIONS=OFF' endif +ifeq (1, $(STATIC)) + # Build TinyGo as a fully statically linked binary (no dynamically loaded + # libraries such as a libc). This is not supported with glibc which is used + # on most major Linux distributions. However, it is supported in Alpine + # Linux with musl. + CGO_LDFLAGS += -static + # Also set the thread stack size to 1MB. This is necessary on musl as the + # default stack size is 128kB and LLVM uses more than that. + # For more information, see: + # https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size + CGO_LDFLAGS += -Wl,-z,stack-size=1048576 + # Build wasm-opt with static linking. + # For details, see: + # https://github.com/WebAssembly/binaryen/blob/version_102/.github/workflows/ci.yml#L181 + BINARYEN_OPTION += -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static" +endif + # Cross compiling support. ifneq ($(CROSS),) CC = $(CROSS)-gcc @@ -246,9 +263,9 @@ lib/wasi-libc/sysroot/lib/wasm32-wasi/libc.a: # Build the Go compiler. tinygo: @if [ ! -f "$(LLVM_BUILDDIR)/bin/llvm-config" ]; then echo "Fetch and build LLVM first by running:"; echo " make llvm-source"; echo " make $(LLVM_BUILDDIR)"; exit 1; fi - CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags byollvm -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" . + CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GOENVFLAGS) $(GO) build -buildmode exe -o build/tinygo$(EXE) -tags "byollvm osusergo" -ldflags="-X github.com/tinygo-org/tinygo/goenv.GitSha1=`git rev-parse --short HEAD`" . test: wasi-libc - CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags byollvm ./builder ./cgo ./compileopts ./compiler ./interp ./transform . + CGO_CPPFLAGS="$(CGO_CPPFLAGS)" CGO_CXXFLAGS="$(CGO_CXXFLAGS)" CGO_LDFLAGS="$(CGO_LDFLAGS)" $(GO) test $(GOTESTFLAGS) -timeout=20m -buildmode exe -tags "byollvm osusergo" ./builder ./cgo ./compileopts ./compiler ./interp ./transform . # Standard library packages that pass tests on darwin, linux, wasi, and windows, but take over a minute in wasi TEST_PACKAGES_SLOW = \