diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0edf9f49..71434c02 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,6 +55,8 @@ jobs: target: - os: ubuntu-latest type: linux + - os: ubuntu-24.04-arm + type: linuxarm64 - os: ubuntu-latest type: windows - os: macos-latest diff --git a/bin/README.md b/bin/README.md index 5811c4f7..3e1939c4 100644 --- a/bin/README.md +++ b/bin/README.md @@ -25,11 +25,13 @@ The expected library file names for different operating systems are: |---------|-----------------------| | Windows | imgui-java64.dll | | Linux | libimgui-java64.so | +| Linux ARM64 | libimgui-java64-arm64.so | | macOS | libimgui-java64.dylib | ### Additional Information - All libraries include statically compiled **FreeType**. +- `libimgui-java64-arm64.so` is the Linux aarch64 build. Its file name differs from the x86-64 one only because CI collects every platform into this single directory; when loading it by path, pass `-Dimgui.library.name=libimgui-java64-arm64.so`. - The macOS version is a universal library and support x86_64 and arm64 architectures. ### Continuous Integration diff --git a/buildSrc/scripts/build.sh b/buildSrc/scripts/build.sh index d7d17d69..a422dfab 100755 --- a/buildSrc/scripts/build.sh +++ b/buildSrc/scripts/build.sh @@ -77,6 +77,27 @@ case "$VTYPE" in fi echo "SO file copied to /tmp/imgui/dst/libimgui-java64.so successfully" ;; + linuxarm64) + echo "Running Gradle task for Linux ARM64 (native aarch64 host required)..." + ./gradlew imgui-binding:generateLibs -Denvs=linuxarm64 -Dfreetype=true + if [ $? -ne 0 ]; then + echo "Gradle task for Linux ARM64 failed" + exit 1 + fi + + echo "Checking if the generated SO file exists..." + check_file_exists /tmp/imgui/libsNative/linuxarm64/libimgui-java64.so + + # Distinct name: CI merges every platform's output into one flat directory (bin/), + # so it must not collide with the x86-64 libimgui-java64.so. + echo "Copying the generated SO file to the destination directory..." + cp /tmp/imgui/libsNative/linuxarm64/libimgui-java64.so /tmp/imgui/dst/libimgui-java64-arm64.so + if [ $? -ne 0 ]; then + echo "Failed to copy SO file to /tmp/imgui/dst/libimgui-java64-arm64.so" + exit 1 + fi + echo "SO file copied to /tmp/imgui/dst/libimgui-java64-arm64.so successfully" + ;; macos) echo "Running Gradle task for macOS and macOS ARM..." ./gradlew imgui-binding:generateLibs -Denvs=macos,macosarm64 -Dfreetype=true diff --git a/buildSrc/scripts/publish.sh b/buildSrc/scripts/publish.sh index 9f73b829..615060e3 100755 --- a/buildSrc/scripts/publish.sh +++ b/buildSrc/scripts/publish.sh @@ -40,6 +40,7 @@ publish_natives() { # Publish natives for each platform publish_natives "windows" publish_natives "linux" +publish_natives "linuxarm64" publish_natives "macos" echo "> Nexus manual upload..." diff --git a/buildSrc/scripts/vendor_freetype.sh b/buildSrc/scripts/vendor_freetype.sh index 5d973c79..68fb42db 100755 --- a/buildSrc/scripts/vendor_freetype.sh +++ b/buildSrc/scripts/vendor_freetype.sh @@ -84,6 +84,10 @@ case "$VTYPE" in linux) build_freetype "-fPIC" "" "lib/libfreetype.a" ;; + linuxarm64) + # Built natively on an aarch64 host (e.g. the ubuntu-24.04-arm runner), so no --host/cross prefix. + build_freetype "-fPIC" "" "lib/libfreetype.a" + ;; macos) MACOS_VERSION=10.15 diff --git a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy index d6dd666c..d7df6b0f 100644 --- a/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy +++ b/buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy @@ -32,6 +32,7 @@ class GenerateLibs extends DefaultTask { private final String[] buildEnvs = System.getProperty('envs')?.split(',') private final boolean forWindows = buildEnvs?.contains('windows') private final boolean forLinux = buildEnvs?.contains('linux') + private final boolean forLinuxArm64 = buildEnvs?.contains('linuxarm64') private final boolean forMac = buildEnvs?.contains('macos') private final boolean forMacArm64 = buildEnvs?.contains('macosarm64') @@ -120,10 +121,11 @@ class GenerateLibs extends DefaultTask { } if (forLinux) { - def linux64 = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64) - requireCpp17(linux64) - addFreeTypeIfEnabled(linux64) - buildTargets += linux64 + buildTargets += createLinuxTarget(Architecture.x86) + } + + if (forLinuxArm64) { + buildTargets += createLinuxTarget(Architecture.ARM) } if (forMac) { @@ -145,6 +147,8 @@ class GenerateLibs extends DefaultTask { BuildExecutor.executeAnt(jniDir + '/build-windows64.xml', commonParams) if (forLinux) BuildExecutor.executeAnt(jniDir + '/build-linux64.xml', commonParams) + if (forLinuxArm64) + BuildExecutor.executeAnt(jniDir + '/build-linuxarm64.xml', commonParams) if (forMac) BuildExecutor.executeAnt(jniDir + '/build-macosx64.xml', commonParams) if (forMacArm64) @@ -156,6 +160,8 @@ class GenerateLibs extends DefaultTask { checkLibExist("windows64/imgui-java64.dll") if (forLinux) checkLibExist("linux64/libimgui-java64.so") + if (forLinuxArm64) + checkLibExist("linuxarm64/libimgui-java64.so") if (forMac) checkLibExist("macosx64/libimgui-java64.dylib") if (forMacArm64) @@ -170,6 +176,21 @@ class GenerateLibs extends DefaultTask { } } + BuildTarget createLinuxTarget(Architecture arch) { + def linuxTarget = BuildTarget.newDefaultTarget(Os.Linux, Architecture.Bitness._64, arch) + linuxTarget.libName = "libimgui-java64.so" // Lib for arm64 will be named the same for consistency. + if (arch == Architecture.ARM && System.getProperty('os.arch') in ['aarch64', 'arm64']) { + // gdx-jnigen defaults ARM Linux targets to the aarch64-linux-gnu- cross-compiler + // prefix. When already running natively on an aarch64 host (e.g. GitHub's + // ubuntu-24.04-arm runners) that prefix isn't needed, and the cross toolchain + // package providing it usually isn't installed there anyway. + linuxTarget.compilerPrefix = '' + } + requireCpp17(linuxTarget) + addFreeTypeIfEnabled(linuxTarget) + return linuxTarget + } + BuildTarget createMacTarget(Architecture arch) { def minMacOsVersion = '10.15' def macTarget = BuildTarget.newDefaultTarget(Os.MacOsX, Architecture.Bitness._64, arch) diff --git a/imgui-binding-natives/build.gradle b/imgui-binding-natives/build.gradle index d8a9c2dc..6666f60b 100644 --- a/imgui-binding-natives/build.gradle +++ b/imgui-binding-natives/build.gradle @@ -19,6 +19,7 @@ def packageDesc = 'Native binaries for imgui-java binding for Linux' def moduleName = 'imgui.natives.linux' def fromDir = '../bin' def libName = 'libimgui-java64.so' +def jarLibName = null // set when the file name in bin/ differs from the name the loader expects on the classpath switch (findProperty('deployType')) { case 'windows': @@ -33,6 +34,13 @@ switch (findProperty('deployType')) { moduleName = 'imgui.natives.linux' libName = 'libimgui-java64.so' break + case 'linuxarm64': + packageName = 'imgui-java-natives-linux-arm64' + packageDesc = 'Native binaries for imgui-java binding for Linux ARM64' + moduleName = 'imgui.natives.linux.arm64' + libName = 'libimgui-java64-arm64.so' + jarLibName = 'libimgui-java64.so' + break case 'macos': packageName = 'imgui-java-natives-macos' packageDesc = 'Native binaries for imgui-java binding for macOS' @@ -45,6 +53,9 @@ jar { from(fromDir) { include "$libName" // this is fine into 'io/imgui/java/native-bin/' + if (jarLibName != null) { + rename { jarLibName } + } } manifest { attributes 'Automatic-Module-Name': moduleName