From 59d607fd6d3afaf460549612f85871cab082a7a2 Mon Sep 17 00:00:00 2001 From: Christian Stein Date: Mon, 13 Jun 2022 12:29:05 +0200 Subject: [PATCH] fix(core) impl version lookup on module path - Package::getImplementationVersion is unspecified when loading a JAR file from the module path. The new multi-release code uses the module descriptor to retrieve the implementation version. - Restored code that retrieves the implementation version from the JAR manifest, as a fallback. Also added the suggested fix from #644, when running on a GraalVM native image. - Changed the LWJGL version string to match the Java version format. Close #770 Co-authored-by: Leon Linhart --- doc/notes/3.3.2.md | 2 + .../core/src/main/java/org/lwjgl/Version.java | 79 ++++++++++++++++++- .../src/main/java/org/lwjgl/VersionImpl.java | 32 ++++++++ .../src/main/java9/org/lwjgl/VersionImpl.java | 51 ++++++++++++ 4 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 modules/lwjgl/core/src/main/java/org/lwjgl/VersionImpl.java create mode 100644 modules/lwjgl/core/src/main/java9/org/lwjgl/VersionImpl.java diff --git a/doc/notes/3.3.2.md b/doc/notes/3.3.2.md index 5ae86b73f5..8d45bd3d42 100644 --- a/doc/notes/3.3.2.md +++ b/doc/notes/3.3.2.md @@ -31,12 +31,14 @@ This build includes the following changes: * The default `Configuration.SHARED_LIBRARY_EXTRACT_DIRECTORY` is now `lwjgl_`. * The default `Configuration.SHARED_LIBRARY_EXTRACT_PATH` now includes the CPU architecture. (`////`) * Added `Configuration.SHARED_LIBRARY_EXTRACT_FORCE`. +- Core: The string returned by `Version::getVersion()` now follows the Java version format (`M.m.r+B` or `M.m.r-snapshot+B`). - The `.sha1` and `.git` files, used for validating LWJGL artifacts, are now stored under the `META-INF` folder. #### Fixes - Core: Fixed Java/native library incompatibility detection. (#737) - Core: Fixed `dlerror` decoding to use UTF-8. (#738) +- Core: Fixed `Version::getVersion()` when LWJGL is in the module path. (#770) - Build: Fixed offline mode with multiple local architectures. (#740) - Opus: Fixed `pcm` parameter type of `opus_decode_float` function. (#785) - Vulkan: Fixed definition of the `VK_API_VERSION_PATCH` macro. (#743) diff --git a/modules/lwjgl/core/src/main/java/org/lwjgl/Version.java b/modules/lwjgl/core/src/main/java/org/lwjgl/Version.java index 14c301fce8..0f53ef980e 100644 --- a/modules/lwjgl/core/src/main/java/org/lwjgl/Version.java +++ b/modules/lwjgl/core/src/main/java/org/lwjgl/Version.java @@ -4,7 +4,11 @@ */ package org.lwjgl; +import javax.annotation.*; +import java.io.*; +import java.net.*; import java.util.*; +import java.util.jar.*; /** This class can be used to query the LWJGL version. */ public final class Version { @@ -18,18 +22,19 @@ public final class Version { /** The development state of the current build. */ public static final BuildType BUILD_TYPE = BuildType.STABLE; - private static final String version = + private static final String versionPlain = String.valueOf(VERSION_MAJOR) + '.' + VERSION_MINOR + - '.' + VERSION_REVISION + BUILD_TYPE.postfix + - ' ' + Optional.ofNullable(Version.class.getPackage().getImplementationVersion()).orElse("SNAPSHOT"); + '.' + VERSION_REVISION + BUILD_TYPE.postfix; + + private static final String version = versionPlain + VersionImpl.find(); private Version() { } public static void main(String[] args) { System.out.println(version); - System.err.println(version.substring(0, version.indexOf(' '))); + System.err.println(versionPlain); } /** Returns the LWJGL version. */ @@ -53,4 +58,70 @@ public enum BuildType { } } + static String createImplementation(String specVersion, String implVersion) { + String build = "+" + (implVersion.startsWith("build ") && 6 < implVersion.length() ? implVersion.substring(6) : implVersion); + + if (specVersion.contains("SNAPSHOT") || specVersion.contains("snapshot")) { + return "-snapshot" + build; + } + + return build; + } + + @Nullable + static String findImplementationFromManifest() { + ClassLoader classLoader = Version.class.getClassLoader(); + + URL url = classLoader.getResource("org/lwjgl/Version.class"); + if (url != null) { + String classURL = url.toString(); + try { + if (classURL.startsWith("jar:")) { // running on standard JDK + URL manifest = Version.class.getResource("/" + JarFile.MANIFEST_NAME); + + String version = readImplementationFromManifest(Objects.requireNonNull(manifest)); + if (version != null) { + return version; + } + } else if (classURL.startsWith("resource:")) { // running on GraalVM native image + Enumeration e = classLoader.getResources(JarFile.MANIFEST_NAME); + while (e.hasMoreElements()) { + String version = readImplementationFromManifest(e.nextElement()); + if (version != null) { + return version; + } + } + } + } catch (Exception ignored) { + } + } + + return null; + } + + @Nullable + private static String readImplementationFromManifest(URL url) { + try (InputStream stream = url.openStream()) { + Attributes attribs = new Manifest(stream).getMainAttributes(); + + // make sure this is the manifest from lwjgl.jar + if (!"lwjgl".equals(attribs.getValue(Attributes.Name.IMPLEMENTATION_TITLE))) { + return null; + } + if (!"lwjgl.org".equals(attribs.getValue(Attributes.Name.IMPLEMENTATION_VENDOR))) { + return null; + } + + String specVersion = attribs.getValue(Attributes.Name.SPECIFICATION_VERSION); + String implVersion = attribs.getValue(Attributes.Name.IMPLEMENTATION_VERSION); + if (specVersion == null || implVersion == null) { + return null; + } + + return createImplementation(specVersion, implVersion); + } catch (Exception ignored) { + return null; + } + } + } \ No newline at end of file diff --git a/modules/lwjgl/core/src/main/java/org/lwjgl/VersionImpl.java b/modules/lwjgl/core/src/main/java/org/lwjgl/VersionImpl.java new file mode 100644 index 0000000000..82154a06a4 --- /dev/null +++ b/modules/lwjgl/core/src/main/java/org/lwjgl/VersionImpl.java @@ -0,0 +1,32 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl; + +/** + * Finds the LWJGL implementation version (build type/number). + * + *

