Reputation: 968
Goal: Link Java to Swift
Problem: I get an UnsatisfiedLinkError
when trying to load a JNI .dylib
file that is linked with a Swift .dylib
file when calling System#loadLibrary(String)
.
Expected Behavior: The dependency of the Java .dylib
would be automatically loaded or the call to System.loadLibrary("SwiftCode")
would load the dependency (the only solution I could come up with).
Note: I am combining this github tutorial and this Medium article to create my JNI .dylib
file and this tutorial to create my Swift .dylib
file.
Full stacktrace:
Exception in thread "main" java.lang.UnsatisfiedLinkError: /Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib: dlopen(/Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib, 1): Library not loaded: libSwiftCode.dylib
Referenced from: /Users/hillmacbookpro/IdeaProjects/JavaToSwift/src/native/libSwiftHelloWorld.dylib
Reason: image not found
at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2408)
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2465)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2662)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2627)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:829)
at java.base/java.lang.System.loadLibrary(System.java:1833)
at helloworld.SwiftHelloWorld.<clinit>(SwiftHelloWorld.java:7)
File Structure:
src
- helloworld
- SwiftHelloWorld.java
- native
- libSwiftHelloWorld.dylib
- libSwiftCode.dylib
MCVE:
SwiftHelloWorld.java
:
package helloworld;
public class SwiftHelloWorld {
static {
System.loadLibrary("SwiftCode"); // loading SwiftHelloWorld's dependency first
System.loadLibrary("SwiftHelloWorld"); // exception thrown here, can't find dependency?
}
public static native void printHelloWorldImpl();
public static void main(final String[] args) {
printHelloWorldImpl();
}
}
libSwiftHelloWorld.dylib
: Created using these two terminal commands:
export JAVA_HOME="$(/usr/libexec/java_home -v 11)"; gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin/" -o libSwiftHelloWorld.dylib -dynamiclib helloworld_SwiftHelloWorld.c libSwiftCode.dylib
and these files:
helloworld_SwiftHelloWorld.c
:
#include <jni.h>
#include <stdio.h>
#include "helloworld_SwiftHelloWorld.h"
#include "helloworld_SwiftHelloWorld_swift.h"
JNIEXPORT void JNICALL Java_helloworld_SwiftHelloWorld_printHelloWorldImpl
(JNIEnv *env, jclass clazz) {
int result = swiftHelloWorld(42);
printf("%s%i%s", "Hello World from JNI! ", result, "\n");
}
helloworld_SwiftHelloWorld.h
: The auto-generated JNI header from
javac -h native/ helloworld/SwiftHelloWorld.java
helloworld_SwiftHelloWorld_swift.h
:
int swiftHelloWorld(int);
libSwiftCode.dylib
: Created using swiftc SwiftCode.swift -emit-library -o libSwiftCode.dylib
and the SwiftCode.swift
file:
import Foundation
// force the function to have a name callable by the c code
@_silgen_name("swiftHelloWorld")
public func swiftHelloWorld(number: Int) -> Int {
print("Hello world from Swift: \(number)")
return 69
}
Related Questions:
Java JNI linking multiple libraries, he/she gets an UnsatifiedLinkError
but with the discription undefined symbol: ...
(unanswered)
Anything tagged with JNI
and containing UnsatifiedLinkError
Other Notes: I am setting the Java virtual machine's options to -Djava.library.path=src/native
, the parent directory of both of the .dylib
files. I am using macOS.
Edit: I have tried preceding the command to compile SwiftCode.swift
with xcrun
as seen in this article (under "Compile Swift code") but I still get the same error.
Upvotes: 3
Views: 2389
Reputation: 2173
macOS ld
builds a library dependency's path into the binary. The loader loading libSwiftHelloWorld.dylib
will only find libSwiftCode.dylib
if the latter is in the current directory. Loading the dependency in Java doesn't work because for the loader it's a different library.
You can change the built-in path for libSwiftCode.dylib
with the -install_name
argument (i.e. swiftc ... -Xlinker -install_name -Xlinker <your path>
). If you rebuild libSwiftHelloWorld.dylib
afterwards it will reference the path that you gave.
Upvotes: 2