From e532ece8a67bec4e5d732532158cb7691b6d2a4f Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Sat, 29 Aug 2026 16:14:36 +0200 Subject: [PATCH] fix(build): ELF-aware ar/ranlib for the aarch64 cross build on macOS hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cross toolchain file set only the compiler; on a macOS host CMake then used /usr/bin/ar + ranlib, which silently produce an EMPTY static archive from cross-compiled ELF objects (just a __.SYMDEF) — published linuxArm64 klibs shipped without the C kernels, and consumers died at link with "undefined symbol: skainet_q4k_matmul" et al. toolchain-aarch64.cmake now resolves CMAKE_AR/CMAKE_RANLIB (overridable via -DSKAINET_AARCH64_AR/-DSKAINET_AARCH64_RANLIB, preferring llvm-ar), and the gradle configure task forwards the matching -PskainetAarch64Ar/Ranlib properties — e.g. Konan's bundled llvm-ar on a Mac. Co-authored-by: Claude Fable 5 --- .../skainet-backend-native-cpu/build.gradle.kts | 6 ++++++ .../native/toolchain-aarch64.cmake | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/skainet-backends/skainet-backend-native-cpu/build.gradle.kts b/skainet-backends/skainet-backend-native-cpu/build.gradle.kts index 48231a072..2d75bd56d 100644 --- a/skainet-backends/skainet-backend-native-cpu/build.gradle.kts +++ b/skainet-backends/skainet-backend-native-cpu/build.gradle.kts @@ -316,6 +316,12 @@ val configureNativeKernelsArm64 by tasks.registering(Exec::class) { "-DCMAKE_BUILD_TYPE=Release", "-DCMAKE_TOOLCHAIN_FILE=$toolchainFilePath", "-DSKAINET_AARCH64_CC=$aarch64Cc", + ) + listOfNotNull( + // Cross-archiving from a macOS host needs an ELF-aware ar/ranlib (the + // BSD defaults silently emit an EMPTY archive from ELF objects) — see + // toolchain-aarch64.cmake. Optional overrides, e.g. Konan's llvm-ar. + (findProperty("skainetAarch64Ar") as String?)?.let { "-DSKAINET_AARCH64_AR=$it" }, + (findProperty("skainetAarch64Ranlib") as String?)?.let { "-DSKAINET_AARCH64_RANLIB=$it" }, ) } diff --git a/skainet-backends/skainet-backend-native-cpu/native/toolchain-aarch64.cmake b/skainet-backends/skainet-backend-native-cpu/native/toolchain-aarch64.cmake index 65e7e2467..dc657f064 100644 --- a/skainet-backends/skainet-backend-native-cpu/native/toolchain-aarch64.cmake +++ b/skainet-backends/skainet-backend-native-cpu/native/toolchain-aarch64.cmake @@ -21,6 +21,21 @@ if(NOT DEFINED SKAINET_AARCH64_CC) endif() set(CMAKE_C_COMPILER ${SKAINET_AARCH64_CC}) +# The archiver must understand ELF objects. On a macOS host the default +# /usr/bin/ar + ranlib silently produce an EMPTY archive from cross-compiled +# ELF objects (just a __.SYMDEF), which then surfaces downstream as +# "undefined symbol: skainet_*" when a consumer links the published klib. +# Allow -DSKAINET_AARCH64_AR/-DSKAINET_AARCH64_RANLIB overrides, and default +# to llvm-ar/llvm-ranlib when they are on the PATH (they handle ELF anywhere). +if(NOT DEFINED SKAINET_AARCH64_AR) + find_program(SKAINET_AARCH64_AR NAMES llvm-ar aarch64-linux-gnu-gcc-ar ar) +endif() +if(NOT DEFINED SKAINET_AARCH64_RANLIB) + find_program(SKAINET_AARCH64_RANLIB NAMES llvm-ranlib aarch64-linux-gnu-gcc-ranlib ranlib) +endif() +set(CMAKE_AR ${SKAINET_AARCH64_AR}) +set(CMAKE_RANLIB ${SKAINET_AARCH64_RANLIB}) + # Search for libraries/headers only in the target sysroot, but find # programs (the compiler) on the host. set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)