diff --git a/buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts b/buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts index 074110f6..58d2ec29 100644 --- a/buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/flatlaf-publish.gradle.kts @@ -15,10 +15,13 @@ */ +open class NativeArtifact( val fileName: String, val classifier: String, val type: String ) {} + open class PublishExtension { var artifactId: String? = null var name: String? = null var description: String? = null + var nativeArtifacts: List? = null } val extension = project.extensions.create( "flatlafPublish" ) @@ -71,6 +74,15 @@ publishing { url.set( "https://github.com/JFormDesigner/FlatLaf/issues" ) } } + + afterEvaluate { + extension.nativeArtifacts?.forEach { + artifact( artifacts.add( "archives", file( it.fileName ) ) { + classifier = it.classifier + type = it.type + } ) + } + } } } diff --git a/flatlaf-core/build.gradle.kts b/flatlaf-core/build.gradle.kts index 07d059af..4ca329a5 100644 --- a/flatlaf-core/build.gradle.kts +++ b/flatlaf-core/build.gradle.kts @@ -14,6 +14,8 @@ * limitations under the License. */ +import Flatlaf_publish_gradle.NativeArtifact + plugins { `java-library` `flatlaf-toolchain` @@ -123,4 +125,11 @@ flatlafPublish { artifactId = "flatlaf" name = "FlatLaf" description = "Flat Look and Feel" + + val natives = "src/main/resources/com/formdev/flatlaf/natives" + nativeArtifacts = listOf( + NativeArtifact( "${natives}/flatlaf-windows-x86.dll", "windows-x86", "dll" ), + NativeArtifact( "${natives}/flatlaf-windows-x86_64.dll", "windows-x86_64", "dll" ), + NativeArtifact( "${natives}/libflatlaf-linux-x86_64.so", "linux-x86_64", "so" ), + ) } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java index c5c6db14..7fef79b5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatNativeLibrary.java @@ -17,6 +17,7 @@ package com.formdev.flatlaf.ui; import java.io.File; +import java.net.URL; import com.formdev.flatlaf.FlatSystemProperties; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.NativeLibrary; @@ -46,13 +47,13 @@ class FlatNativeLibrary if( nativeLibrary != null ) return; - String libraryName; + String classifier; + String ext; if( SystemInfo.isWindows_10_orLater && (SystemInfo.isX86 || SystemInfo.isX86_64) ) { // Windows: requires Windows 10/11 (x86 or x86_64) - libraryName = "flatlaf-windows-x86"; - if( SystemInfo.isX86_64 ) - libraryName += "_64"; + classifier = SystemInfo.isX86_64 ? "windows-x86_64" : "windows-x86"; + ext = "dll"; // In Java 8, load jawt.dll (part of JRE) explicitly because it // is not found when running application with /bin/java.exe. @@ -64,7 +65,8 @@ class FlatNativeLibrary } else if( SystemInfo.isLinux && SystemInfo.isX86_64 ) { // Linux: requires x86_64 - libraryName = "flatlaf-linux-x86_64"; + classifier = "linux-x86_64"; + ext = "so"; // Load libjawt.so (part of JRE) explicitly because it is not found // in all Java versions/distributions. @@ -76,10 +78,13 @@ class FlatNativeLibrary return; // no native library available for current OS or CPU architecture // load native library - nativeLibrary = createNativeLibrary( libraryName ); + nativeLibrary = createNativeLibrary( classifier, ext ); } - private static NativeLibrary createNativeLibrary( String libraryName ) { + private static NativeLibrary createNativeLibrary( String classifier, String ext ) { + String libraryName = "flatlaf-" + classifier; + + // load from "java.library.path" or from path specified in system property "flatlaf.nativeLibraryPath" String libraryPath = System.getProperty( FlatSystemProperties.NATIVE_LIBRARY_PATH ); if( libraryPath != null ) { if( "system".equals( libraryPath ) ) { @@ -97,9 +102,69 @@ class FlatNativeLibrary } } + // load from beside the FlatLaf jar + // e.g. for flatlaf-3.1.jar, load flatlaf-3.1-windows-x86_64.dll (from same directory) + File libraryFile = findLibraryBesideJar( classifier, ext ); + if( libraryFile != null ) + return new NativeLibrary( libraryFile, true ); + + // load from FlatLaf jar (extract native library to temp folder) return new NativeLibrary( "com/formdev/flatlaf/natives/" + libraryName, null, true ); } + /** + * Search for a native library beside the jar that contains this class + * (usually the FlatLaf jar). + * The native library must be in the same directory (or in "../bin" if jar is in "lib") + * as the jar and have the same basename as the jar. + * If FlatLaf jar is repackaged into fat/uber application jar, "-flatlaf" is appended to jar basename. + * The classifier and the extension are appended to the jar basename. + * + * E.g. + * flatlaf-3.1.jar + * flatlaf-3.1-windows-x86_64.dll + * flatlaf-3.1-linux-x86_64.so + */ + private static File findLibraryBesideJar( String classifier, String ext ) { + try { + // get location of FlatLaf jar + URL jarUrl = FlatNativeLibrary.class.getProtectionDomain().getCodeSource().getLocation(); + if( jarUrl == null ) + return null; + + File jarFile = new File( jarUrl.toURI() ); + + // if jarFile is a directory, then we're in a development environment + if( !jarFile.isFile() ) + return null; + + // build library file + String jarName = jarFile.getName(); + String jarBasename = jarName.substring( 0, jarName.lastIndexOf( '.' ) ); + File parent = jarFile.getParentFile(); + String libraryName = jarBasename + + (jarBasename.contains( "flatlaf" ) ? "" : "-flatlaf") + + '-' + classifier + '.' + ext; + + // check whether native library exists in same directory as jar + File libraryFile = new File( parent, libraryName ); + if( libraryFile.isFile() ) + return libraryFile; + + // if jar is in "lib" directory, then also check whether library exists + // in "../bin" directory + if( parent.getName().equalsIgnoreCase( "lib" ) ) { + libraryFile = new File( parent.getParentFile(), "bin/" + libraryName ); + if( libraryFile.isFile() ) + return libraryFile; + } + } catch( Exception ex ) { + LoggingFacade.INSTANCE.logSevere( ex.getMessage(), ex ); + } + + return null; + } + private static void loadJAWT() { try { System.loadLibrary( "jawt" );