From d66287265d9b2c0852881b15a3011782c36f3af9 Mon Sep 17 00:00:00 2001 From: Rob ODwyer <126593209+rob-odwyer@users.noreply.github.com> Date: Thu, 3 Aug 2023 18:44:13 -0700 Subject: [PATCH] Build shared library for Apple Silicon (#51) * aarch64 build with cargo * turn off tests for aarch64-macos * use an env var to avoid more than 2 gradle build variants * support macos_aarch64 shared library * always upload artifact after build step * fix gradle log * fix debug command on windows * use arch from rust target triple when set in gradle * fix platform + arch in shared library filename * add arch into shared lib filename for other platforms * restore conditional check for uploading artifacts * add note to README about aarch64 --- .github/workflows/ci.yml | 30 ++++++++++++--- README.md | 11 +++--- build.gradle | 9 ++++- .../wasmtime/NativeLibraryLoader.java | 38 +++++++++++++++---- 4 files changed, 69 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0303228..140c73f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,20 @@ on: branches: [ master ] jobs: build: + name: Build JNI lib for ${{ matrix.build }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, macos-latest, windows-latest] + include: + - build: x86_64-linux + os: ubuntu-latest + - build: x86_64-macos + os: macos-latest + - build: aarch64-macos + os: macos-latest + target: aarch64-apple-darwin + - build: x86_64-windows + os: windows-latest steps: - uses: actions/checkout@v2 - name: Set up JDK 1.8 @@ -31,16 +41,26 @@ jobs: - name: Binary compatibility settings (Windows) if: ${{ startsWith(matrix.os, 'windows') }} run: echo 'RUSTFLAGS=-Ctarget-feature=+crt-static' >> $GITHUB_ENV - # Run gradle normally to build the JNI lib - - name: Build with Gradle - if: ${{ !startsWith(matrix.os, 'ubuntu') }} - run: ./gradlew build copyJniLib + - name: Set build target for cross-compiling + if: matrix.target != '' + run: | + echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV + echo JNILIB_RUST_TARGET=${{ matrix.target }} >> $GITHUB_ENV + echo GRADLE_ARGS="${GRADLE_ARGS} -x test">> $GITHUB_ENV + rustup target add ${{ matrix.target }} # Run gradle in a docker container to build the JNI lib on Linux for old glibc compatibility - name: Build with Gradle (Linux) if: ${{ startsWith(matrix.os, 'ubuntu') }} run: | docker build -t build-image ./ci/docker/x86_64-linux docker run --rm --volume $PWD:/build --workdir /build build-image ./gradlew build copyJniLib -x javadoc + # Otherwise, run gradle normally to build the JNI lib + - name: Build with Gradle + if: ${{ !startsWith(matrix.os, 'ubuntu') }} + run: ./gradlew build copyJniLib ${GRADLE_ARGS} + - name: List shared library files + run: + ls build/jni-libs - name: Save JNI lib output if: startsWith(github.ref, 'refs/tags/') uses: actions/upload-artifact@v2 diff --git a/README.md b/README.md index e6dda97..29371e7 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,12 @@ dependencies { An artifact (JAR) of `wasmtime-java` ships along with prebuilt JNI libraries for some major platforms, so just adding the above dependency provides you a self-contained `wasmtime` runtime on supported platforms: -| OS | Arch | -| ---- | ---- | -| Linux (ELF) | x86_64 | -| Mac OS | x86_64 | -| Windows | x86_64 | +| OS | Arch | +| ---- | ---- | +| Linux (ELF) | x86_64 | +| Mac OS | x86_64 | +| Mac OS | aarch64 | +| Windows | x86_64 | # Example diff --git a/build.gradle b/build.gradle index cb142d8..0ceaf7f 100644 --- a/build.gradle +++ b/build.gradle @@ -59,6 +59,7 @@ test { static def jniLibOsClassifier() { def os = System.getProperty("os.name").toLowerCase() + if (os.contains("linux")) { return "linux" } @@ -71,6 +72,12 @@ static def jniLibOsClassifier() { throw new RuntimeException("platform not supported: " + System.getProperty("os.name")) } +static def jniLibArchClassifier() { + def target = rustTargetTriple() ?: "" + // Use the first part of the rust target triple as the arch classifier if set, otherwise assume "x86_64" + return target ? target.split("-")[0] : "x86_64" +} + static def rustTargetTriple() { System.getenv("JNILIB_RUST_TARGET") } @@ -170,7 +177,7 @@ task copyJniLib(type: Copy) { def targetDir = rustTargetTriple() ?: "" from "wasmtime-jni/target/$targetDir/release" include '*.so', '*.dylib', "*.dll" - rename "^(lib)?wasmtime_jni", "\$1wasmtime_jni_${project.version}_${jniLibOsClassifier()}" + rename "^(lib)?wasmtime_jni", "\$1wasmtime_jni_${project.version}_${jniLibOsClassifier()}_${jniLibArchClassifier()}" into new File(project.buildDir, "jni-libs") } diff --git a/src/main/java/io/github/kawamuray/wasmtime/NativeLibraryLoader.java b/src/main/java/io/github/kawamuray/wasmtime/NativeLibraryLoader.java index 45dab3a..ddfe7b5 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/NativeLibraryLoader.java +++ b/src/main/java/io/github/kawamuray/wasmtime/NativeLibraryLoader.java @@ -60,7 +60,7 @@ private static String libraryPath() throws IOException { Platform platform = detectPlatform(); String version = libVersion(); String ext = platform.ext; - String fileName = platform.prefix + NATIVE_LIBRARY_NAME + '_' + version + '_' + platform.classifier; + String fileName = platform.prefix + NATIVE_LIBRARY_NAME + '_' + version + '_' + platform.os.value + '_' + platform.arch.value; Path tempFile = Files.createTempFile(fileName, ext); try (InputStream in = NativeLibraryLoader.class.getResourceAsStream('/' + fileName + ext)) { Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING); @@ -70,34 +70,56 @@ private static String libraryPath() throws IOException { private static String libVersion() throws IOException { final Properties props; - try (InputStream in = NativeLibraryLoader.class.getResourceAsStream( '/' + META_PROPS_FILE)) { + try (InputStream in = NativeLibraryLoader.class.getResourceAsStream('/' + META_PROPS_FILE)) { props = new Properties(); props.load(in); } return props.getProperty(JNI_LIB_VERSION_PROP); } + @AllArgsConstructor + private enum Os { + LINUX("linux"), + MACOS("macos"), + WINDOWS("windows"); + + final String value; + } + + @AllArgsConstructor + private enum Arch { + X86_64("x86_64"), + AARCH64("aarch64"); + + final String value; + } + @AllArgsConstructor private enum Platform { - LINUX("linux","lib" , ".so"), - MACOS("macos","lib", ".dylib"), - WINDOWS("windows","",".dll") - ; + LINUX(Os.LINUX, Arch.X86_64, "lib", ".so"), + MACOS(Os.MACOS, Arch.X86_64, "lib", ".dylib"), + MACOS_AARCH64(Os.MACOS, Arch.AARCH64, "lib", ".dylib"), + WINDOWS(Os.WINDOWS, Arch.X86_64, "", ".dll"); - final String classifier; + final Os os; + final Arch arch; final String prefix; final String ext; } private static Platform detectPlatform() { String os = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); if (os.contains("linux")) { return Platform.LINUX; } if (os.contains("mac os") || os.contains("darwin")) { + if (arch.equals("aarch64")) { + return Platform.MACOS_AARCH64; + } return Platform.MACOS; } - if(os.toLowerCase().contains("windows")){ + if (os.toLowerCase().contains("windows")) { return Platform.WINDOWS; } throw new RuntimeException("platform not supported: " + os);