diff --git a/interface/java_binding/.gitignore b/interface/java_binding/.gitignore new file mode 100644 index 0000000000..c3a9b66340 --- /dev/null +++ b/interface/java_binding/.gitignore @@ -0,0 +1,3 @@ +/.project +/.classpath +/.settings diff --git a/interface/java_binding/pom.xml b/interface/java_binding/pom.xml index 5d6536ac04..a63aa3c5f8 100644 --- a/interface/java_binding/pom.xml +++ b/interface/java_binding/pom.xml @@ -1,226 +1,244 @@ - - - 4.0.0 + + + 4.0.0 - - 4.0.0-SNAPSHOT - UTF-8 - 11 - 11 - + + 4.3.3-SNAPSHOT + UTF-8 + 1.8 + 1.8 + - org.khronos - libktx - ${revision} - libktx - Java Bindings for Khronos KTX Library - https://www.khronos.org/ktx/ - - - The Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - Shukant Pal - shukantpal@outlook.com - Khronos Group - http://www.khronos.org - - - - scm:git:git://github.com/KhronosGroup/KTX-Software.git - scm:git:ssh://github.com:KhronosGroup/KTX-Software.git - https://github.com/KhronosGroup/KTX-Software/tree/main - + org.khronos + libktx + ${revision} + libktx + Java Bindings for Khronos KTX Library + https://www.khronos.org/ktx/ + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + Shukant Pal + shukantpal@outlook.com + Khronos Group + http://www.khronos.org + + + + scm:git:git://github.com/KhronosGroup/KTX-Software.git + scm:git:ssh://github.com:KhronosGroup/KTX-Software.git + https://github.com/KhronosGroup/KTX-Software/tree/main + - - - org.junit.jupiter - junit-jupiter - 5.4.0 - test - - + + + org.junit.jupiter + junit-jupiter + 5.4.0 + test + + - - - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.8.0 - - - maven-surefire-plugin - 2.22.1 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - maven-site-plugin - 3.7.1 - - - maven-project-info-reports-plugin - 3.0.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - true - false - forked-path - -Dgpg.passphrase=${gpg.passphrase} - - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.9.5 - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.3 - - - attach-javadocs - - jar - - - - - - - - - - - release-sign-artifacts - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - - + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 3.5.2 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + false + forked-path + -Dgpg.passphrase=${gpg.passphrase} + + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.9.5 + + + - - + + + org.apache.maven.plugins + maven-resources-plugin + + + UTF-8 + + + + + copy-resources + prepare-package + + copy-resources + + + ${project.build.outputDirectory}/lib + + + ${project.basedir}/nativeLibraries/ + false + + **/*.dll + **/*.jnilib + **/*.dylib + **/*.so + + + + + + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.3 + + + attach-javadocs + + jar + + + + + + + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + + + diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/KtxErrorCode.java b/interface/java_binding/src/main/java/org/khronos/ktx/KtxErrorCode.java index a2b99c0f8d..cb7607abf6 100644 --- a/interface/java_binding/src/main/java/org/khronos/ktx/KtxErrorCode.java +++ b/interface/java_binding/src/main/java/org/khronos/ktx/KtxErrorCode.java @@ -11,6 +11,11 @@ */ public class KtxErrorCode { + static + { + KtxLibraryLoader.load(); + } + /** * Operation was successful */ diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/KtxLibraryLoader.java b/interface/java_binding/src/main/java/org/khronos/ktx/KtxLibraryLoader.java new file mode 100644 index 0000000000..52a23e94a8 --- /dev/null +++ b/interface/java_binding/src/main/java/org/khronos/ktx/KtxLibraryLoader.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, Khronos Group and Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +package org.khronos.ktx; + +/** + * Utility class for loading the native KTX library. + */ +class KtxLibraryLoader +{ + /** + * Whether the native library was loaded + */ + private static boolean loaded = false; + + /** + * Try to load the native KTX library.
+ *
+ * This should be called in a static initializer block of any class + * that contains native methods that rely on the native + * library. When such a class is loaded by the ClassLoader, then this + * function will be executed and try to load the native library.
+ *
+ * If the native library cannot be loaded due to an + * UnsatisfiedLinkError, an error message will be printed. + */ + static void load() + { + if (loaded) + { + return; + } + loaded = true; + + // TODO Allow this... ? + // String ktxDir = System.getenv("LIBKTX_BINARY_DIR"); + // if (ktxDir == null) { + // System.out.println("KtxTestLibraryLoader: USING FIXED PATH!"); + // ktxDir = + // "C:/Develop/KhronosGroup/KTX-Software/interface/java_binding/nativeLibraries"; + // //return null; + // } + + try + { + LibUtils.loadLibrary("ktx-jni", "ktx"); + } + catch (UnsatisfiedLinkError e) + { + e.printStackTrace(); + } + } +} diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture.java b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture.java index 892ab3ab21..7c77eeacd7 100644 --- a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture.java +++ b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture.java @@ -20,6 +20,11 @@ */ public abstract class KtxTexture { + static + { + KtxLibraryLoader.load(); + } + /** * The native pointer to the ktxTexture instance, * converted to a 'long' value. diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture1.java b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture1.java index 215cab91cb..bfcdfe5bd7 100644 --- a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture1.java +++ b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture1.java @@ -20,6 +20,11 @@ */ public class KtxTexture1 extends KtxTexture { + static + { + KtxLibraryLoader.load(); + } + /** * Creates a new instance.
*
diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture2.java b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture2.java index ae06b1fb4e..0bbbc5b483 100644 --- a/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture2.java +++ b/interface/java_binding/src/main/java/org/khronos/ktx/KtxTexture2.java @@ -21,6 +21,12 @@ * may be null.
*/ public class KtxTexture2 extends KtxTexture { + + static + { + KtxLibraryLoader.load(); + } + /** * Creates a new instance.
*
diff --git a/interface/java_binding/src/main/java/org/khronos/ktx/LibUtils.java b/interface/java_binding/src/main/java/org/khronos/ktx/LibUtils.java new file mode 100644 index 0000000000..e9aaf34dcb --- /dev/null +++ b/interface/java_binding/src/main/java/org/khronos/ktx/LibUtils.java @@ -0,0 +1,673 @@ +/* + * Copyright (c) 2024, Khronos Group and Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/* This file was originally part of JOCL, from https://github.com/gpu/JOCL, + * as of commit 923eec9ce77b26d9355c864cb1db712889ddb8ec, and is published + * by the author (with minor modifications) as part of KTX-Software, under + * the Apache-2.0 license. + * + * Original license header: + * + * JOCL - Java bindings for OpenCL + * + * Copyright (c) 2009-2015 Marco Hutter - http://www.jocl.org + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.khronos.ktx; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility class for detecting the operating system and architecture + * types, and automatically loading the matching native library + * as a resource or from a file.
+ *
+ * This class is not intended to be used by clients.
+ *
+ */ +public final class LibUtils +{ + // The architecture and OS detection has been adapted from + // http://javablog.co.uk/2007/05/19/making-jni-cross-platform/ + // and extended with http://lopica.sourceforge.net/os.html + + /** + * The logger used in this class + */ + private static final Logger logger = + Logger.getLogger(LibUtils.class.getName()); + + /** + * The default log level + */ + private static final Level level = Level.INFO; + + /** + * The directory where libraries are expected in JAR files, + * when they are loaded as resources + */ + private static final String LIBRARY_PATH_IN_JAR = "/lib"; + + /** + * Enumeration of common operating systems, independent of version + * or architecture. + */ + enum OSType + { + /** + * Android + */ + ANDROID, + + /** + * Apple/MacOS + */ + APPLE, + + /** + * Linux + */ + LINUX, + + /** + * Sun. Solaris, probably... + */ + SUN, + + /** + * Windows + */ + WINDOWS, + + /** + * Unknown + */ + UNKNOWN + } + + /** + * Enumeration of common CPU architectures. + */ + enum ArchType + { + /** + * Power PC (32 bit) + */ + PPC, + + /** + * Power PC, 64 bit + */ + PPC_64, + + /** + * SPARC + */ + SPARC, + + /** + * x86 (32 bit) + */ + X86, + + /** + * x86 (64 bit) + */ + X86_64, + + /** + * ARM (32 bit) + */ + ARM, + + /** + * ARM (64 bit) + */ + ARM64, + + /** + * MIPS (32 bit) + */ + MIPS, + + /** + * MIPS (64 bit) + */ + MIPS64, + + /** + * RISC + */ + RISC, + + /** + * Unknown + */ + UNKNOWN + } + + /** + * Private constructor to prevent instantiation. + */ + private LibUtils() + { + // Private constructor to prevent instantiation. + } + + /** + * Loads the specified library.
+ *
+ * The method will attempt to load the library using the usual + * System.loadLibrary call. In this case, the specified + * dependent libraries are ignored, because they are assumed to be + * loaded automatically in the same way as the main library.
+ *
+ * If the library can not be loaded with the + * System.loadLibrary call, then this method will attempt + * to load the file as a resource (usually one that is contained in + * a JAR file). In this case, the library is assumed to be located + * in subdirectory called "/lib" inside the JAR file. + * The method will try to load a resource that has the platform-specific + * {@link #createLibraryFileName(String) library file name} from + * this directory, extract it into the default directory for temporary + * files, and load the library from there.
+ *
+ * In this case, the specified dependent libraries may also be loaded + * as resources. They are assumed to be located in subdirectories + * that are named according to the {@link #osString()} and + * {@link #archString()} of the executing platform. For example, such + * a library may be located in a directory inside the JAR that is + * called "/lib/windows/x86_64". These dependent libraries + * will be extracted and loaded before the main library is loaded. + * + * @param libraryName The name of the library (without a platform specific + * prefix or file extension) + * @param dependentLibraryNames The names of libraries that the library + * to load depends on. If the library is loaded as a resource, then + * it will be attempted to also load these libraries as resources, as + * described above + * @throws UnsatisfiedLinkError if the native library + * could not be loaded. + */ + public static void loadLibrary( + String libraryName, String ... dependentLibraryNames) + { + logger.log(level, "Loading library: " + libraryName); + + // First, try to load the specified library as a file + // that is visible in the default search path + Throwable throwableFromFile; + try + { + logger.log(level, "Loading library as a file"); + System.loadLibrary(libraryName); + logger.log(level, "Loading library as a file DONE"); + return; + } + catch (Throwable t) + { + logger.log(level, "Loading library as a file FAILED"); + throwableFromFile = t; + } + + // Now try to load the library by extracting the + // corresponding resource from the JAR file + try + { + logger.log(level, "Loading library as a resource"); + loadLibraryResource(LIBRARY_PATH_IN_JAR, + libraryName, "", dependentLibraryNames); + logger.log(level, "Loading library as a resource DONE"); + return; + } + catch (Throwable throwableFromResource) + { + logger.log(level, "Loading library as a resource FAILED", + throwableFromResource); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + pw.println("Error while loading native library \"" + + libraryName + "\""); + pw.println("Operating system name: "+ + System.getProperty("os.name")); + pw.println("Architecture : "+ + System.getProperty("os.arch")); + pw.println("Architecture bit size: "+ + System.getProperty("sun.arch.data.model")); + + pw.println("---(start of nested stack traces)---"); + + pw.println("Stack trace from the attempt to " + + "load the library as a file:"); + throwableFromFile.printStackTrace(pw); + + pw.println("Stack trace from the attempt to " + + "load the library as a resource:"); + throwableFromResource.printStackTrace(pw); + + pw.println("---(end of nested stack traces)---"); + + pw.close(); + throw new UnsatisfiedLinkError(sw.toString()); + } + } + + + + + /** + * Load the library with the given name from a resource. + * + * @param resourceSubdirectoryName The subdirectory where the resource + * is expected + * @param libraryName The library name, e.g. "EXAMPLE-windows-x86" + * @param tempSubdirectoryName The name for the subdirectory in the + * temp directory, where the temporary files for dependent libraries + * should be stored + * @param dependentLibraryNames The names of libraries that the library + * to load depends on, and that may have to be loaded as resources and + * stored as temporary files as well + * @throws Throwable If the library could not be loaded + */ + private static void loadLibraryResource( + String resourceSubdirectoryName, + String libraryName, + String tempSubdirectoryName, + String ... dependentLibraryNames) throws Throwable + { + // First try to load all dependent libraries, recursively + for (String dependentLibraryName : dependentLibraryNames) + { + logger.log(level, + "Library " + libraryName + + " depends on " + dependentLibraryName); + + String dependentResourceSubdirectoryName = + resourceSubdirectoryName + "/" + + osString() + "/" + + archString(); + + String dependentLibraryTempSubDirectoryName = + libraryName+"_dependents" + File.separator + + osString() + File.separator + + archString() + File.separator; + + loadLibraryResource( + dependentResourceSubdirectoryName, + dependentLibraryName, + dependentLibraryTempSubDirectoryName); + } + + // Now, prepare loading the actual library + String libraryFileName = createLibraryFileName(libraryName); + File libraryTempFile = createTempFile( + tempSubdirectoryName, libraryFileName); + + // If the temporary file for the library does not exist, create it + if (!libraryTempFile.exists()) + { + String libraryResourceName = + resourceSubdirectoryName + "/" + libraryFileName; + logger.log(level, + "Writing resource " + libraryResourceName); + logger.log(level, + "to temporary file " + libraryTempFile); + writeResourceToFile(libraryResourceName, libraryTempFile); + } + + // Finally, try to load the library from the temporary file + logger.log(level, "Loading library " + libraryTempFile); + System.load(libraryTempFile.toString()); + logger.log(level, "Loading library " + libraryTempFile + " DONE"); + } + + + /** + * Create a file object representing the file with the given name + * in the specified subdirectory of the default "temp" directory. + * If the specified subdirectory does not exist yet, it is created. + * + * @param tempSubdirectoryName The subdirectory name + * @param name The file name + * @return The file + * @throws IOException If the subdirectory can not be created + */ + private static File createTempFile( + String tempSubdirectoryName, String name) throws IOException + { + String tempDirName = System.getProperty("java.io.tmpdir"); + File tempSubDirectory = + new File(tempDirName + File.separator + tempSubdirectoryName); + if (!tempSubDirectory.exists()) + { + boolean createdDirectory = tempSubDirectory.mkdirs(); + if (!createdDirectory) + { + throw new IOException( + "Could not create directory for temporary file: " + + tempSubDirectory); + } + } + String tempFileName = tempSubDirectory + File.separator + name; + File tempFile = new File(tempFileName); + return tempFile; + } + + + /** + * Obtain an input stream to the resource with the given name, and write + * it to the specified file (which may not be null, and + * may not exist yet) + * + * @param resourceName The name of the resource + * @param file The file to write to + * @throws NullPointerException If the given file is null + * @throws IllegalArgumentException If the given file already exists + * @throws IOException If an IO error occurs + */ + private static void writeResourceToFile( + String resourceName, File file) throws IOException + { + if (file == null) + { + throw new NullPointerException("Target file may not be null"); + } + if (file.exists()) + { + throw new IllegalArgumentException( + "Target file already exists: "+file); + } + InputStream inputStream = + LibUtils.class.getResourceAsStream(resourceName); + if (inputStream == null) + { + throw new IOException( + "No resource found with name '"+resourceName+"'"); + } + OutputStream outputStream = null; + try + { + outputStream = new FileOutputStream(file); + byte[] buffer = new byte[32768]; + while (true) + { + int read = inputStream.read(buffer); + if (read < 0) + { + break; + } + outputStream.write(buffer, 0, read); + } + outputStream.flush(); + } + finally + { + if (outputStream != null) + { + try + { + outputStream.close(); + } + catch (IOException e) + { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + try + { + inputStream.close(); + } + catch (IOException e) + { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + } + + + /** + * Create the full library file name, including the extension + * and prefix, for the given library name. For example, the + * name "EXAMPLE" will become
+ * EXAMPLE.dll on Windows
+ * libEXAMPLE.so on Linux
+ * EXAMPLE.dylib on MacOS
+ * + * @param libraryName The library name + * @return The full library name, with extension + */ + public static String createLibraryFileName(String libraryName) + { + String libPrefix = createLibraryPrefix(); + String libExtension = createLibraryExtension(); + String fullName = libPrefix + libraryName + "." + libExtension; + return fullName; + } + + + /** + * Returns the extension for dynamically linked libraries on the + * current OS. That is, returns "dylib" on Apple, + * "so" on Linux and Sun, and "dll" + * on Windows. + * + * @return The library extension + */ + private static String createLibraryExtension() + { + OSType osType = calculateOS(); + switch (osType) + { + case APPLE: + return "dylib"; + case ANDROID: + case LINUX: + case SUN: + return "so"; + case WINDOWS: + return "dll"; + default: + break; + } + return ""; + } + + /** + * Returns the prefix for dynamically linked libraries on the + * current OS. That is, returns "lib" on Apple, + * Linux and Sun, and the empty String on Windows. + * + * @return The library prefix + */ + private static String createLibraryPrefix() + { + OSType osType = calculateOS(); + switch (osType) + { + case ANDROID: + case APPLE: + case LINUX: + case SUN: + return "lib"; + case WINDOWS: + return ""; + default: + break; + } + return ""; + } + + + /** + * Creates the name for the native library with the given base name for + * the current platform, by appending strings that indicate the current + * operating system and architecture.
+ *
+ * The resulting name will be of the form
+ * baseName-OSType-ArchType
+ * where OSType and ArchType are the lower case Strings + * of the respective {@link LibUtils.OSType OSType} and + * {@link LibUtils.ArchType ArcType} enum constants.
+ *
+ * For example, the library name with the base name "EXAMPLE" may be
+ * EXAMPLE-windows-x86
+ *
+ * Note that the resulting name will not include any platform specific + * prefixes or extensions for the actual name. + * + * @param baseName The base name of the library + * @return The library name + */ + public static String createPlatformLibraryName(String baseName) + { + return baseName + "-" + osString() + "-" + archString(); + } + + /** + * Returns a the lower case String representation of + * the {@link #calculateOS() OSType} of this platform. E.g. + * "windows". + * + * @return The string describing the operating system + */ + private static String osString() + { + OSType osType = calculateOS(); + return osType.toString().toLowerCase(Locale.ENGLISH); + } + + /** + * Returns a the lower case String representation of + * the {@link #calculateArch() ArchType} of this platform. E.g. + * "x86_64". + * + * @return The string describing the architecture + */ + private static String archString() + { + ArchType archType = calculateArch(); + return archType.toString().toLowerCase(Locale.ENGLISH); + } + + /** + * Calculates the current OSType + * + * @return The current OSType + */ + static OSType calculateOS() + { + String vendor = System.getProperty("java.vendor"); + if ("The Android Project".equals(vendor)) + { + return OSType.ANDROID; + } + String osName = System.getProperty("os.name"); + osName = osName.toLowerCase(Locale.ENGLISH); + if (osName.startsWith("mac os")) + { + return OSType.APPLE; + } + if (osName.startsWith("windows")) + { + return OSType.WINDOWS; + } + if (osName.startsWith("linux")) + { + return OSType.LINUX; + } + if (osName.startsWith("sun")) + { + return OSType.SUN; + } + return OSType.UNKNOWN; + } + + + /** + * Calculates the current ARCHType + * + * @return The current ARCHType + */ + private static ArchType calculateArch() + { + String osArch = System.getProperty("os.arch"); + osArch = osArch.toLowerCase(Locale.ENGLISH); + if ("i386".equals(osArch) || + "x86".equals(osArch) || + "i686".equals(osArch)) + { + return ArchType.X86; + } + if (osArch.startsWith("amd64") || osArch.startsWith("x86_64")) + { + return ArchType.X86_64; + } + if (osArch.startsWith("arm64") || osArch.equals("aarch64")) + { + return ArchType.ARM64; + } + if (osArch.startsWith("arm")) + { + return ArchType.ARM; + } + if ("ppc".equals(osArch) || "powerpc".equals(osArch)) + { + return ArchType.PPC; + } + if (osArch.startsWith("ppc")) + { + return ArchType.PPC_64; + } + if (osArch.startsWith("sparc")) + { + return ArchType.SPARC; + } + if (osArch.startsWith("mips64")) + { + return ArchType.MIPS64; + } + if (osArch.startsWith("mips")) + { + return ArchType.MIPS; + } + if (osArch.contains("risc")) + { + return ArchType.RISC; + } + return ArchType.UNKNOWN; + } +} \ No newline at end of file diff --git a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxParallelTest.java b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxParallelTest.java index 2bf96c25b7..c824907751 100644 --- a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxParallelTest.java +++ b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxParallelTest.java @@ -12,7 +12,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -@ExtendWith({ KtxTestLibraryLoader.class }) public class KtxParallelTest { private static final int NUM_THREADS = 2; private static final Logger logger = Logger.getLogger(KtxParallelTest.class.getCanonicalName()); diff --git a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTestLibraryLoader.java b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTestLibraryLoader.java deleted file mode 100644 index 686dd68282..0000000000 --- a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTestLibraryLoader.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2021, Shukant Pal and Contributors - * Copyright (c) 2024, Khronos Group and Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -package org.khronos.ktx.test; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Locale; - -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -/** - * A class that will be used for extending the unit tests, and allow using the - * native ktx and ktx-jni libraries from any - * directory for the tests. - * - * (Usually, this will be a local build output directory) - * - * It will check the LIBKTX_BINARY_DIR environment variable. If this - * environment variable is a directory that contains the KTX JNI library, then - * this library will be loaded. - * - * Otherwise, it will load the KTX JNI library that was installed globally with - * the usual installation procedure. - */ -public class KtxTestLibraryLoader implements BeforeAllCallback, ExtensionContext.Store.CloseableResource { - private static boolean started = false; - - @Override - public void beforeAll(final ExtensionContext context) throws Exception { - - if (started) { - return; - } - started = true; - - String ktxJniLibrary = findKtxJniLibraryName(); - if (ktxJniLibrary != null) { - System.load(ktxJniLibrary); - } else { - System.loadLibrary("ktx-jni"); - } - } - - /** - * Try to find the name (full, absolute path) of the KTX JNI library that should - * be loaded. - * - * This method will search for the library in the directory that is defined via - * the LIBKTX_BINARY_DIR environment variable. If this variable is - * not defined, or no suitable library can be found, then null is - * returned. - * - * @return The KTX JNI library name - */ - private static String findKtxJniLibraryName() { - String ktxDir = System.getenv("LIBKTX_BINARY_DIR"); - if (ktxDir == null) { - return null; - } - - Path ktxPath = Path.of(ktxDir); - if (!ktxPath.isAbsolute() || !Files.exists(ktxPath) || !Files.isDirectory(ktxPath)) { - System.out.println( - "KTXTestLibraryLoader: The value of the LIBKTX_BINARY_DIR environment variable is invalid: " - + ktxDir); - return null; - } - - String expectedKtxJniLibraryName = isRunningOnWindows() ? "ktx-jni" : "libktx-jni"; - - System.out.println("KTXTestLibraryLoader: Loading KTX libraries from " + ktxDir); - File ktxDirFile = new File(ktxDir); - for (File file : ktxDirFile.listFiles()) { - if (!file.isFile()) { - continue; - } - String[] tokens = file.getName().split("\\."); - if (tokens.length == 2 && tokens[0].equals(expectedKtxJniLibraryName)) { - - String ktxJniLibrary = file.getAbsolutePath(); - System.out.println("KTXTestLibraryLoader: Found " + expectedKtxJniLibraryName + " at " + ktxJniLibrary); - return ktxJniLibrary; - } - } - System.out - .println("KTXTestLibraryLoader: Could not find " + expectedKtxJniLibraryName + " in given directory"); - return null; - } - - /** - * Returns whether the os.name system property indicates that the - * operating system is Windows. - * - * @return Whether the operating system is Windows - */ - private static boolean isRunningOnWindows() { - String osName = System.getProperty("os.name"); - osName = osName.toLowerCase(Locale.ENGLISH); - return osName.startsWith("windows"); - } - - @Override - public void close() { - // NOOP - } -} diff --git a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture1Test.java b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture1Test.java index 7b4a69aa94..2a16baf992 100644 --- a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture1Test.java +++ b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture1Test.java @@ -17,7 +17,6 @@ import static org.junit.jupiter.api.Assertions.*; -@ExtendWith({ KtxTestLibraryLoader.class }) public class KtxTexture1Test { @Test public void testCreateFromNamedFile() { diff --git a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture2Test.java b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture2Test.java index db2a1c4602..b99b385504 100644 --- a/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture2Test.java +++ b/interface/java_binding/src/test/java/org/khronos/ktx/test/KtxTexture2Test.java @@ -31,7 +31,6 @@ import org.khronos.ktx.KtxTranscodeFormat; import org.khronos.ktx.VkFormat; -@ExtendWith({ KtxTestLibraryLoader.class }) public class KtxTexture2Test { @Test