Skip to content
Merged
55 changes: 55 additions & 0 deletions .github/workflows/release_java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -76,16 +80,55 @@ 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 \
--features services-all \
--enable-zigbuild false

chown -R "$HOST_UID:$HOST_GID" /work/bindings/java/target
'

- name: Local staging
working-directory: bindings/java
shell: bash
run: |
EXTRA_MVN_ARGS=""
if [[ "${{ matrix.classifier }}" == *"musl" ]]; then
EXTRA_MVN_ARGS="-Dexec.skip=true"
fi

./mvnw -Papache-release package verify org.sonatype.plugins:nexus-staging-maven-plugin:deploy \
-DskipTests=true \
-Djni.classifier=${{ matrix.classifier }} \
-Dcargo-build.profile=release \
-Dcargo-build.features=services-all \
-Dcargo-build.enableZigbuild=${{ env.CARGO_BUILD_ENABLE_ZIGBUILD }} \
$EXTRA_MVN_ARGS \
-DaltStagingDirectory=local-staging \
-DskipRemoteStaging=true \
-DserverId=apache.releases.https \
Expand Down Expand Up @@ -135,11 +178,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@v5
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@v5
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:
Expand All @@ -160,7 +213,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

Expand Down
2 changes: 1 addition & 1 deletion bindings/java/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ repository = "https://github.com/apache/opendal"
rust-version = "1.85"

[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib", "staticlib"]
doc = false

[features]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public static void loadLibrary() {
} catch (IOException e) {
libraryLoaded.set(LibraryState.NOT_LOADED);
throw new UncheckedIOException("Unable to load the OpenDAL shared library", e);
} catch (UnsatisfiedLinkError e) {
libraryLoaded.set(LibraryState.NOT_LOADED);
throw e;
}
libraryLoaded.set(LibraryState.LOADED);
return;
Expand Down Expand Up @@ -104,12 +107,39 @@ private static void doLoadLibrary() throws IOException {

private static void doLoadBundledLibrary() throws IOException {
final String libraryPath = bundledLibraryPath();
UnsatisfiedLinkError linkError = null;
try (final InputStream is = NativeObject.class.getResourceAsStream(libraryPath)) {
if (is != null) {
final int dot = libraryPath.indexOf('.');
final File tmpFile = File.createTempFile(libraryPath.substring(0, dot), libraryPath.substring(dot));
tmpFile.deleteOnExit();
Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
try {
System.load(tmpFile.getAbsolutePath());
return;
} catch (UnsatisfiedLinkError e) {
linkError = e;
}
}
}

final String fallbackLibraryPath = fallbackBundledLibraryPath();
if (fallbackLibraryPath == null) {
if (linkError != null) {
throw linkError;
}
throw new IOException("cannot find " + libraryPath);
}
try (final InputStream is = NativeObject.class.getResourceAsStream(fallbackLibraryPath)) {
if (is == null) {
if (linkError != null) {
throw linkError;
}
throw new IOException("cannot find " + libraryPath);
}
final int dot = libraryPath.indexOf('.');
final File tmpFile = File.createTempFile(libraryPath.substring(0, dot), libraryPath.substring(dot));
final int dot = fallbackLibraryPath.indexOf('.');
final File tmpFile =
File.createTempFile(fallbackLibraryPath.substring(0, dot), fallbackLibraryPath.substring(dot));
tmpFile.deleteOnExit();
Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.load(tmpFile.getAbsolutePath());
Expand All @@ -121,4 +151,17 @@ private static String bundledLibraryPath() {
final String libraryName = System.mapLibraryName("opendal_java");
return "/native/" + classifier + "/" + libraryName;
}

private static String fallbackBundledLibraryPath() {
final String classifier = Environment.getClassifier();
if (!classifier.startsWith("linux-")) {
return null;
}
final String libraryName = System.mapLibraryName("opendal_java");
if (classifier.endsWith("-musl")) {
final String gnu = classifier.substring(0, classifier.length() - "-musl".length());
return "/native/" + gnu + "/" + libraryName;
}
return "/native/" + classifier + "-musl/" + libraryName;
}
}
Comment thread
Xuanwo marked this conversation as resolved.
24 changes: 21 additions & 3 deletions bindings/java/tools/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
from pathlib import Path
import glob
import shutil
import subprocess

Expand All @@ -30,8 +31,12 @@ def classifier_to_target(classifier: str) -> str:
return "x86_64-apple-darwin"
if classifier == "linux-aarch_64":
return "aarch64-unknown-linux-gnu"
if classifier == "linux-aarch_64-musl":
return "aarch64-unknown-linux-musl"
if classifier == "linux-x86_64":
return "x86_64-unknown-linux-gnu"
if classifier == "linux-x86_64-musl":
return "x86_64-unknown-linux-musl"
if classifier == "windows-x86_64":
return "x86_64-pc-windows-msvc"
raise Exception(f"Unsupported classifier: {classifier}")
Expand All @@ -46,6 +51,9 @@ def get_cargo_artifact_name(classifier: str) -> str:
return "opendal_java.dll"
raise Exception(f"Unsupported classifier: {classifier}")

def is_musl_runtime() -> bool:
return len(glob.glob("/lib/ld-musl-*.so.1")) > 0 or len(glob.glob("/usr/lib/ld-musl-*.so.1")) > 0


if __name__ == "__main__":
basedir = Path(__file__).parent.parent
Expand All @@ -68,8 +76,11 @@ def get_cargo_artifact_name(classifier: str) -> str:
print("$ " + subprocess.list2cmdline(command))
subprocess.run(command, cwd=basedir, check=True)

# Enable zigbuild if flag enabled and we are building linux target
enable_zigbuild = args.enable_zigbuild == "true" and "linux" in target
# Enable zigbuild if flag enabled and we are building linux gnu target.
#
# For musl targets, prefer using the system musl toolchain (e.g. `musl-tools` on Ubuntu)
# instead of zigbuild.
enable_zigbuild = args.enable_zigbuild == "true" and "linux" in target and not target.endswith("-musl")

cmd = [
"cargo",
Expand All @@ -81,7 +92,7 @@ def get_cargo_artifact_name(classifier: str) -> str:
if args.features:
cmd += ["--features", args.features]

if enable_zigbuild:
if enable_zigbuild and target.endswith("-gnu"):
# Pin glibc to 2.17 if zigbuild has been enabled.
cmd += ["--target", f"{target}.2.17"]
else:
Expand All @@ -96,8 +107,15 @@ def get_cargo_artifact_name(classifier: str) -> str:

# History reason of cargo profiles.
profile = "debug" if args.profile in ["dev", "test", "bench"] else args.profile

artifact = get_cargo_artifact_name(args.classifier)
src = output / target / profile / artifact
dst = basedir / "target" / "classes" / "native" / args.classifier / artifact
dst.parent.mkdir(exist_ok=True, parents=True)

if target.endswith("-musl") and not is_musl_runtime():
raise Exception(
"Building musl artifacts requires running inside a musl environment (e.g. Alpine)."
)

shutil.copy2(src, dst)
Loading