替换对 JDK 内置 Pack200 的依赖,允许在 JDK14 及更高版本上构建项目 (#904)

* fix: Pack200 has been removed

* Automatically add pack200 to class path

* add license header to Pack200Utils.java
This commit is contained in:
Glavo 2021-07-10 22:44:13 +08:00 committed by GitHub
parent e5326a3c4d
commit 8fc1ec7bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 149 additions and 18 deletions

View File

@ -1,9 +1,11 @@
buildscript { buildscript {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
classpath 'org.tukaani:xz:1.8' classpath 'org.tukaani:xz:1.8'
classpath 'org.glavo:pack200:0.3.0'
} }
} }
@ -19,13 +21,13 @@ import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.util.jar.JarFile import java.util.jar.JarFile
import java.util.jar.JarOutputStream import java.util.jar.JarOutputStream
import java.util.jar.Pack200
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import java.util.zip.ZipFile import java.util.zip.ZipFile
import java.nio.file.Files import java.nio.file.Files
import org.tukaani.xz.LZMA2Options import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.XZOutputStream import org.tukaani.xz.XZOutputStream
import org.glavo.pack200.Pack200
def dev = null def dev = null
def shortcommit = System.getenv("GITHUB_SHA")?.toLowerCase()?.substring(0, 7) ?: null def shortcommit = System.getenv("GITHUB_SHA")?.toLowerCase()?.substring(0, 7) ?: null
@ -99,8 +101,10 @@ sourceSets {
} }
compileJava11Java { compileJava11Java {
javaCompiler = javaToolchains.compilerFor { if(JavaVersion.current() < JavaVersion.VERSION_11) {
languageVersion = JavaLanguageVersion.of(11) javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of(11)
}
} }
options.compilerArgs.add('--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED') options.compilerArgs.add('--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED')
sourceCompatibility = 11 sourceCompatibility = 11
@ -120,6 +124,7 @@ shadowJar {
'Main-Class': mainClassName, 'Main-Class': mainClassName,
'Multi-Release': 'true', 'Multi-Release': 'true',
'Implementation-Version': version, 'Implementation-Version': version,
'Class-Path': 'pack200.jar',
'Add-Opens': [ 'Add-Opens': [
'java.base/java.lang', 'java.base/java.lang',
'java.base/java.lang.reflect', 'java.base/java.lang.reflect',
@ -164,8 +169,9 @@ processResources {
def cssFile = new File(this.projectDir, "src/main/resources/" + resource) def cssFile = new File(this.projectDir, "src/main/resources/" + resource)
def bssFile = new File(this.projectDir, "build/compiled-resources/" + resource[0..-4] + "bss") def bssFile = new File(this.projectDir, "build/compiled-resources/" + resource[0..-4] + "bss")
bssFile.parentFile.mkdirs() bssFile.parentFile.mkdirs()
exec { javaexec {
commandLine 'javapackager', '-createbss', '-outdir', bssFile.parent, '-srcfiles', cssFile.path main = "com.sun.javafx.css.parser.Css2Bin"
args = [cssFile, bssFile]
} }
} }
} }

View File

@ -18,6 +18,7 @@
package org.jackhuang.hmcl.upgrade; package org.jackhuang.hmcl.upgrade;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.util.Pack200Utils;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.tukaani.xz.XZInputStream; import org.tukaani.xz.XZInputStream;
@ -26,7 +27,6 @@ import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
class HMCLDownloadTask extends FileDownloadTask { class HMCLDownloadTask extends FileDownloadTask {
@ -52,7 +52,7 @@ class HMCLDownloadTask extends FileDownloadTask {
byte[] raw = Files.readAllBytes(target); byte[] raw = Files.readAllBytes(target);
try (InputStream in = new XZInputStream(new ByteArrayInputStream(raw)); try (InputStream in = new XZInputStream(new ByteArrayInputStream(raw));
JarOutputStream out = new JarOutputStream(Files.newOutputStream(target))) { JarOutputStream out = new JarOutputStream(Files.newOutputStream(target))) {
Pack200.newUnpacker().unpack(in, out); Pack200Utils.unpack(in, out);
} }
break; break;

View File

@ -21,9 +21,9 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
import org.jackhuang.hmcl.util.Pack200Utils;
import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.gson.JsonUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.platform.SystemUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Optional; import java.util.Optional;
@ -38,7 +38,7 @@ public class RemoteVersion {
String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null); String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null);
String packXZUrl = Optional.ofNullable(response.get("packxz")).map(JsonElement::getAsString).orElse(null); String packXZUrl = Optional.ofNullable(response.get("packxz")).map(JsonElement::getAsString).orElse(null);
String packXZHash = Optional.ofNullable(response.get("packxzsha1")).map(JsonElement::getAsString).orElse(null); String packXZHash = Optional.ofNullable(response.get("packxzsha1")).map(JsonElement::getAsString).orElse(null);
if (SystemUtils.JRE_CAPABILITY_PACK200 && packXZUrl != null && packXZHash != null) { if (Pack200Utils.isSupported() && packXZUrl != null && packXZHash != null) {
return new RemoteVersion(version, packXZUrl, Type.PACK_XZ, new IntegrityCheck("SHA-1", packXZHash)); return new RemoteVersion(version, packXZUrl, Type.PACK_XZ, new IntegrityCheck("SHA-1", packXZHash));
} else if (jarUrl != null && jarHash != null) { } else if (jarUrl != null && jarHash != null) {
return new RemoteVersion(version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash)); return new RemoteVersion(version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash));

View File

@ -25,10 +25,10 @@ import org.jackhuang.hmcl.task.DownloadException;
import org.jackhuang.hmcl.task.FileDownloadTask; import org.jackhuang.hmcl.task.FileDownloadTask;
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Pack200Utils;
import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils; import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.platform.SystemUtils;
import org.tukaani.xz.XZInputStream; import org.tukaani.xz.XZInputStream;
import java.io.*; import java.io.*;
@ -38,7 +38,9 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.jar.*; import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.logging.Level; import java.util.logging.Level;
import static org.jackhuang.hmcl.util.DigestUtils.digest; import static org.jackhuang.hmcl.util.DigestUtils.digest;
@ -129,7 +131,7 @@ public class LibraryDownloadTask extends Task<Void> {
} }
} }
if (SystemUtils.JRE_CAPABILITY_PACK200 && testURLExistence(url)) { if (Pack200Utils.isSupported() && testURLExistence(url)) {
List<URL> urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(url + ".pack.xz"); List<URL> urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(url + ".pack.xz");
task = new FileDownloadTask(urls, xzFile, null); task = new FileDownloadTask(urls, xzFile, null);
task.setCacheRepository(cacheRepository); task.setCacheRepository(cacheRepository);
@ -264,7 +266,7 @@ public class LibraryDownloadTask extends Task<Void> {
} }
try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) { try (FileOutputStream jarBytes = new FileOutputStream(dest); JarOutputStream jos = new JarOutputStream(jarBytes)) {
Pack200.newUnpacker().unpack(temp.toFile(), jos); Pack200Utils.unpack(temp.toFile(), jos);
JarEntry checksumsFile = new JarEntry("checksums.sha1"); JarEntry checksumsFile = new JarEntry("checksums.sha1");
checksumsFile.setTime(0L); checksumsFile.setTime(0L);

View File

@ -0,0 +1,123 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.jar.JarOutputStream;
import java.util.logging.Level;
import static org.jackhuang.hmcl.util.Logging.LOG;
/**
* @author Glavo
*/
public final class Pack200Utils {
private Pack200Utils() {
}
private static final String[] IMPL_NAMES = {
"java.util.jar.Pack200",
"org.glavo.pack200.Pack200",
"io.pack200.Pack200"
};
private static final MethodHandle newUnpackerHandle;
private static final MethodHandle unpackHandle;
private static final MethodHandle unpackFileHandle;
static {
Class<?> pack200Class = null;
Class<?> unpackerClass = null;
for (String implName : IMPL_NAMES) {
try {
pack200Class = Class.forName(implName);
unpackerClass = Class.forName(implName + "$Unpacker");
break;
} catch (ClassNotFoundException ignored) {
}
}
if (pack200Class == null) {
LOG.warning("Pack200 not found");
newUnpackerHandle = null;
unpackHandle = null;
unpackFileHandle = null;
} else {
final MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodHandle newUnpacker = null;
MethodHandle unpack = null;
MethodHandle unpackFile = null;
try {
newUnpacker = lookup.findStatic(pack200Class, "newUnpacker", MethodType.methodType(unpackerClass));
unpack = lookup.findVirtual(unpackerClass, "unpack", MethodType.methodType(void.class, InputStream.class, JarOutputStream.class));
unpackFile = lookup.findVirtual(unpackerClass, "unpack", MethodType.methodType(void.class, File.class, JarOutputStream.class));
} catch (Throwable e) {
LOG.log(Level.WARNING, "Failed to find pack200 methods", e);
}
if (newUnpacker != null) {
newUnpackerHandle = newUnpacker;
unpackHandle = unpack;
unpackFileHandle = unpackFile;
} else {
newUnpackerHandle = null;
unpackHandle = null;
unpackFileHandle = null;
}
}
}
public static boolean isSupported() {
return newUnpackerHandle != null;
}
public static void unpack(InputStream in, JarOutputStream out) throws IOException {
if (newUnpackerHandle == null) {
throw new UnsupportedOperationException("Pack200");
}
try {
unpackHandle.invoke(newUnpackerHandle.invoke(), in, out);
} catch (IOException | RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void unpack(File in, JarOutputStream out) throws IOException {
if (newUnpackerHandle == null) {
throw new UnsupportedOperationException("Pack200");
}
try {
unpackFileHandle.invoke(newUnpackerHandle.invoke(), in, out);
} catch (IOException | RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}

View File

@ -17,8 +17,6 @@
*/ */
package org.jackhuang.hmcl.util.platform; package org.jackhuang.hmcl.util.platform;
import org.jackhuang.hmcl.util.Lang;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -28,8 +26,6 @@ import java.util.List;
public final class SystemUtils { public final class SystemUtils {
private SystemUtils() {} private SystemUtils() {}
public static final boolean JRE_CAPABILITY_PACK200 = Lang.test(() -> Class.forName("java.util.jar.Pack200"));
public static int callExternalProcess(String... command) throws IOException, InterruptedException { public static int callExternalProcess(String... command) throws IOException, InterruptedException {
return callExternalProcess(Arrays.asList(command)); return callExternalProcess(Arrays.asList(command));
} }

View File

@ -42,7 +42,7 @@ Simply execute following command:
```bash ```bash
./gradlew clean build ./gradlew clean build
``` ```
Make sure you have Java installed with Pack200 and JavaFX 8 at least. Liberica full JDK 8~11 is recommended. Make sure you have Java installed with JavaFX 8 at least. Liberica full JDK 8~16 is recommended.
## JVM Options (for debugging) ## JVM Options (for debugging)
|Parameter|Description| |Parameter|Description|

View File

@ -25,6 +25,10 @@ subprojects {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
checkstyle {
sourceSets = []
}
sourceCompatibility = 1.8 sourceCompatibility = 1.8
compileJava.options.encoding = "UTF-8" compileJava.options.encoding = "UTF-8"
compileTestJava.options.encoding = "UTF-8" compileTestJava.options.encoding = "UTF-8"