Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build aarch64-darwin binaries #1182

Merged
merged 15 commits into from
Dec 1, 2023
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# This workflow builds a static executable for the hydra-node and its tools
name: Static executable
# Build executables that can be used on any Linux with 64bit CPUs.
#
# Produces static ELF binary using MuslC which includes all needed libraries
# statically linked into it.
name: Linux x86_64

on:
push:
Expand All @@ -10,11 +13,14 @@ on:

jobs:
build-executables:
name: "Build static executables"
name: "Build x86_64-linux static executables"
runs-on: ubuntu-latest
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
with:
# Also ensure we have all history with all tags
fetch-depth: 0

- name: ❄ Prepare nix
uses: cachix/install-nix-action@v23
Expand All @@ -31,13 +37,16 @@ jobs:

- name: ❄ Build static executables
run: |
mkdir -p bin/
nix build .#hydra-node-static && cp result/bin/* bin/
nix build .#hydra-tui-static && cp result/bin/* bin/
nix build .#release-static

# XXX: Why unzip https://github.com/actions/upload-artifact/issues/39
- name: 🤐 Unzip and prepare upload
run: |
echo "VERSION=$(git describe --always ${{ github.ref }})" >> "$GITHUB_ENV"
unzip result/*.zip -d out

- name: 💾 Upload executables
uses: actions/upload-artifact@v3
with:
name: hydra-x86_64-unknown-linux-musl
path: |
./bin
name: hydra-x86_64-linux-${{ env.VERSION }} # automatically zips
path: out/*
52 changes: 52 additions & 0 deletions .github/workflows/binaries-macos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Build executables to be used on MacOS with Apple silicon (aarch64) CPUs.
#
# Produces a dynamic Mach-O binary which is still dynamically linked against
# system dependencies, but all third-party libraries are statically linked.
name: MacOS ARM64

on:
push:
branches:
- "**"
tags:
- "*.*.*"

jobs:
build-executables:
name: "Build aarch64-darwin dynamic executables"
runs-on: [self-hosted, macOS, ARM64]
steps:
- name: 📥 Checkout repository
uses: actions/checkout@v4
with:
# Also ensure we have all history with all tags
fetch-depth: 0

- name: ❄ Prepare nix
uses: cachix/install-nix-action@v23
with:
extra_nix_config: |
accept-flake-config = true
log-lines = 1000

- name: ❄ Cachix cache of nix derivations
uses: cachix/cachix-action@v12
with:
name: cardano-scaling
authToken: '${{ secrets.CACHIX_CARDANO_SCALING_AUTH_TOKEN }}'

- name: ❄ Build executables
run: |
nix build .#release

# XXX: Why unzip https://github.com/actions/upload-artifact/issues/39
- name: 🤐 Unzip and prepare upload
run: |
echo "VERSION=$(git describe --always ${{ github.ref }})" >> "$GITHUB_ENV"
unzip result/*.zip -d out

- name: 💾 Upload executables
uses: actions/upload-artifact@v3
with:
name: hydra-aarch64-darwin-${{ env.VERSION }} # automatically zips
path: out/*
50 changes: 35 additions & 15 deletions nix/hydra/packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
let
lib = pkgs.lib;

packaging = import ../packaging.nix { inherit pkgs; };

# Creates a fixed length string by padding with given filler as suffix.
padSuffix = width: filler: str:
let
Expand All @@ -31,21 +33,27 @@ let
(lib.stringLength placeholder == lib.stringLength rev)
"Mismatching length of placeholder (${placeholder}) and rev (${rev})";

pkgs.runCommandCC "${exe}-with-revision" { } ''
set -e

echo "Patching embedded git revision in ${exe} to ${rev} ..."

# Ensure only one occurrence of placeholder
if [[ $(strings ${drv}/bin/${exe} | grep -c ${placeholder}) -ne 1 ]]; then
echo "Not exactly one occurrence of ${placeholder} in ${drv}/bin/${exe}!"
exit 1
fi

mkdir -p $out/bin
sed 's/${placeholder}/${rev}/' ${drv}/bin/${exe} > $out/bin/${exe}
chmod +x $out/bin/${exe}
'';
# Using mkDerivation instead of runCommand to make sure to use the same
# stdenv as the original drv (important to determine targetPlatform).
drv.stdenv.mkDerivation {
name = "${exe}-with-revision";
phases = [ "buildPhase" ];
buildPhase = ''
set -e

echo "Patching embedded git revision in ${exe} to ${rev} ..."

# Ensure only one occurrence of placeholder
if [[ $(grep -c -a ${placeholder} ${drv}/bin/${exe}) -ne 1 ]]; then
echo "Not exactly one occurrence of ${placeholder} in ${drv}/bin/${exe}!"
exit 1
fi

mkdir -p $out/bin
sed 's/${placeholder}/${rev}/' ${drv}/bin/${exe} > $out/bin/${exe}
chmod +x $out/bin/${exe}
'';
};

nativePkgs = hydraProject.hsPkgs;
# Allow reinstallation of terminfo as it's not installed with cross compilers.
Expand All @@ -54,6 +62,16 @@ let
musl64Pkgs = patchedForCrossProject.projectCross.musl64.hsPkgs;
in
rec {
release =
packaging.asZip
{ name = "hydra-${pkgs.hostPlatform.system}"; }
[ hydra-node hydra-tui ];

release-static =
packaging.asZip
{ name = "hydra-${pkgs.hostPlatform.system}"; }
[ hydra-node-static hydra-tui-static ];

hydra-node =
embedRevision
nativePkgs.hydra-node.components.exes.hydra-node
Expand All @@ -77,13 +95,15 @@ rec {
nativePkgs.hydra-tui.components.exes.hydra-tui
"hydra-tui"
paddedRevision;

hydra-tui-static =
embedRevision
musl64Pkgs.hydra-tui.components.exes.hydra-tui
"hydra-tui"
paddedRevision;

hydraw = nativePkgs.hydraw.components.exes.hydraw;

hydraw-static = musl64Pkgs.hydraw.components.exes.hydraw;

tests = {
Expand Down
13 changes: 13 additions & 0 deletions nix/hydra/project.nix
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ let
# Reason: haskell.nix modules/overlays neds to be last
# https://github.com/input-output-hk/haskell.nix/issues/1954
haskellNix.overlay
# Custom static libs used for darwin build
(import ../static-libs.nix)
];
};

Expand Down Expand Up @@ -66,6 +68,17 @@ let
# XXX: Could not figure out where to make this flag ^^^ effective in the haddock build
packages.strict-containers.doHaddock = false;
}
# Use different static libs on darwin
# TODO: Always use these?
(pkgs.lib.mkIf pkgs.hostPlatform.isDarwin {
packages.hydra-node.ghcOptions = with pkgs; [
"-L${lib.getLib static-gmp}/lib"
"-L${lib.getLib static-libsodium-vrf}/lib"
"-L${lib.getLib static-secp256k1}/lib"
"-L${lib.getLib static-openssl}/lib"
"-L${lib.getLib static-libblst}/lib"
];
})
# Fix compilation with newer ghc versions
({ lib, config, ... }:
lib.mkIf (lib.versionAtLeast config.compiler.version "9.4") {
Expand Down
79 changes: 79 additions & 0 deletions nix/packaging.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Vendored from https://github.com/input-output-hk/haskell-nix-example/blob/2247d445b91b0cc21eda4359e060d76b3cc0ce41/packaging.nix
#
# Modified to not be an overlay and changed the resultin zip format slightly.
{ pkgs }:
{
# Given one or more 'drvs', this produces a single zip containing all binaries
# potentially patched for better distribution.
asZip = { name ? null }: drvs:
let
drv = if builtins.isList drvs then builtins.head drvs else drvs;
name' = if name == null then drv.pname or drv.name else name;
combined = pkgs.symlinkJoin { name = "${name'}-binaries"; paths = drvs; };
targetPlatform = drv.stdenv.targetPlatform;
interpForSystem = sys:
let
s = {
"i686-linux" = "/lib/ld-linux.so.2";
"x86_64-linux" = "/lib64/ld-linux-x86-64.so.2";
"aarch64-linux" = "/lib/ld-linux-aarch64.so.1";
"armv7l-linux" = "/lib/ld-linux-armhf.so.3";
"armv7a-linux" = "/lib/ld-linux-armhf.so.3";
};
in
s.${sys} or (builtins.abort "Unsupported system ${sys}. Supported systms are: ${builtins.concatStringsSep ", " (builtins.attrNames s)}.");
fixup-nix-deps = pkgs.writeShellApplication {
name = "fixup-nix-deps";
text = ''
for nixlib in $(otool -L "$1" |awk '/nix\/store/{ print $1 }'); do
case "$nixlib" in
*libiconv.dylib) install_name_tool -change "$nixlib" /usr/lib/libiconv.dylib "$1" ;;
*libffi.*.dylib) install_name_tool -change "$nixlib" /usr/lib/libffi.dylib "$1" ;;
*libc++.*.dylib) install_name_tool -change "$nixlib" /usr/lib/libc++.dylib "$1" ;;
*libz.dylib) install_name_tool -change "$nixlib" /usr/lib/libz.dylib "$1" ;;
*) ;;
esac
done
'';
};
in
pkgs.stdenv.mkDerivation {
name = "${name'}.zip";
buildInputs = with pkgs; [ patchelf zip fixup-nix-deps ];

phases = [ "buildPhase" "installPhase" ];

buildPhase = ''
mkdir -p bin
cp ${combined}/bin/* bin/
''
# set the interpreter to the default expected location on linux. (See interpForSystem above)
+ pkgs.lib.optionalString (targetPlatform.isLinux && targetPlatform.isGnu) ''
for bin in bin/*; do
mode=$(stat -c%a $bin)
chmod +w $bin
patchelf --set-interpreter ${interpForSystem targetPlatform.system} $bin
chmod $mode $bin
done
'' + pkgs.lib.optionalString (targetPlatform.isDarwin) ''
for bin in bin/*; do
mode=$(stat -c%a $bin)
chmod +w $bin
fixup-nix-deps $bin
chmod $mode $bin
done
'';

# compress and put into hydra products
installPhase = ''
mkdir -p $out/
cd bin/
zip -r -9 $out/${name'}.zip .
'';

passthru = {
isPackage = true;
packageName = "${name'}.zip";
};
};
}
18 changes: 18 additions & 0 deletions nix/static-libs.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Overlay vendored from https://github.com/input-output-hk/haskell-nix-example/blob/2247d445b91b0cc21eda4359e060d76b3cc0ce41/flake.nix#L53C17-L53C17
final: prev: {
static-libsodium-vrf = final.libsodium-vrf.overrideDerivation (old: {
configureFlags = old.configureFlags ++ [ "--disable-shared" ];
});
static-secp256k1 = final.secp256k1.overrideDerivation (old: {
configureFlags = old.configureFlags ++ [ "--enable-static" "--disable-shared" ];
});
static-gmp = (final.gmp.override { withStatic = true; }).overrideDerivation (old: {
configureFlags = old.configureFlags ++ [ "--enable-static" "--disable-shared" ];
});
static-libblst = (final.libblst.override { enableShared = false; }).overrideDerivation (old: {
postFixup = "";
});
static-openssl = (final.openssl.override { static = true; });
static-zlib = final.zlib.override { shared = false; };
static-pcre = final.pcre.override { shared = false; };
}
Loading