diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index a16ac8a89..ae047284c 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -194,10 +194,10 @@ extension FFMSwift2JavaGenerator { @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = initializeLibs(); static boolean initializeLibs() { - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE); - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); - System.loadLibrary(LIB_NAME); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_CORE); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); return true; } @@ -345,10 +345,10 @@ extension FFMSwift2JavaGenerator { static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { if (SwiftLibraries.AUTO_LOAD_LIBS) { - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE); - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_JAVA); - System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); - System.loadLibrary(LIB_NAME); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_CORE); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_JAVA); + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + SwiftLibraries.loadLibraryWithFallbacks(LIB_NAME); } if (PlatformUtils.isMacOS()) { diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java index 2a138a408..b20437e8c 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/SwiftLibraries.java @@ -41,39 +41,138 @@ public final class SwiftLibraries { : Boolean.getBoolean("swiftkit.auto-load-libraries"); @SuppressWarnings("unused") - private static final boolean INITIALIZED_LIBS = loadLibraries(false); + private static final boolean INITIALIZED_LIBS = AUTO_LOAD_LIBS ? loadLibraries(false) : true; public static boolean loadLibraries(boolean loadSwiftRuntimeFunctions) { - System.loadLibrary(LIB_NAME_SWIFT_CORE); - System.loadLibrary(LIB_NAME_SWIFT_JAVA); - if (loadSwiftRuntimeFunctions) { - System.loadLibrary(LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + try { + loadLibraryWithFallbacks(LIB_NAME_SWIFT_CORE); + loadLibraryWithFallbacks(LIB_NAME_SWIFT_JAVA); + if (loadSwiftRuntimeFunctions) { + loadLibraryWithFallbacks(LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + } + return true; + } catch (RuntimeException e) { + // Libraries could not be loaded + if (CallTraces.TRACE_DOWNCALLS) { + System.err.println("[swift-java] Could not load libraries: " + e.getMessage()); + System.err.println("[swift-java] Libraries will need to be loaded explicitly or from JAR resources"); + } + return false; } - return true; } // ==== ------------------------------------------------------------------------------------------------------------ // Loading libraries - public static void loadResourceLibrary(String libname) { - String resourceName = PlatformUtils.dynamicLibraryName(libname); - if (CallTraces.TRACE_DOWNCALLS) { - System.out.println("[swift-java] Loading resource library: " + resourceName); + /** + * Returns the platform-specific system path to the swiftCore library. + * + * @return Full path to swiftCore on the system, or null if platform is not macOS or Linux + */ + public static String libSwiftCorePath() { + if (PlatformUtils.isMacOS()) { + return "/usr/lib/swift/libswiftCore.dylib"; + } else if (PlatformUtils.isLinux()) { + return "/usr/lib/swift/linux/libswiftCore.so"; + } + return null; + } + + /** + * Attempts to load a library using multiple fallback strategies with nice error reporting. + * Tries in order: java.library.path, JAR resources, system path (for swiftCore only). + * + * @param libname The library name to load + * @throws RuntimeException if all loading strategies fail + */ + public static void loadLibraryWithFallbacks(String libname) { + // Try 1: Load from java.library.path + try { + System.loadLibrary(libname); + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loaded " + libname + " from java.library.path"); + } + return; + } catch (Throwable e) { + if (CallTraces.TRACE_DOWNCALLS) { + System.err.println("[swift-java] Failed to load " + libname + " from java.library.path: " + e.getMessage()); + } } - try (InputStream libInputStream = SwiftLibraries.class.getResourceAsStream("/" + resourceName)) { - if (libInputStream == null) { - throw new RuntimeException("Expected library '" + libname + "' ('" + resourceName + "') was not found as resource!"); + // Try 2: Load from JAR resources + try { + loadResourceLibrary(libname); + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loaded " + libname + " from JAR resources"); + } + return; + } catch (Throwable e2) { + if (CallTraces.TRACE_DOWNCALLS) { + System.err.println("[swift-java] Failed to load " + libname + " from JAR: " + e2.getMessage()); } - // TODO: we could do an in memory file system here - File tempFile = File.createTempFile(libname, ""); - tempFile.deleteOnExit(); - Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + // Try 3: For swiftCore only, try system path + if (libname.equals(LIB_NAME_SWIFT_CORE)) { + String systemPath = libSwiftCorePath(); + if (systemPath != null) { + try { + System.load(systemPath); + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loaded " + libname + " from system path: " + systemPath); + } + return; + } catch (Throwable e3) { + throw new RuntimeException( + "Failed to load " + libname + " from java.library.path, JAR resources, and system path (" + systemPath + ")", + e3 + ); + } + } else { + if (CallTraces.TRACE_DOWNCALLS) { + System.err.println("[swift-java] System path not available on this platform"); + } + } + } - System.load(tempFile.getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException("Failed to load dynamic library '" + libname + "' ('" + resourceName + "') as resource!", e); + throw new RuntimeException("Failed to load " + libname + " from java.library.path and JAR resources", e2); + } + } + + // Cache of already-loaded libraries to prevent duplicate extraction + private static final java.util.concurrent.ConcurrentHashMap loadedLibraries = new java.util.concurrent.ConcurrentHashMap<>(); + + public static void loadResourceLibrary(String libname) { + loadedLibraries.computeIfAbsent(libname, key -> { + String resourceName = PlatformUtils.dynamicLibraryName(key); + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loading resource library: " + resourceName); + } + + try (InputStream libInputStream = SwiftLibraries.class.getResourceAsStream("/" + resourceName)) { + if (libInputStream == null) { + throw new RuntimeException("Expected library '" + key + "' ('" + resourceName + "') was not found as resource!"); + } + + // TODO: we could do an in memory file system here + // Extract to temp file + File tempFile = File.createTempFile(key, ""); + tempFile.deleteOnExit(); + Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + System.load(tempFile.getAbsolutePath()); + + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loaded and cached library: " + key + " from " + tempFile.getAbsolutePath()); + } + + return tempFile; + } catch (IOException e) { + throw new RuntimeException("Failed to load dynamic library '" + key + "' ('" + resourceName + "') as resource!", e); + } + }); + + if (CallTraces.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Library already loaded from cache: " + libname); } } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java index 74a270c0a..961f2324b 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkit/ffm/SwiftRuntime.java @@ -16,6 +16,7 @@ import org.swift.swiftkit.core.SwiftInstance; import org.swift.swiftkit.core.CallTraces; +import org.swift.swiftkit.core.SwiftLibraries; import org.swift.swiftkit.core.util.PlatformUtils; import org.swift.swiftkit.ffm.SwiftRuntime.swiftjava; @@ -32,22 +33,26 @@ public class SwiftRuntime { - public static final String STDLIB_DYLIB_NAME = "swiftCore"; - public static final String SWIFT_RUNTIME_FUNCTIONS_DYLIB_NAME = "SwiftRuntimeFunctions"; - - private static final String STDLIB_MACOS_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; - private static final Arena LIBRARY_ARENA = Arena.ofAuto(); @SuppressWarnings("unused") private static final boolean INITIALIZED_LIBS = loadLibraries(false); public static boolean loadLibraries(boolean loadSwiftRuntimeFunctions) { - System.loadLibrary(STDLIB_DYLIB_NAME); - if (loadSwiftRuntimeFunctions) { - System.loadLibrary(SWIFT_RUNTIME_FUNCTIONS_DYLIB_NAME); + try { + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_CORE); + if (loadSwiftRuntimeFunctions) { + SwiftLibraries.loadLibraryWithFallbacks(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS); + } + return true; + } catch (RuntimeException e) { + // Libraries could not be loaded + if (CallTraces.TRACE_DOWNCALLS) { + System.err.println("[swift-java] SwiftRuntime: Could not load libraries: " + e.getMessage()); + System.err.println("[swift-java] Libraries will need to be loaded explicitly or from JAR resources"); + } + return false; } - return true; } static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); @@ -55,7 +60,7 @@ public static boolean loadLibraries(boolean loadSwiftRuntimeFunctions) { private static SymbolLookup getSymbolLookup() { if (PlatformUtils.isMacOS()) { // On Apple platforms we need to lookup using the complete path - return SymbolLookup.libraryLookup(STDLIB_MACOS_DYLIB_PATH, LIBRARY_ARENA) + return SymbolLookup.libraryLookup(SwiftLibraries.libSwiftCorePath(), LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); } else {