Skip to content

Commit

Permalink
fix(core) impl version lookup on module path
Browse files Browse the repository at this point in the history
- 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 LWJGL#644, when
  running on a GraalVM native image.
- Changed the LWJGL version string to match the Java version format.

Close LWJGL#770

Co-authored-by: Leon Linhart <themrmilchmann@gmail.com>
  • Loading branch information
2 people authored and Spasi committed Aug 24, 2022
1 parent 0bc28ef commit 59d607f
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 4 deletions.
2 changes: 2 additions & 0 deletions doc/notes/3.3.2.md
Expand Up @@ -31,12 +31,14 @@ This build includes the following changes:
* The default `Configuration.SHARED_LIBRARY_EXTRACT_DIRECTORY` is now `lwjgl_<trimmed_user_name>`.
* The default `Configuration.SHARED_LIBRARY_EXTRACT_PATH` now includes the CPU architecture. (`<temp_root>/<extract_dir>/<version>/<arch>/`)
* 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)
Expand Down
79 changes: 75 additions & 4 deletions modules/lwjgl/core/src/main/java/org/lwjgl/Version.java
Expand Up @@ -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 {
Expand All @@ -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. */
Expand All @@ -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<URL> 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;
}
}

}
32 changes: 32 additions & 0 deletions 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).
*
* <p>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.</p>
*/
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";
}

}
51 changes: 51 additions & 0 deletions 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).
*
* <p>Module-aware implementation for Java 9 or higher.</p>
*/
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";
}

}

0 comments on commit 59d607f

Please sign in to comment.