Base implementation for Java 8. When run in the module path, it cannot find the implementation version without reading the JAR manifest. See the + * {@code lwjgl.core9} module for the module-aware implementation.

+ */ +final class VersionImpl { + + static String find() { + Package org_lwjgl = Version.class.getPackage(); + + String specVersion = org_lwjgl.getSpecificationVersion(); + String implVersion = org_lwjgl.getImplementationVersion(); + if (specVersion != null && implVersion != null) { + return Version.createImplementation(specVersion, implVersion); + } + + String version = Version.findImplementationFromManifest(); + if (version != null) { + return version; + } + + return "-snapshot"; + } + +} \ No newline at end of file diff --git a/modules/lwjgl/core/src/main/java9/org/lwjgl/VersionImpl.java b/modules/lwjgl/core/src/main/java9/org/lwjgl/VersionImpl.java new file mode 100644 index 0000000000..361e18f234 --- /dev/null +++ b/modules/lwjgl/core/src/main/java9/org/lwjgl/VersionImpl.java @@ -0,0 +1,51 @@ +/* + * Copyright LWJGL. All rights reserved. + * License terms: https://www.lwjgl.org/license + */ +package org.lwjgl; + +import java.lang.module.*; + +/** + * Finds the LWJGL implementation version (build type/number). + * + *

Module-aware implementation for Java 9 or higher.

+ */ +final class VersionImpl { + + static String find() { + Package org_lwjgl = Version.class.getPackage(); + + String specVersion = org_lwjgl.getSpecificationVersion(); + String implVersion = org_lwjgl.getImplementationVersion(); + if (specVersion != null && implVersion != null) { + return Version.createImplementation(specVersion, implVersion); + } + + Module module = Version.class.getModule(); + if ("org.lwjgl".equals(module.getName())) { + String moduleVersion = module.getDescriptor() + .version() + .map(ModuleDescriptor.Version::toString) + .orElse(null); + + if (moduleVersion != null) { + int plusIndex = moduleVersion.indexOf('+'); + if (plusIndex != -1) { + return Version.createImplementation( + moduleVersion.substring(0, plusIndex), + moduleVersion.substring(plusIndex + 1) + ); + } + } + } + + String version = Version.findImplementationFromManifest(); + if (version != null) { + return version; + } + + return "-snapshot"; + } + +} \ No newline at end of file