Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,18 @@ jobs:
- target: x86_64-unknown-linux-musl
os: ubuntu-24.04
attr: nativelink-x86_64-linux
# The runner shares this target's architecture, so the packaged
# binary can be executed to prove it actually starts.
smoke_test: true
- target: aarch64-unknown-linux-musl
os: ubuntu-24.04
attr: nativelink-aarch64-linux
# Cross-built from x86_64, so the runner cannot execute it.
smoke_test: false
- target: aarch64-apple-darwin
os: macos-26
attr: nativelink-aarch64-darwin
smoke_test: true
runs-on: ${{ matrix.os }}
timeout-minutes: 90
env:
Expand Down Expand Up @@ -125,6 +131,38 @@ jobs:
sha256 "${ASSET}.tar.gz" | tee "${ASSET}.tar.gz.sha256"
echo "asset=${ASSET}" >> "${GITHUB_OUTPUT}"
# The v1.6.6 macOS tarball shipped with an absolute /nix/store install
# name for libiconv and could not launch on any machine but the builder
# (#2727). The Nix build now fails on a leftover store reference, and
# this re-checks the packaged tarball so a regression cannot reach a
# published release unnoticed.
- name: Smoke test packaged binary
if: matrix.smoke_test
run: |
set -euo pipefail
ASSET="${{ steps.package.outputs.asset }}"
CHECK="$(mktemp -d)"
tar -C "${CHECK}" -xzf "${ASSET}.tar.gz"
if [[ "${{ runner.os }}" == "macOS" ]]; then
otool -L "${CHECK}/nativelink"
if otool -L "${CHECK}/nativelink" | tail -n +2 | grep '/nix/store'; then
echo "::error::Packaged macOS binary links against /nix/store paths that do not exist on user machines."
exit 1
fi
codesign --verify --verbose "${CHECK}/nativelink"
fi
# nativelink exits non-zero with a usage message when handed no
# config file. Reaching that message proves dyld loaded the binary
# and it ran, which is all this check needs to establish.
output="$("${CHECK}/nativelink" 2>&1 || true)"
echo "${output}"
if ! echo "${output}" | grep -q 'Usage: nativelink'; then
echo "::error::Packaged binary for ${{ matrix.target }} did not start."
exit 1
fi
- name: Generate SBOM
uses: >- # v0.24.0
anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610
Expand Down
57 changes: 56 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,69 @@
cargoArtifactsFor = p: (craneLibFor p).buildDepsOnly (commonArgsFor p);
nightlyCargoArtifactsFor = p: (nightlyCraneLibFor p).buildDepsOnly (commonArgsFor p);

# Darwin binaries link against Nix's `libiconv`, which records an
# absolute `/nix/store/...` install name. That path only exists on the
# machine that built the binary, so the published macOS tarball failed
# to launch anywhere else with a dyld "Library not loaded" error.
#
# Nix's darwin `libiconv` is Apple's own libiconv and exports the same
# symbols as the copy macOS keeps in the dyld shared cache, so pointing
# the install name at `/usr/lib` is ABI-safe and leaves the binary with
# no Nix store references at all.
#
# See https://github.com/TraceMachina/nativelink/issues/2727.
darwinSystemDylibArgs = p: {
# `install_name_tool` invalidates the ad-hoc signature that arm64
# macOS requires. This hook re-signs during fixup, i.e. after the
# `preFixup` rewrite below.
nativeBuildInputs =
(commonArgsFor p).nativeBuildInputs
++ [p.darwin.autoSignDarwinBinariesHook];

preFixup = ''
for binary in "$out"/bin/*; do
[ -f "$binary" ] || continue

# Skip anything that isn't a Mach-O image, such as wrapper scripts.
linkage="$(otool -L "$binary" 2>/dev/null)" || continue

printf '%s\n' "$linkage" | tail -n +2 | awk '{print $1}' \
| while read -r dylib; do
case "$dylib" in
/nix/store/*/lib/libiconv*.dylib | /nix/store/*/lib/libcharset*.dylib)
echo "relocating $dylib -> /usr/lib/''${dylib##*/} in $binary"
install_name_tool \
-change "$dylib" "/usr/lib/''${dylib##*/}" "$binary"
;;
esac
done

remaining="$(
otool -L "$binary" | tail -n +2 | awk '{print $1}' \
| grep '^/nix/store' || true
)"
if [ -n "$remaining" ]; then
echo "error: $binary still links against Nix store libraries:" >&2
echo "$remaining" >&2
echo "Those paths do not exist on machines without Nix, so the" >&2
echo "released binary would fail to launch. Either relocate the" >&2
echo "library to its /usr/lib equivalent above, or link it" >&2
echo "statically." >&2
exit 1
fi
done
'';
};

nativelinkFor = p:
(craneLibFor p).buildPackage ((commonArgsFor p)
// {
cargoArtifacts = cargoArtifactsFor p;
# If you're testing Nativelink locally, doing a dev profile will
# massively speedup build times. Just don't commit/push anything build with dev!
# CARGO_PROFILE = "dev";
});
}
// pkgs.lib.optionalAttrs p.stdenv.targetPlatform.isDarwin (darwinSystemDylibArgs p));

nativeTargetPkgs =
if pkgs.stdenv.hostPlatform.system == "x86_64-linux"
Expand Down
Loading