From aa66b2f73a0b3a340059eac7916a22ef32a3cdb7 Mon Sep 17 00:00:00 2001 From: RaphiMC <50594595+RaphiMC@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:53:18 +0200 Subject: [PATCH] Implemented gradle plugin and cleaned up code Release 1.0.0 --- README.md | 41 ++++ build.gradle | 8 + gradle-plugin/build.gradle | 5 + gradle-plugin/gradle.properties | 1 + .../gradle/task/DowngradeJarTask.java | 187 ++++++++++++++++++ .../gradle/task/DowngradeSourceSetTask.java | 141 +++++++++++++ gradle.properties | 2 +- impl-classtransform/build.gradle | 5 + impl-classtransform/gradle.properties | 1 + .../JavaDowngraderTransformer.java | 24 ++- .../classprovider}/AbstractClassProvider.java | 2 +- .../ClosingFileSystemClassProvider.java | 2 +- .../FileSystemClassProvider.java | 2 +- .../classprovider}/LazyFileClassProvider.java | 10 +- .../classprovider}/PathClassProvider.java | 10 +- .../classtransform/util/ClassNameUtil.java | 23 ++- .../classtransform/util/FileSystemUtil.java | 39 ++++ settings.gradle | 2 + standalone/build.gradle | 11 +- .../javadowngrader/standalone/Main.java | 94 ++++----- .../standalone/util/CloseableSupplier.java | 49 ----- .../standalone/util/GeneralUtil.java | 36 +--- 22 files changed, 532 insertions(+), 163 deletions(-) create mode 100644 gradle-plugin/build.gradle create mode 100644 gradle-plugin/gradle.properties create mode 100644 gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeJarTask.java create mode 100644 gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeSourceSetTask.java create mode 100644 impl-classtransform/build.gradle create mode 100644 impl-classtransform/gradle.properties rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform}/JavaDowngraderTransformer.java (70%) rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider}/AbstractClassProvider.java (96%) rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider}/ClosingFileSystemClassProvider.java (94%) rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider}/FileSystemClassProvider.java (94%) rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider}/LazyFileClassProvider.java (87%) rename {standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider}/PathClassProvider.java (86%) rename standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/Closeable.java => impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/ClassNameUtil.java (57%) create mode 100644 impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/FileSystemUtil.java delete mode 100644 standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/CloseableSupplier.java diff --git a/README.md b/README.md index 755aa5a..b3f5a9e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,47 @@ Here is an example command to convert the jar input.jar to Java 8 and output it ## Usage (As a library) To transform a ``ClassNode`` you can use the ``JavaDowngrader`` class. As a low level class modification framework in your application [ClassTransform](https://github.com/Lenni0451/ClassTransform) is recommended. +JavaDowngrader provides the ``impl-classtransform`` submodule which contains various utility classes for ClassTransform. + +## Usage (In Gradle) +To use JavaDowngrader in gradle (To downgrade a whole jar or one of your source sets) you have to add the following to the top of your build.gradle: +```groovy +buildscript { + repositories { + maven { + name = "Lenni0451 Releases" + url "https://maven.lenni0451.net/releases" + } + } + + dependencies { + classpath "net.raphimc.javadowngrader:gradle-plugin:1.0.0" + } +} +``` + +### Downgrade the main source set +```groovy +tasks.register("java8Main", DowngradeSourceSetTask) { + sourceSet = sourceSets.main +}.get().dependsOn("classes") +classes.finalizedBy("java8Main") +``` + +### Downgrade the built jar (If you use Java 8+ libraries) +```groovy +tasks.register("java8Jar", DowngradeJarTask) { + input = tasks.jar.archiveFile.get().asFile + outputSuffix = "+java8" + compileClassPath = sourceSets.main.compileClasspath +}.get().dependsOn("build") +build.finalizedBy("java8Jar") +``` + +Some of the optional properties include: +- ``targetVersion``: The target classfile version (Default: 8) +- ``outputSuffix``: The suffix to append to the output jar file (Default: "-downgraded") +- ``copyRuntimeClasses``: Whether to copy the JavaDowngrader runtime classes to the output jar (Default: true). Should be set to false if your jar already contains JavaDowngrader itself ## Contact If you encounter any issues, please report them on the diff --git a/build.gradle b/build.gradle index e1fc53c..723a160 100644 --- a/build.gradle +++ b/build.gradle @@ -14,6 +14,14 @@ allprojects { repositories { mavenCentral() + maven { + name = "Lenni0451 Releases" + url "https://maven.lenni0451.net/releases" + } + maven { + name = "Lenni0451 Snapshots" + url "https://maven.lenni0451.net/snapshots" + } } java { diff --git a/gradle-plugin/build.gradle b/gradle-plugin/build.gradle new file mode 100644 index 0000000..40160c6 --- /dev/null +++ b/gradle-plugin/build.gradle @@ -0,0 +1,5 @@ +dependencies { + api project(":impl-classtransform") + + implementation gradleApi() +} diff --git a/gradle-plugin/gradle.properties b/gradle-plugin/gradle.properties new file mode 100644 index 0000000..dfa687a --- /dev/null +++ b/gradle-plugin/gradle.properties @@ -0,0 +1 @@ +maven_name=gradle-plugin diff --git a/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeJarTask.java b/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeJarTask.java new file mode 100644 index 0000000..82d89e7 --- /dev/null +++ b/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeJarTask.java @@ -0,0 +1,187 @@ +/* + * This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 . + */ +package net.raphimc.javadowngrader.gradle.task; + +import net.lenni0451.classtransform.TransformerManager; +import net.lenni0451.classtransform.utils.tree.BasicClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.LazyFileClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.PathClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil; +import net.raphimc.javadowngrader.impl.classtransform.util.FileSystemUtil; +import net.raphimc.javadowngrader.runtime.RuntimeRoot; +import net.raphimc.javadowngrader.util.Constants; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Objects; +import java.util.stream.Stream; + +public class DowngradeJarTask extends DefaultTask { + + @Internal + private File input; + + @Internal + private String outputSuffix = "-downgraded"; + + @Internal + private FileCollection compileClassPath; + + @Internal + private int targetVersion = Opcodes.V1_8; + + @Internal + private boolean copyRuntimeClasses = true; + + @TaskAction + public void run() throws IOException, URISyntaxException { + Objects.requireNonNull(this.input, "input must be set"); + Objects.requireNonNull(this.outputSuffix, "outputSuffix must be set"); + Objects.requireNonNull(this.compileClassPath, "compileClassPath must be set"); + if (!this.input.exists()) throw new IllegalArgumentException("input does not exist"); + if (!this.input.isFile() || !this.input.getName().endsWith(".jar")) throw new IllegalArgumentException("input is not a jar file"); + + System.out.println("Downgrading jar: " + this.input.getName()); + try (FileSystem inFs = FileSystems.newFileSystem(this.input.toPath(), null)) { + final Path inRoot = inFs.getRootDirectories().iterator().next(); + + final TransformerManager transformerManager = new TransformerManager( + new PathClassProvider(inRoot, new LazyFileClassProvider(this.compileClassPath.getFiles(), new BasicClassProvider())) + ); + transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer( + transformerManager, this.targetVersion, c -> Files.isRegularFile(inRoot.resolve(ClassNameUtil.toClassFilename(c))) + )); + + final String outputName = this.input.getName().substring(0, this.input.getName().length() - 4) + this.outputSuffix; + final File outputFile = new File(this.input.getParentFile(), outputName + ".jar"); + + try (FileSystem outFs = FileSystems.newFileSystem(new URI("jar:" + outputFile.toURI()), Collections.singletonMap("create", "true"))) { + final Path outRoot = outFs.getRootDirectories().iterator().next(); + + // Downgrade classes + try (Stream stream = Files.walk(inRoot)) { + stream.forEach(path -> { + try { + final String relative = ClassNameUtil.slashName(inRoot.relativize(path)); + final Path dest = outRoot.resolve(relative); + if (Files.isDirectory(path)) { + Files.createDirectories(dest); + return; + } + final Path parent = dest.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } + if (!relative.endsWith(".class") || relative.contains("META-INF/versions/")) { + Files.copy(path, dest); + return; + } + final String className = ClassNameUtil.toClassName(relative); + final byte[] bytecode = Files.readAllBytes(path); + final byte[] result; + try { + result = transformerManager.transform(className, bytecode); + } catch (Throwable e) { + throw new RuntimeException("Failed to transform " + className, e); + } + Files.write(dest, result != null ? result : bytecode); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + // Copy runtime classes + if (this.copyRuntimeClasses) { + try (FileSystem runtimeRootFs = FileSystemUtil.getOrCreateFileSystem(RuntimeRoot.class.getResource("").toURI())) { + final Path runtimeRoot = runtimeRootFs.getPath(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE); + try (Stream stream = Files.walk(runtimeRoot)) { + stream.filter(Files::isRegularFile) + .filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT)) + .forEach(path -> { + final String relative = ClassNameUtil.slashName(runtimeRoot.relativize(path)); + final Path dest = outRoot.resolve(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + relative); + try { + Files.createDirectories(dest.getParent()); + Files.copy(path, dest); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } + } + } + } + } + + public File getInput() { + return this.input; + } + + public String getOutputSuffix() { + return this.outputSuffix; + } + + public FileCollection getCompileClassPath() { + return this.compileClassPath; + } + + public int getTargetVersion() { + return this.targetVersion; + } + + public boolean getCopyRuntimeClasses() { + return this.copyRuntimeClasses; + } + + public void setInput(final File input) { + this.input = input; + } + + public void setOutputSuffix(final String outputSuffix) { + this.outputSuffix = outputSuffix; + } + + public void setCompileClassPath(final FileCollection compileClassPath) { + this.compileClassPath = compileClassPath; + } + + public void setTargetVersion(final int targetVersion) { + this.targetVersion = targetVersion; + } + + public void setCopyRuntimeClasses(final boolean copyRuntimeClasses) { + this.copyRuntimeClasses = copyRuntimeClasses; + } + +} diff --git a/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeSourceSetTask.java b/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeSourceSetTask.java new file mode 100644 index 0000000..3d04617 --- /dev/null +++ b/gradle-plugin/src/main/java/net/raphimc/javadowngrader/gradle/task/DowngradeSourceSetTask.java @@ -0,0 +1,141 @@ +/* + * This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 . + */ +package net.raphimc.javadowngrader.gradle.task; + +import net.lenni0451.classtransform.TransformerManager; +import net.lenni0451.classtransform.utils.tree.BasicClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.LazyFileClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.PathClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil; +import net.raphimc.javadowngrader.impl.classtransform.util.FileSystemUtil; +import net.raphimc.javadowngrader.runtime.RuntimeRoot; +import net.raphimc.javadowngrader.util.Constants; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskAction; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; +import java.util.stream.Stream; + +public class DowngradeSourceSetTask extends DefaultTask { + + @Internal + private SourceSet sourceSet; + + @Internal + private int targetVersion = Opcodes.V1_8; + + @Internal + private boolean copyRuntimeClasses = true; + + @TaskAction + public void run() throws IOException, URISyntaxException { + Objects.requireNonNull(this.sourceSet, "sourceSet must be set"); + + for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) { + System.out.println("Downgrading source set: " + this.getProject().getProjectDir().toPath().relativize(classesDir.toPath())); + + final Path inRoot = classesDir.toPath(); + final TransformerManager transformerManager = new TransformerManager( + new PathClassProvider(inRoot, new LazyFileClassProvider(this.sourceSet.getCompileClasspath().getFiles(), new BasicClassProvider())) + ); + transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer( + transformerManager, this.targetVersion, c -> Files.isRegularFile(inRoot.resolve(ClassNameUtil.toClassFilename(c))) + )); + + // Downgrade classes + try (Stream stream = Files.walk(inRoot)) { + stream.forEach(path -> { + try { + final String relative = ClassNameUtil.slashName(inRoot.relativize(path)); + if (!relative.endsWith(".class")) return; + final String className = ClassNameUtil.toClassName(relative); + final byte[] bytecode = Files.readAllBytes(path); + final byte[] result; + try { + result = transformerManager.transform(className, bytecode); + } catch (Throwable e) { + throw new RuntimeException("Failed to transform " + className, e); + } + if (result != null) { + Files.write(path, result); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + // Copy runtime classes + if (this.copyRuntimeClasses) { + try (FileSystem runtimeRootFs = FileSystemUtil.getOrCreateFileSystem(RuntimeRoot.class.getResource("").toURI())) { + final Path runtimeRoot = runtimeRootFs.getPath(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE); + try (Stream stream = Files.walk(runtimeRoot)) { + stream.filter(Files::isRegularFile) + .filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT)) + .forEach(path -> { + final String relative = ClassNameUtil.slashName(runtimeRoot.relativize(path)); + final Path dest = inRoot.resolve(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + relative); + try { + Files.createDirectories(dest.getParent()); + Files.copy(path, dest); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } + } + } + } + + public SourceSet getSourceSet() { + return this.sourceSet; + } + + public int getTargetVersion() { + return this.targetVersion; + } + + public boolean getCopyRuntimeClasses() { + return this.copyRuntimeClasses; + } + + public void setSourceSet(final SourceSet sourceSet) { + this.sourceSet = sourceSet; + } + + public void setTargetVersion(final int targetVersion) { + this.targetVersion = targetVersion; + } + + public void setCopyRuntimeClasses(final boolean copyRuntimeClasses) { + this.copyRuntimeClasses = copyRuntimeClasses; + } + +} diff --git a/gradle.properties b/gradle.properties index 095a8fe..09b9816 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.configureondemand=true maven_group=net.raphimc.javadowngrader maven_name=core -maven_version=1.0.0-SNAPSHOT +maven_version=1.0.0 diff --git a/impl-classtransform/build.gradle b/impl-classtransform/build.gradle new file mode 100644 index 0000000..a49e525 --- /dev/null +++ b/impl-classtransform/build.gradle @@ -0,0 +1,5 @@ +dependencies { + api project(":") + + api "net.lenni0451.classtransform:core:1.11.0-SNAPSHOT" +} diff --git a/impl-classtransform/gradle.properties b/impl-classtransform/gradle.properties new file mode 100644 index 0000000..0b620df --- /dev/null +++ b/impl-classtransform/gradle.properties @@ -0,0 +1 @@ +maven_name=impl-classtransform diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/JavaDowngraderTransformer.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/JavaDowngraderTransformer.java similarity index 70% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/JavaDowngraderTransformer.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/JavaDowngraderTransformer.java index c403efc..3484a56 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/JavaDowngraderTransformer.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/JavaDowngraderTransformer.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform; import net.lenni0451.classtransform.TransformerManager; import net.lenni0451.classtransform.transformer.IBytecodeTransformer; @@ -28,10 +28,32 @@ public class JavaDowngraderTransformer implements IBytecodeTransformer { + public static final int NATIVE_CLASS_VERSION; + + static { + final String classVersion = System.getProperty("java.class.version"); + final String[] versions = classVersion.split("\\."); + final int majorVersion = Integer.parseInt(versions[0]); + final int minorVersion = Integer.parseInt(versions[1]); + NATIVE_CLASS_VERSION = minorVersion << 16 | majorVersion; + } + private final TransformerManager transformerManager; private final int targetVersion; private final Predicate classFilter; + public JavaDowngraderTransformer(final TransformerManager transformerManager) { + this(transformerManager, NATIVE_CLASS_VERSION); + } + + public JavaDowngraderTransformer(final TransformerManager transformerManager, final int targetVersion) { + this(transformerManager, targetVersion, s -> true); + } + + public JavaDowngraderTransformer(final TransformerManager transformerManager, final Predicate classFilter) { + this(transformerManager, NATIVE_CLASS_VERSION, classFilter); + } + public JavaDowngraderTransformer(final TransformerManager transformerManager, final int targetVersion, final Predicate classFilter) { this.transformerManager = transformerManager; this.targetVersion = targetVersion; diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/AbstractClassProvider.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/AbstractClassProvider.java similarity index 96% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/AbstractClassProvider.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/AbstractClassProvider.java index 6f13d0f..9b58ad7 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/AbstractClassProvider.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/AbstractClassProvider.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform.classprovider; import net.lenni0451.classtransform.utils.tree.IClassProvider; diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/ClosingFileSystemClassProvider.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/ClosingFileSystemClassProvider.java similarity index 94% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/ClosingFileSystemClassProvider.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/ClosingFileSystemClassProvider.java index f78f813..16480f9 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/ClosingFileSystemClassProvider.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/ClosingFileSystemClassProvider.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform.classprovider; import net.lenni0451.classtransform.utils.tree.IClassProvider; diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/FileSystemClassProvider.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/FileSystemClassProvider.java similarity index 94% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/FileSystemClassProvider.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/FileSystemClassProvider.java index e54f8df..f990c95 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/FileSystemClassProvider.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/FileSystemClassProvider.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform.classprovider; import net.lenni0451.classtransform.utils.tree.IClassProvider; diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/LazyFileClassProvider.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/LazyFileClassProvider.java similarity index 87% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/LazyFileClassProvider.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/LazyFileClassProvider.java index 6436eba..8c4b9db 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/LazyFileClassProvider.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/LazyFileClassProvider.java @@ -15,19 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform.classprovider; import net.lenni0451.classtransform.utils.tree.IClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.util.FileSystemUtil; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; import java.net.URISyntaxException; -import java.nio.file.FileSystemAlreadyExistsException; -import java.nio.file.FileSystems; import java.util.Collection; -import java.util.Collections; import java.util.NoSuchElementException; public class LazyFileClassProvider extends AbstractClassProvider implements AutoCloseable { @@ -68,9 +66,7 @@ private static PathClassProvider open(final File file) { } try { - return new ClosingFileSystemClassProvider(FileSystems.newFileSystem(uri, Collections.emptyMap()), null); - } catch (FileSystemAlreadyExistsException e) { - return new FileSystemClassProvider(FileSystems.getFileSystem(uri), null); + return new ClosingFileSystemClassProvider(FileSystemUtil.getOrCreateFileSystem(uri), null); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/PathClassProvider.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/PathClassProvider.java similarity index 86% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/PathClassProvider.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/PathClassProvider.java index 9babce7..3f7d6e4 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/transform/PathClassProvider.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/classprovider/PathClassProvider.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.transform; +package net.raphimc.javadowngrader.impl.classtransform.classprovider; import net.lenni0451.classtransform.utils.tree.IClassProvider; -import net.raphimc.javadowngrader.standalone.util.GeneralUtil; +import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil; import java.io.IOException; import java.io.UncheckedIOException; @@ -41,7 +41,7 @@ public PathClassProvider(final Path root, final IClassProvider parent) { @Override public byte[] getClass(String name) { - final Path path = this.root.resolve(GeneralUtil.toClassFilename(name)); + final Path path = this.root.resolve(ClassNameUtil.toClassFilename(name)); if (Files.exists(path)) { try { return Files.readAllBytes(path); @@ -62,7 +62,7 @@ public Map> getAllClasses() { .filter(Files::isRegularFile) .filter(f -> f.getFileName().endsWith(".class")) .collect(Collectors.toMap( - p -> GeneralUtil.toClassName(GeneralUtil.slashName(this.root.relativize(p))), + p -> ClassNameUtil.toClassName(ClassNameUtil.slashName(this.root.relativize(p))), p -> () -> { try { return Files.readAllBytes(p); @@ -78,7 +78,7 @@ public Map> getAllClasses() { } @SafeVarargs - public static Map merge(final Map map, final Map... others) { + private static Map merge(final Map map, final Map... others) { final Map newMap = new HashMap<>(map); for (Map other : others) newMap.putAll(other); return newMap; diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/Closeable.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/ClassNameUtil.java similarity index 57% rename from standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/Closeable.java rename to impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/ClassNameUtil.java index 542780a..437da3d 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/Closeable.java +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/ClassNameUtil.java @@ -15,17 +15,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package net.raphimc.javadowngrader.standalone.util; +package net.raphimc.javadowngrader.impl.classtransform.util; -public interface Closeable extends AutoCloseable { - @Override - void close() throws E; +import net.lenni0451.classtransform.utils.ASMUtils; - static Closeable ofRunnable(Runnable run) { - return run::run; +import java.nio.file.Path; + +public class ClassNameUtil { + + public static String toClassFilename(final String className) { + return ASMUtils.slash(className).concat(".class"); } - static Closeable none() { - return () -> {}; + public static String toClassName(final String classFilename) { + return ASMUtils.dot(classFilename.substring(0, classFilename.length() - 6)); } + + public static String slashName(final Path path) { + return path.toString().replace(path.getFileSystem().getSeparator(), "/"); + } + } diff --git a/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/FileSystemUtil.java b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/FileSystemUtil.java new file mode 100644 index 0000000..f2944cd --- /dev/null +++ b/impl-classtransform/src/main/java/net/raphimc/javadowngrader/impl/classtransform/util/FileSystemUtil.java @@ -0,0 +1,39 @@ +/* + * This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 . + */ +package net.raphimc.javadowngrader.impl.classtransform.util; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.util.Collections; + +public class FileSystemUtil { + + public static FileSystem getOrCreateFileSystem(final URI uri) throws IOException { + FileSystem fileSystem; + try { + fileSystem = FileSystems.getFileSystem(uri); + } catch (FileSystemNotFoundException e) { + fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap()); + } + return fileSystem; + } + +} diff --git a/settings.gradle b/settings.gradle index 3c577fb..f28cc28 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,5 @@ rootProject.name = "JavaDowngrader" include(":standalone") include(":runtime-dep") +include(":impl-classtransform") +include(":gradle-plugin") diff --git a/standalone/build.gradle b/standalone/build.gradle index 64db375..22be540 100644 --- a/standalone/build.gradle +++ b/standalone/build.gradle @@ -10,17 +10,8 @@ configurations { api.extendsFrom include } -repositories { - maven { - name = "Lenni0451 Releases" - url "https://maven.lenni0451.net/releases" - } -} - dependencies { - include project(":") - - include "net.lenni0451.classtransform:core:1.10.1" + include project(":impl-classtransform") include "net.sf.jopt-simple:jopt-simple:5.0.4" diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/Main.java b/standalone/src/main/java/net/raphimc/javadowngrader/standalone/Main.java index 5434396..f66580c 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/Main.java +++ b/standalone/src/main/java/net/raphimc/javadowngrader/standalone/Main.java @@ -22,11 +22,13 @@ import me.tongfei.progressbar.ProgressBarStyle; import net.lenni0451.classtransform.TransformerManager; import net.lenni0451.classtransform.utils.tree.BasicClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.LazyFileClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.classprovider.PathClassProvider; +import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil; +import net.raphimc.javadowngrader.impl.classtransform.util.FileSystemUtil; +import net.raphimc.javadowngrader.runtime.RuntimeRoot; import net.raphimc.javadowngrader.standalone.progress.MultiThreadedProgressBar; -import net.raphimc.javadowngrader.standalone.transform.JavaDowngraderTransformer; -import net.raphimc.javadowngrader.standalone.transform.LazyFileClassProvider; -import net.raphimc.javadowngrader.standalone.transform.PathClassProvider; -import net.raphimc.javadowngrader.standalone.util.CloseableSupplier; import net.raphimc.javadowngrader.standalone.util.GeneralUtil; import net.raphimc.javadowngrader.util.Constants; import org.slf4j.Logger; @@ -76,9 +78,9 @@ public static void main(String[] args) throws Throwable { .withRequiredArg() .withValuesConvertedBy(new PathConverter()); final OptionSpec threadCount = parser.acceptsAll(asList("thread_count", "threads", "t"), "The number of threads to use for the downgrading") - .withRequiredArg() - .ofType(Integer.class) - .defaultsTo(Math.min(Runtime.getRuntime().availableProcessors(), 255)); + .withRequiredArg() + .ofType(Integer.class) + .defaultsTo(Math.min(Runtime.getRuntime().availableProcessors(), 255)); final OptionSet options; try { @@ -117,10 +119,10 @@ public static void main(String[] args) throws Throwable { try { final long start = System.nanoTime(); doConversion( - inputFile, outputFile, - options.valueOf(version), - GeneralUtil.flatten(options.valuesOf(libraryPath)), - Math.min(options.valueOf(threadCount), 255) + inputFile, outputFile, + options.valueOf(version), + GeneralUtil.flatten(options.valuesOf(libraryPath)), + Math.min(options.valueOf(threadCount), 255) ); final long end = System.nanoTime(); LOGGER.info( @@ -176,60 +178,41 @@ private static void doConversion( LOGGER.info("Opening source JAR"); try (FileSystem inFs = FileSystems.newFileSystem(inputFile.toPath(), null)) { final Path inRoot = inFs.getRootDirectories().iterator().next(); + final TransformerManager transformerManager = new TransformerManager( new PathClassProvider(inRoot, new LazyFileClassProvider(libraryPath, new BasicClassProvider())) ); transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer( - transformerManager, targetVersion.getVersion(), - c -> Files.isRegularFile(inRoot.resolve(GeneralUtil.toClassFilename(c))) + transformerManager, targetVersion.getVersion(), c -> Files.isRegularFile(inRoot.resolve(ClassNameUtil.toClassFilename(c))) )); try (FileSystem outFs = FileSystems.newFileSystem(new URI("jar:" + outputFile.toURI()), Collections.singletonMap("create", "true"))) { final Path outRoot = outFs.getRootDirectories().iterator().next(); - LOGGER.info("Copying runtime classes"); - try (CloseableSupplier supplier = GeneralUtil.getPath(Main.class.getResource( - '/' + Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + Constants.JAVADOWNGRADER_RUNTIME_ROOT - ).toURI())) { - final Path runtimeRoot = supplier.get().getParent(); - try (Stream stream = Files.walk(runtimeRoot)) { - stream.filter(Files::isRegularFile) - .filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT)) - .forEach(p -> { - final String relative = GeneralUtil.slashName(runtimeRoot.relativize(p)); - final Path dest = outRoot.resolve(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + relative); - try { - Files.createDirectories(dest.getParent()); - Files.copy(p, dest); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); - } - } + LOGGER.info("Downgrading classes with {} thread(s)", threadCount); final ExecutorService threadPool = Executors.newFixedThreadPool(threadCount); final List> tasks; final MultiThreadedProgressBar[] pb = new MultiThreadedProgressBar[1]; try (Stream stream = Files.walk(inRoot)) { tasks = stream.map(path -> (Callable) () -> { - final String relative = GeneralUtil.slashName(inRoot.relativize(path)); + final String relative = ClassNameUtil.slashName(inRoot.relativize(path)); pb[0].setThreadTask(relative); - final Path inOther = outRoot.resolve(relative); + final Path dest = outRoot.resolve(relative); if (Files.isDirectory(path)) { - Files.createDirectories(inOther); + Files.createDirectories(dest); pb[0].step(); return null; } - final Path parent = inOther.getParent(); + final Path parent = dest.getParent(); if (parent != null) { Files.createDirectories(parent); } - if (!relative.endsWith(".class")) { - Files.copy(path, inOther); + if (!relative.endsWith(".class") || relative.contains("META-INF/versions/")) { + Files.copy(path, dest); pb[0].step(); return null; } - final String className = GeneralUtil.toClassName(relative); + final String className = ClassNameUtil.toClassName(relative); final byte[] bytecode = Files.readAllBytes(path); byte[] result = null; try { @@ -237,7 +220,7 @@ private static void doConversion( } catch (Exception e) { LOGGER.error("Failed to transform {}", className, e); } - Files.write(inOther, result != null ? result : bytecode); + Files.write(dest, result != null ? result : bytecode); pb[0].step(); return null; @@ -245,11 +228,11 @@ private static void doConversion( } try { pb[0] = MultiThreadedProgressBar.create( - new ProgressBarBuilder() - .setTaskName("Downgrading") - .setStyle(ProgressBarStyle.ASCII) - .setInitialMax(tasks.size()) - .setUpdateIntervalMillis(100) + new ProgressBarBuilder() + .setTaskName("Downgrading") + .setStyle(ProgressBarStyle.ASCII) + .setInitialMax(tasks.size()) + .setUpdateIntervalMillis(100) ); threadPool.invokeAll(tasks); } finally { @@ -261,6 +244,25 @@ private static void doConversion( if (!threadPool.awaitTermination(1000, TimeUnit.MILLISECONDS)) { throw new IllegalStateException("Thread pool didn't shutdown correctly"); } + + LOGGER.info("Copying runtime classes"); + try (FileSystem runtimeRootFs = FileSystemUtil.getOrCreateFileSystem(RuntimeRoot.class.getResource("").toURI())) { + final Path runtimeRoot = runtimeRootFs.getPath(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE); + try (Stream stream = Files.walk(runtimeRoot)) { + stream.filter(Files::isRegularFile) + .filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT)) + .forEach(path -> { + final String relative = ClassNameUtil.slashName(runtimeRoot.relativize(path)); + final Path dest = outRoot.resolve(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + relative); + try { + Files.createDirectories(dest.getParent()); + Files.copy(path, dest); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + } LOGGER.info("Writing final JAR"); } } diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/CloseableSupplier.java b/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/CloseableSupplier.java deleted file mode 100644 index 7d74a5d..0000000 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/CloseableSupplier.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader - * Copyright (C) 2023 RK_01/RaphiMC and contributors - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser 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 . - */ -package net.raphimc.javadowngrader.standalone.util; - -import java.util.function.Supplier; - -public interface CloseableSupplier extends Supplier, Closeable { - - static CloseableSupplier of(Supplier supplier, Closeable closeable) { - return new CloseableSupplier() { - @Override - public void close() throws E { - closeable.close(); - } - - @Override - public T get() { - return supplier.get(); - } - }; - } - - static CloseableSupplier of(Supplier supplier) { - return of(supplier, Closeable.none()); - } - - static CloseableSupplier ofValue(T value, Closeable closeable) { - return of(() -> value, closeable); - } - - static CloseableSupplier ofValue(T value) { - return ofValue(value, Closeable.none()); - } -} diff --git a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/GeneralUtil.java b/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/GeneralUtil.java index cb2029c..00c65a0 100644 --- a/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/GeneralUtil.java +++ b/standalone/src/main/java/net/raphimc/javadowngrader/standalone/util/GeneralUtil.java @@ -17,45 +17,15 @@ */ package net.raphimc.javadowngrader.standalone.util; -import net.lenni0451.classtransform.utils.ASMUtils; - -import java.io.IOException; -import java.net.URI; -import java.nio.file.*; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; public class GeneralUtil { + public static List flatten(List> list) { return list.stream() - .flatMap(List::stream) - .collect(Collectors.toList()); + .flatMap(List::stream) + .collect(Collectors.toList()); } - public static String toClassFilename(String className) { - return ASMUtils.slash(className).concat(".class"); - } - - public static String toClassName(String classFilename) { - return ASMUtils.dot(classFilename.substring(0, classFilename.length() - 6)); - } - - public static String slashName(Path path) { - final String separator = path.getFileSystem().getSeparator(); - if (separator.equals("/")) { - return path.toString(); - } - return path.toString().replace(separator, "/"); - } - - @SuppressWarnings("DuplicateExpressions") - public static CloseableSupplier getPath(URI uri) throws IOException { - try { - return CloseableSupplier.ofValue(Paths.get(uri)); - } catch (FileSystemNotFoundException e) { - final FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap()); - return CloseableSupplier.ofValue(Paths.get(uri), fs::close); - } - } }