Skip to content

Commit

Permalink
.github, config: use Zig to cross-compile arm64 Linux asset (exercism…
Browse files Browse the repository at this point in the history
…#460)

Add a script to install Zig, and add a job to the build workflow that
uses `zig cc` to cross compile configlet for arm64 (AKA aarch64) Linux.

This means that the next configlet release will have two new release
assets:

    configlet_4.0.0-beta.14_linux_arm64.tar.gz
    configlet_4.0.0-beta.14_linux_arm64.tar.gz.minisig

and extracting the archive will yield the executable:

    $ file ./configlet
    ./configlet: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped

Also support running `nimble build -d:zig` locally.

For now, the new executable has a large comment section:

    $ readelf -p .comment ./configlet

    String dump of section '.comment':
      [     0]  clang version 16.0.6 (https://github.com/ziglang/zig-bootstrap 1dda86241204c4649f668d46b6a37feed707c7b4)
      [...] (repeat for 39 KiB)
      [  8d82]  clang version 16.0.6 (https://github.com/ziglang/zig-bootstrap 1dda86241204c4649f668d46b6a37feed707c7b4)
      [  8deb]  Linker: LLD 16.0.6

Try to strip it later. Our `strip` doesn't support elf64-aarch64 as a
target, and `zig objcopy` doesn't have a `-R, --remove-section` option
currently. But prepare to use `llvm-strip`.

Cross-compiling for arm64 macos currently produces an error:

    CC: ../nimdir/lib/std/sysrand.nim
    /home/runner/.cache/nim/configlet_r/@m..@snimdir@slib@sstd@ssysrand.nim.c:9:10:
    fatal error: 'Security/SecRandom.h' file not found

which is due to a recent commit [1].

See some posts on cross-compiling with Zig [2][3][4] and the support table [5].

Refs: #24
Refs: exercism#122
Refs: exercism#764

[1] 53a75a2 ("nimble, uuid: generate UUIDs via std/sysrand, not pragmagic/uuids", 2023-08-07)
[2] https://ziglang.org/learn/overview/#cross-compiling-is-a-first-class-use-case
[3] https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html
[4] https://www.uber.com/blog/bootstrapping-ubers-infrastructure-on-arm64-with-zig/
[5] https://ziglang.org/download/0.11.0/release-notes.html#Support-Table
  • Loading branch information
ee7 committed Aug 16, 2023
1 parent 6123274 commit 0e8d665
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 8 deletions.
45 changes: 45 additions & 0 deletions .github/bin/cross-compile
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -eo pipefail

artifacts_dir='artifacts'
build_tag="${GITHUB_REF_NAME}"

cross_compile() {
local target="$1"
local arch='arm64'
local os
os="$(cut -d'-' -f2 <<< "${target}")"
local nim_os
case "${os}" in
macos) nim_os='macosx' ;;
*) nim_os="${os}" ;;
esac

nimble --verbose build --cpu:"${arch}" --os:"${nim_os}" -d:release -d:zig -d:target:"${target}"
local binary_name='configlet'
if command -v llvm-strip &> /dev/null; then
echo "stripping large comment section from executable..." >&2
llvm-strip -R .comment "${binary_name}"
fi
mkdir -p "${artifacts_dir}"
local artifact_file="${artifacts_dir}/${binary_name}_${build_tag}_${os}_${arch}.tar.gz"
tar -cvzf "${artifact_file}" "${binary_name}"
}

main() {
nimble --accept install --depsOnly

local targets=(
aarch64-linux-musl
# aarch64-macos-none
# aarch64-windows-gnu
)

for target in "${targets[@]}"; do
cross_compile "${target}"
done

gh release upload "${build_tag}" "${artifacts_dir}"/*
}

main
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#!/usr/bin/env sh

# Install musl
sudo apt-get install musl-dev musl-tools
45 changes: 45 additions & 0 deletions .github/bin/linux-install-zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -eo pipefail

version='0.11.0' # 2023-08-04
release_name="zig-linux-x86_64-${version}"
archive="${release_name}.tar.xz"
url="https://ziglang.org/download/${version}/${archive}"

curlopts=(
--silent
--show-error
--fail
--location
--retry 3
)

# Download the release archive.
echo "Downloading Zig release archive..." >&2
curl "${curlopts[@]}" --output "${archive}" "${url}"

# Check that the archive has the expected hash.
echo "Verifying archive..." >&2
archive_sha256='2d00e789fec4f71790a6e7bf83ff91d564943c5ee843c5fd966efc474b423047'
echo "${archive_sha256} ${archive}" | sha256sum -c -

# Extract the archive, then remove it.
echo "Extracting archive..." >&2
tar xJf "${archive}"
rm "${archive}"

# Add zig directory to `GITHUB_PATH`.
zig_dir="$(pwd)/${release_name}"
echo "${zig_dir}" >> "${GITHUB_PATH}"

# Install `zigcc`, which is just a wrapper for `zig cc`. We need this because
# the value of e.g. `--clang.exe` cannot contain a space (Nim requires the value
# to be an executable, not a command).
zigcc_path="${zig_dir}/zigcc"
printf '#!/usr/bin/env sh\nzig cc $@\n' > "${zigcc_path}"
chmod +x "${zigcc_path}"

# Print the versions of Zig and Clang.
"${zig_dir}"/zig version
"${zig_dir}"/zig cc --version
echo "Successfully installed Zig ${version}."
29 changes: 27 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

- name: On Linux, install musl
if: matrix.os == 'linux'
run: ./.github/bin/linux-install-build-tools
run: ./.github/bin/linux-install-musl

- name: Install Nim
uses: iffy/install-nim@ac410af52523f06e0fa037ee81d06ead7b95692c
Expand All @@ -62,9 +62,34 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

checksums:
cross-compile:
needs: [build]
runs-on: ubuntu-22.04
name: cross-compile
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9

- name: Install Nim
uses: iffy/install-nim@ac410af52523f06e0fa037ee81d06ead7b95692c
with:
version: "binary:2.0.0"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Install Zig
run: ./.github/bin/linux-install-zig

- name: Cross-compile
run: ./.github/bin/cross-compile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

checksums:
needs: [cross-compile]
runs-on: ubuntu-22.04
name: Upload signatures and checksums
permissions:
contents: write
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:

- name: On Linux, install musl
if: matrix.os == 'linux'
run: ./.github/bin/linux-install-build-tools
run: ./.github/bin/linux-install-musl

- name: Install Nim
uses: iffy/install-nim@ac410af52523f06e0fa037ee81d06ead7b95692c
Expand Down
20 changes: 17 additions & 3 deletions config.nims
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,30 @@ switch("mm", "refc")
patchFile("stdlib", "json", "src/patched_stdlib/json")
patchFile("stdlib", "parsejson", "src/patched_stdlib/parsejson")

if defined(zig) and findExe("zigcc").len > 0:
switch("cc", "clang")
# We can't write `zig cc` below, because the value cannot contain a space.
switch("clang.exe", "zigcc")
switch("clang.linkerexe", "zigcc")
const target {.strdefine.} = ""
if target.len > 0:
switch("passC", "-target " & target)
switch("passL", "-target " & target)

if defined(release):
switch("opt", "size")
switch("passC", "-flto")
switch("passL", "-flto")

if not (defined(zig) and defined(macosx)):
# `zig ld` doesn't support LTO.
# See https://github.com/ziglang/zig/issues/8680
switch("passC", "-flto")
switch("passL", "-flto")

if defined(linux) or defined(windows):
switch("passL", "-s")
switch("passL", "-static")

if defined(linux):
if defined(linux) and not defined(zig):
if defined(gcc):
switch("gcc.exe", "musl-gcc")
switch("gcc.linkerexe", "musl-gcc")
Expand Down

0 comments on commit 0e8d665

Please sign in to comment.