diff --git a/Dockerfile b/Dockerfile index cb3fb77..c92403c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,19 @@ +# --- +# Special thanks to luislavena for this approach to cross-compiling Crystal apps with Zig. +# Source: https://forum.crystal-lang.org/t/cross-compiling-crystal-applications-part-1/6956 +# Repo: https://github.com/luislavena/crystal-xbuild-container +# Background Crystal Lang Docs on: +# - Cross-Compilation: https://crystal-lang.org/reference/1.13/syntax_and_semantics/cross-compilation.html +# - Static Linking: https://crystal-lang.org/reference/1.13/guides/static_linking.html +# +# NOTE: +# We've modified the original approach here as necessary for coverage-reporter: +# - Installed additional dependencies for our target architectures +# - Made a few tweaks to address recurring issues with our target architectures +# - Populated our Makefile with convenience targets for our use case +# - Leveraging those targets to extended this approach to our CI/CD for releases +# --- + # Base image from luislavena's hydrofoil-crystal image FROM ghcr.io/luislavena/hydrofoil-crystal:1.13 AS base @@ -46,6 +62,10 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ # --- # Alpine Linux libraries for multi-arch cross-compilation +# +# NOTE: +# We are cross-compiling for `x86_64` and `aarch64` at this time. +# --- # Set the library path to include both /lib and /opt/multiarch-libs ENV LIBRARY_PATH="/lib:/opt/multiarch-libs/aarch64-linux-musl/lib:/opt/multiarch-libs/x86_64-linux-musl/lib" @@ -104,10 +124,10 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ cp $target_path/lib/libz.a /opt/multiarch-libs/x86_64-linux-musl/lib/; \ fi; \ # Verify the installed libz.a for each $target_arch - echo "DEBUG: Checking installed libz.a for $target_arch"; \ + # echo "DEBUG: Checking installed libz.a for $target_arch"; \ ls -al /tmp/$target_arch-apk-chroot/lib/libz.a; \ # Debug: List the contents of $target_path/usr/lib/ - echo "DEBUG: Listing contents of $target_path/usr/lib/"; \ + # echo "DEBUG: Listing contents of $target_path/usr/lib/"; \ pkg_path="/opt/multiarch-libs/$target_arch-linux-musl"; \ ls -al $target_path/usr/lib/; \ mkdir -p $pkg_path/lib/pkgconfig; \ @@ -115,16 +135,26 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ cp $target_path/usr/lib/*.a $pkg_path/lib/; \ cp $target_path/usr/lib/pkgconfig/*.pc $pkg_path/lib/pkgconfig/; \ # Debug: List the contents of /opt/multiarch-libs/$target_arch-linux-musl/ - echo "DEBUG: Installed libraries for $target_arch"; \ - echo "DEBUG: Listing contents of $pkg_path"; \ + # echo "DEBUG: Installed libraries for $target_arch"; \ + # echo "DEBUG: Listing contents of $pkg_path"; \ ls -al $pkg_path/lib/; \ done; \ } # --- -# macOS +# MacOS +# +# NOTE: +# We are not using this script to cross-compile for MacOS at this time +# (you'll notice we have no accompanying target in our Makefile for a MacOS build). +# Instead, we're continuing to leverage our Homebrew formula ("bottle") here: https://github.com/coverallsapp/homebrew-coveralls +# (which our `build.yml` workflow updates using the `homebrew-bump-formula` GitHub Action). +# +# That said, we're continuing to build the MacOS dependencies here in case we decide to +# switch to this method for cross-compiling MacOS binaries in the future. +# --- -# macOS dependencies are installed in separate target +# MacOS dependencies are installed in separate target FROM base AS macos-packages COPY ./scripts/homebrew-downloader.cr /homebrew-downloader.cr @@ -150,11 +180,11 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ ; \ } -# copy macOS dependencies back into `base` +# Copy macOS dependencies back into `base` FROM base COPY --from=macos-packages /opt/multiarch-libs/aarch64-apple-darwin /opt/multiarch-libs/aarch64-apple-darwin -# install macOS SDK +# Install macOS SDK RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ --mount=type=tmpfs,target=/tmp \ set -eux -o pipefail; \ @@ -173,5 +203,5 @@ RUN --mount=type=cache,sharing=private,target=/var/cache/apk \ ln -nfs /opt/multiarch-libs/MacOSX${MACOS_SDK_VERSION}.sdk /opt/multiarch-libs/MacOSX${MACOS_SDK_MAJOR_VERSION}.sdk; \ } -# copy xbuild helper +# Copy xbuild helper COPY ./scripts/xbuild.sh /usr/local/bin/xbuild diff --git a/Makefile b/Makefile index 10ebc9c..1364da7 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,18 @@ BINARY_X86_64 := build/x86_64-linux-musl/coveralls-linux-x86_64 BINARY_LINUX := build/x86_64-linux-musl/coveralls-linux DIST_DIR := dist -# Developers, if you intend to run these targets on on macOS, -# install gnu-tar with `brew install gnu-tar`as it supports the --transform option + +# DEV NOTE: +# If you intend to run these targets on on MacOS, you'll want to install `gnu-tar` with `brew install gnu-tar`, +# since it supports the `--transform` option we use with `tar` below in our `package` target. +# Otherwise, on MacOS systems, this script will use `gtar` instead of `tar` and, if not installed, +# you can expect it to fail with a related error. TAR := $(shell if [ $(shell uname) = Darwin ]; then echo gtar; else echo tar; fi) -# Targets for ci.yml (build and test app) +# --- +# Targets for `ci.yml` (standard CI: build & test app) +# --- + .PHONY: build build: shards build coveralls --progress --error-trace @@ -29,7 +36,10 @@ test: lint: bin/ameba -# Targets for build.yml (cross-compile linux binaries for release) +# --- +# Targets for `build.yml` (build binaries for releases: `build-linux` job) +# --- + .PHONY: build-xbuild-container build-xbuild-container: $(DOCKERFILE) docker build -t ${IMAGE_NAME}:${VERSION} -f ${DOCKERFILE} . @@ -42,6 +52,11 @@ run-xbuild-container: $(DOCKERFILE) compile-x86_64: docker run --rm -v $(shell pwd):/app -w /app ${IMAGE_NAME}:${VERSION} xbuild src/cli.cr coveralls-linux-x86_64 x86_64-linux-musl +# NOTE: +# There is a known, unavoidable warning that will appear in STDOUT when cross-compiling for `aarch64`. +# It's due to the version of `clang` used by `zig`. The warning is harmless and can be ignored. +# We're supposed to be able to suppress the warning with the `-Wno-deprecated-non-prototype` flag, +# but unfortunately, it doesn't work as expected. ZigLang tracking issue here: https://github.com/ziglang/zig/issues/13385 .PHONY: compile-aarch64 compile-aarch64: docker run --rm -v $(shell pwd):/app -w /app ${IMAGE_NAME}:${VERSION} xbuild src/cli.cr coveralls-linux-aarch64 aarch64-linux-musl @@ -74,7 +89,10 @@ package: $(DIST_DIR) $(TAR) -czf $(DIST_DIR)/coveralls-$$arch.tar.gz -C $(DIST_DIR) --transform="s/coveralls-$$arch/coveralls/" coveralls-$$arch; \ done +# --- # Test containers for different architectures +# --- + # Ubuntu 22.04 (amd64) .PHONY: ubuntu-amd64 ubuntu-amd64: @@ -85,6 +103,10 @@ ubuntu-amd64: ubuntu-aarch64: docker run -it --rm -u $(UUID):$(GUID) --platform linux/aarch64 -v .:/app -w /app ubuntu:22.04 bash -i +# --- +# Used for releasing new versions +# --- + # Creates and pushes new tag with annotation for new release .ONESHELL: new_version: diff --git a/scripts/xbuild.sh b/scripts/xbuild.sh index 642da00..52e3fa5 100755 --- a/scripts/xbuild.sh +++ b/scripts/xbuild.sh @@ -111,6 +111,10 @@ esac echo "Linking with: $libs" # link the object_file with the supplied libraries +# NOTE: +# There is an unavoidable warning that appears in STDOUT when compiling for `aarch64`. +# The warning is harmless and can be ignored. +# See our comment above the `compile-aarch64` target in the Makefile for more info. link_output=$(zig cc -target "$link_platform" -Wno-deprecated-non-prototype "$object_file" -o "$executable_name" $link_paths $libs) if [ $? -ne 0 ]; then