Skip to content
Merged
20 changes: 20 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,6 +80,10 @@ jobs:
if: ${{ contains(matrix.os, 'ubuntu') }}
run: pip install cargo-zigbuild

- name: Setup linux for musl
if: ${{ contains(matrix.os, 'ubuntu') && contains(matrix.classifier, 'musl') }}
run: sudo apt-get install musl-tools

- name: Local staging
working-directory: bindings/java
shell: bash
Expand Down Expand Up @@ -135,11 +143,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@v5
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@v5
with:
Expand All @@ -160,7 +178,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
28 changes: 28 additions & 0 deletions bindings/java/src/main/java/org/apache/opendal/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;

/**
Expand Down Expand Up @@ -60,9 +62,35 @@ public enum Environment {
} else {
classifier.append("x86_64");
}
if (classifier.toString().startsWith("linux-") && isMusl(arch)) {
classifier.append("-musl");
}
INSTANCE.classifier = classifier.toString();
}

private static boolean isMusl(String osArch) {
final String loader = muslLoaderName(osArch);
if (loader == null) {
return false;
}
return Files.exists(Paths.get("/lib", loader)) || Files.exists(Paths.get("/usr/lib", loader));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you test this line works?

I suspect that with bundled jar the file doesn't exist on /lib but in the JAR package.

}

private static String muslLoaderName(String osArch) {
if (osArch == null) {
return null;
}
switch (osArch) {
case "aarch64":
return "ld-musl-aarch64.so.1";
case "x86_64":
case "amd64":
return "ld-musl-x86_64.so.1";
default:
return null;
}
}

/**
* Returns the classifier of the compiled environment.
*
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.
39 changes: 36 additions & 3 deletions bindings/java/tools/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,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 +50,11 @@ def get_cargo_artifact_name(classifier: str) -> str:
return "opendal_java.dll"
raise Exception(f"Unsupported classifier: {classifier}")

def get_static_artifact_name(classifier: str) -> str:
if classifier.startswith("windows"):
return "opendal_java.lib"
return "libopendal_java.a"


if __name__ == "__main__":
basedir = Path(__file__).parent.parent
Expand All @@ -68,8 +77,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 +93,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,6 +108,27 @@ 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

if target.endswith("-musl"):
static_artifact = get_static_artifact_name(args.classifier)
src = output / target / profile / static_artifact
artifact = get_cargo_artifact_name(args.classifier)
dst = basedir / "target" / "classes" / "native" / args.classifier / artifact
dst.parent.mkdir(exist_ok=True, parents=True)

link_cmd = [
"musl-gcc",
"-shared",
"-o",
str(dst),
"-Wl,--whole-archive",
str(src),
"-Wl,--no-whole-archive",
]
print("$ " + subprocess.list2cmdline(link_cmd))
subprocess.run(link_cmd, cwd=basedir, check=True)
raise SystemExit(0)
Comment thread
Xuanwo marked this conversation as resolved.
Outdated

artifact = get_cargo_artifact_name(args.classifier)
src = output / target / profile / artifact
dst = basedir / "target" / "classes" / "native" / args.classifier / artifact
Expand Down
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ pub mod services {
pub use opendal_service_tikv::*;
#[cfg(feature = "services-upyun")]
pub use opendal_service_upyun::*;
#[cfg(feature = "services-vercel-artifacts")]
pub use opendal_service_vercel_artifacts::*;
#[cfg(feature = "services-vercel-blob")]
pub use opendal_service_vercel_blob::*;
#[cfg(feature = "services-webdav")]
Expand Down
Loading