diff --git a/.github/workflows/release_java.yml b/.github/workflows/release_java.yml index 9d21368501f6..067816325367 100644 --- a/.github/workflows/release_java.yml +++ b/.github/workflows/release_java.yml @@ -42,8 +42,12 @@ jobs: include: - os: ubuntu-latest classifier: linux-x86_64 + - os: ubuntu-latest + classifier: linux-x86_64-musl - os: ubuntu-24.04-arm classifier: linux-aarch_64 + - os: ubuntu-24.04-arm + classifier: linux-aarch_64-musl - os: windows-latest classifier: windows-x86_64 - os: macos-latest @@ -76,24 +80,103 @@ jobs: if: ${{ contains(matrix.os, 'ubuntu') }} run: pip install cargo-zigbuild + - name: Build linux musl JNI library in Alpine + if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.classifier, 'musl') }} + shell: bash + run: | + set -euo pipefail + HOST_UID="$(id -u)" + HOST_GID="$(id -g)" + PLATFORM="linux/amd64" + if [[ "${{ matrix.classifier }}" == *"aarch_64"* ]]; then + PLATFORM="linux/arm64" + fi + + docker run --rm \ + --platform "$PLATFORM" \ + -e HOST_UID="$HOST_UID" \ + -e HOST_GID="$HOST_GID" \ + -v "$GITHUB_WORKSPACE":/work \ + -w /work/bindings/java \ + rust:1.85-alpine3.20 \ + sh -lc ' + set -eu + apk add --no-cache python3 build-base cmake pkgconfig git protobuf sqlite-dev zlib-dev + export PATH=/usr/local/cargo/bin:$PATH + + python3 tools/build.py \ + --classifier "${{ matrix.classifier }}" \ + --profile release \ + --enable-zigbuild false + + chown -R "$HOST_UID:$HOST_GID" /work/bindings/java/target + ' + - name: Local staging working-directory: bindings/java shell: bash run: | - ./mvnw -Papache-release package verify org.sonatype.plugins:nexus-staging-maven-plugin:deploy \ + EXTRA_MVN_ARGS="" + if [[ "${{ matrix.classifier }}" == *"musl" ]]; then + EXTRA_MVN_ARGS="-Dexec.skip=true" + fi + + MVN_GOALS="package verify" + if [[ "${{ github.event_name }}" != "pull_request" ]]; then + MVN_GOALS="$MVN_GOALS org.sonatype.plugins:nexus-staging-maven-plugin:deploy" + EXTRA_MVN_ARGS="$EXTRA_MVN_ARGS \ + -DaltStagingDirectory=local-staging \ + -DskipRemoteStaging=true \ + -DserverId=apache.releases.https \ + -DnexusUrl=https://repository.apache.org" + fi + + ./mvnw -Papache-release $MVN_GOALS \ -DskipTests=true \ -Djni.classifier=${{ matrix.classifier }} \ -Dcargo-build.profile=release \ -Dcargo-build.enableZigbuild=${{ env.CARGO_BUILD_ENABLE_ZIGBUILD }} \ - -DaltStagingDirectory=local-staging \ - -DskipRemoteStaging=true \ - -DserverId=apache.releases.https \ - -DnexusUrl=https://repository.apache.org + $EXTRA_MVN_ARGS env: MAVEN_USERNAME: ${{ secrets.NEXUS_STAGE_DEPLOYER_USER }} MAVEN_CENTRAL_TOKEN: ${{ secrets.NEXUS_STAGE_DEPLOYER_PW }} MAVEN_GPG_PASSPHRASE: "" + - name: Smoke linux musl JNI library in Alpine + if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.classifier, 'musl') }} + shell: bash + run: | + set -euo pipefail + PLATFORM="linux/amd64" + if [[ "${{ matrix.classifier }}" == *"aarch_64"* ]]; then + PLATFORM="linux/arm64" + fi + + docker run --rm \ + --platform "$PLATFORM" \ + -v "$GITHUB_WORKSPACE":/work \ + -w /work/bindings/java \ + alpine:3.20 \ + sh -lc ' + set -eu + apk add --no-cache openjdk17-jdk libgcc + BASE_JAR="$(find target -maxdepth 1 -name "opendal-*.jar" ! -name "*-linux-*.jar" ! -name "*-sources.jar" ! -name "*-javadoc.jar" | head -n 1)" + NATIVE_JAR="$(find target -maxdepth 1 -name "opendal-*-${{ matrix.classifier }}.jar" | head -n 1)" + test -n "$BASE_JAR" + test -n "$NATIVE_JAR" + + { + echo "import org.apache.opendal.NativeLibrary;" + echo "public class OpenDALSmoke {" + echo " public static void main(String[] args) {" + echo " NativeLibrary.loadLibrary();" + echo " }" + echo "}" + } > /tmp/OpenDALSmoke.java + + java -cp "$BASE_JAR:$NATIVE_JAR" /tmp/OpenDALSmoke.java + ' - name: Upload local staging directory + if: ${{ github.event_name != 'pull_request' }} uses: actions/upload-artifact@v6 with: name: ${{ matrix.classifier }}-local-staging @@ -102,6 +185,7 @@ jobs: include-hidden-files: true deploy-staged-snapshots: + if: ${{ github.event_name != 'pull_request' }} runs-on: ubuntu-latest needs: [stage-snapshot] steps: @@ -134,11 +218,21 @@ jobs: with: name: linux-x86_64-local-staging path: ~/linux-x86_64-local-staging + - name: Download linux x86_64 (musl) staging directory + uses: actions/download-artifact@v7 + with: + name: linux-x86_64-musl-local-staging + path: ~/linux-x86_64-musl-local-staging - name: Download linux aarch_64 staging directory uses: actions/download-artifact@v7 with: name: linux-aarch_64-local-staging path: ~/linux-aarch_64-local-staging + - name: Download linux aarch_64 (musl) staging directory + uses: actions/download-artifact@v7 + with: + name: linux-aarch_64-musl-local-staging + path: ~/linux-aarch_64-musl-local-staging - name: Download darwin staging directory uses: actions/download-artifact@v7 with: @@ -159,7 +253,9 @@ jobs: python ./scripts/merge_local_staging.py $LOCAL_STAGING_DIR/staging \ ~/windows-x86_64-local-staging/staging \ ~/linux-x86_64-local-staging/staging \ + ~/linux-x86_64-musl-local-staging/staging \ ~/linux-aarch_64-local-staging/staging \ + ~/linux-aarch_64-musl-local-staging/staging \ ~/osx-x86_64-local-staging/staging \ ~/osx-aarch_64-local-staging/staging diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml index b1d7a4cd5c24..67dcfd8e2f86 100644 --- a/bindings/java/Cargo.toml +++ b/bindings/java/Cargo.toml @@ -27,7 +27,7 @@ repository = "https://github.com/apache/opendal" rust-version = "1.85" [lib] -crate-type = ["cdylib"] +crate-type = ["cdylib", "staticlib"] doc = false [dependencies] diff --git a/bindings/java/README.md b/bindings/java/README.md index 24e892de22fc..6d4d1fe2966a 100644 --- a/bindings/java/README.md +++ b/bindings/java/README.md @@ -72,6 +72,16 @@ Then add the dependency to `opendal` as following: ``` +On musl-based Linux distributions such as Alpine, use the musl classified artifact instead of the default Linux classifier: + +```xml +linux-x86_64-musl +``` + +Use `linux-aarch_64-musl` for aarch64 Linux musl environments. + +The musl native library is dynamically linked against musl libc and the GCC runtime. On Alpine Linux, install `libgcc` before loading the native library. + ### Gradle For Gradle, you can first add the `com.google.osdetector` for automatically detect the classifier based on your platform: @@ -91,6 +101,10 @@ dependencies { } ``` +On musl-based Linux distributions such as Alpine, use `linux-x86_64-musl` or `linux-aarch_64-musl` as the classifier. + +The musl native library is dynamically linked against musl libc and the GCC runtime. On Alpine Linux, install `libgcc` before loading the native library. + ### Classified library Note that the dependency without classifier ships all classes and resources except the "opendal_java" shared library. And those with classifier bundle only the shared library. diff --git a/bindings/java/src/main/java/org/apache/opendal/Environment.java b/bindings/java/src/main/java/org/apache/opendal/Environment.java index ccdaab8caab7..7af0a8e8b1ea 100644 --- a/bindings/java/src/main/java/org/apache/opendal/Environment.java +++ b/bindings/java/src/main/java/org/apache/opendal/Environment.java @@ -22,6 +22,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Properties; /** @@ -60,6 +64,9 @@ public enum Environment { } else { classifier.append("x86_64"); } + if (classifier.indexOf("linux-") == 0 && isMuslRuntime()) { + classifier.append("-musl"); + } INSTANCE.classifier = classifier.toString(); } @@ -80,4 +87,16 @@ public static String getClassifier() { public static String getVersion() { return INSTANCE.projectVersion; } + + private static boolean isMuslRuntime() { + return hasMuslLoader(Paths.get("/lib")) || hasMuslLoader(Paths.get("/usr/lib")); + } + + private static boolean hasMuslLoader(Path dir) { + try (DirectoryStream stream = Files.newDirectoryStream(dir, "ld-musl-*.so.1")) { + return stream.iterator().hasNext(); + } catch (IOException | SecurityException e) { + return false; + } + } } diff --git a/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java b/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java index bc67e3237610..4877e9b7f683 100644 --- a/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java +++ b/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java @@ -58,7 +58,9 @@ private enum LibraryState { * You can use the prebuilt library: *