Skip to content
This repository was archived by the owner on May 12, 2024. It is now read-only.

Commit aa66b2f

Browse files
committed
Implemented gradle plugin and cleaned up code
Release 1.0.0
1 parent ebd3c27 commit aa66b2f

22 files changed

Lines changed: 532 additions & 163 deletions

File tree

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,47 @@ Here is an example command to convert the jar input.jar to Java 8 and output it
2929
## Usage (As a library)
3030
To transform a ``ClassNode`` you can use the ``JavaDowngrader`` class.
3131
As a low level class modification framework in your application [ClassTransform](https://github.com/Lenni0451/ClassTransform) is recommended.
32+
JavaDowngrader provides the ``impl-classtransform`` submodule which contains various utility classes for ClassTransform.
33+
34+
## Usage (In Gradle)
35+
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:
36+
```groovy
37+
buildscript {
38+
repositories {
39+
maven {
40+
name = "Lenni0451 Releases"
41+
url "https://maven.lenni0451.net/releases"
42+
}
43+
}
44+
45+
dependencies {
46+
classpath "net.raphimc.javadowngrader:gradle-plugin:1.0.0"
47+
}
48+
}
49+
```
50+
51+
### Downgrade the main source set
52+
```groovy
53+
tasks.register("java8Main", DowngradeSourceSetTask) {
54+
sourceSet = sourceSets.main
55+
}.get().dependsOn("classes")
56+
classes.finalizedBy("java8Main")
57+
```
58+
59+
### Downgrade the built jar (If you use Java 8+ libraries)
60+
```groovy
61+
tasks.register("java8Jar", DowngradeJarTask) {
62+
input = tasks.jar.archiveFile.get().asFile
63+
outputSuffix = "+java8"
64+
compileClassPath = sourceSets.main.compileClasspath
65+
}.get().dependsOn("build")
66+
build.finalizedBy("java8Jar")
67+
```
68+
69+
Some of the optional properties include:
70+
- ``targetVersion``: The target classfile version (Default: 8)
71+
- ``outputSuffix``: The suffix to append to the output jar file (Default: "-downgraded")
72+
- ``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
3273

3374
## Contact
3475
If you encounter any issues, please report them on the

build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ allprojects {
1414

1515
repositories {
1616
mavenCentral()
17+
maven {
18+
name = "Lenni0451 Releases"
19+
url "https://maven.lenni0451.net/releases"
20+
}
21+
maven {
22+
name = "Lenni0451 Snapshots"
23+
url "https://maven.lenni0451.net/snapshots"
24+
}
1725
}
1826

1927
java {

gradle-plugin/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dependencies {
2+
api project(":impl-classtransform")
3+
4+
implementation gradleApi()
5+
}

gradle-plugin/gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
maven_name=gradle-plugin
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* This file is part of JavaDowngrader - https://github.com/RaphiMC/JavaDowngrader
3+
* Copyright (C) 2023 RK_01/RaphiMC and contributors
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package net.raphimc.javadowngrader.gradle.task;
19+
20+
import net.lenni0451.classtransform.TransformerManager;
21+
import net.lenni0451.classtransform.utils.tree.BasicClassProvider;
22+
import net.raphimc.javadowngrader.impl.classtransform.JavaDowngraderTransformer;
23+
import net.raphimc.javadowngrader.impl.classtransform.classprovider.LazyFileClassProvider;
24+
import net.raphimc.javadowngrader.impl.classtransform.classprovider.PathClassProvider;
25+
import net.raphimc.javadowngrader.impl.classtransform.util.ClassNameUtil;
26+
import net.raphimc.javadowngrader.impl.classtransform.util.FileSystemUtil;
27+
import net.raphimc.javadowngrader.runtime.RuntimeRoot;
28+
import net.raphimc.javadowngrader.util.Constants;
29+
import org.gradle.api.DefaultTask;
30+
import org.gradle.api.file.FileCollection;
31+
import org.gradle.api.tasks.Internal;
32+
import org.gradle.api.tasks.TaskAction;
33+
import org.objectweb.asm.Opcodes;
34+
35+
import java.io.File;
36+
import java.io.IOException;
37+
import java.io.UncheckedIOException;
38+
import java.net.URI;
39+
import java.net.URISyntaxException;
40+
import java.nio.file.FileSystem;
41+
import java.nio.file.FileSystems;
42+
import java.nio.file.Files;
43+
import java.nio.file.Path;
44+
import java.util.Collections;
45+
import java.util.Objects;
46+
import java.util.stream.Stream;
47+
48+
public class DowngradeJarTask extends DefaultTask {
49+
50+
@Internal
51+
private File input;
52+
53+
@Internal
54+
private String outputSuffix = "-downgraded";
55+
56+
@Internal
57+
private FileCollection compileClassPath;
58+
59+
@Internal
60+
private int targetVersion = Opcodes.V1_8;
61+
62+
@Internal
63+
private boolean copyRuntimeClasses = true;
64+
65+
@TaskAction
66+
public void run() throws IOException, URISyntaxException {
67+
Objects.requireNonNull(this.input, "input must be set");
68+
Objects.requireNonNull(this.outputSuffix, "outputSuffix must be set");
69+
Objects.requireNonNull(this.compileClassPath, "compileClassPath must be set");
70+
if (!this.input.exists()) throw new IllegalArgumentException("input does not exist");
71+
if (!this.input.isFile() || !this.input.getName().endsWith(".jar")) throw new IllegalArgumentException("input is not a jar file");
72+
73+
System.out.println("Downgrading jar: " + this.input.getName());
74+
try (FileSystem inFs = FileSystems.newFileSystem(this.input.toPath(), null)) {
75+
final Path inRoot = inFs.getRootDirectories().iterator().next();
76+
77+
final TransformerManager transformerManager = new TransformerManager(
78+
new PathClassProvider(inRoot, new LazyFileClassProvider(this.compileClassPath.getFiles(), new BasicClassProvider()))
79+
);
80+
transformerManager.addBytecodeTransformer(new JavaDowngraderTransformer(
81+
transformerManager, this.targetVersion, c -> Files.isRegularFile(inRoot.resolve(ClassNameUtil.toClassFilename(c)))
82+
));
83+
84+
final String outputName = this.input.getName().substring(0, this.input.getName().length() - 4) + this.outputSuffix;
85+
final File outputFile = new File(this.input.getParentFile(), outputName + ".jar");
86+
87+
try (FileSystem outFs = FileSystems.newFileSystem(new URI("jar:" + outputFile.toURI()), Collections.singletonMap("create", "true"))) {
88+
final Path outRoot = outFs.getRootDirectories().iterator().next();
89+
90+
// Downgrade classes
91+
try (Stream<Path> stream = Files.walk(inRoot)) {
92+
stream.forEach(path -> {
93+
try {
94+
final String relative = ClassNameUtil.slashName(inRoot.relativize(path));
95+
final Path dest = outRoot.resolve(relative);
96+
if (Files.isDirectory(path)) {
97+
Files.createDirectories(dest);
98+
return;
99+
}
100+
final Path parent = dest.getParent();
101+
if (parent != null) {
102+
Files.createDirectories(parent);
103+
}
104+
if (!relative.endsWith(".class") || relative.contains("META-INF/versions/")) {
105+
Files.copy(path, dest);
106+
return;
107+
}
108+
final String className = ClassNameUtil.toClassName(relative);
109+
final byte[] bytecode = Files.readAllBytes(path);
110+
final byte[] result;
111+
try {
112+
result = transformerManager.transform(className, bytecode);
113+
} catch (Throwable e) {
114+
throw new RuntimeException("Failed to transform " + className, e);
115+
}
116+
Files.write(dest, result != null ? result : bytecode);
117+
} catch (IOException e) {
118+
throw new UncheckedIOException(e);
119+
}
120+
});
121+
}
122+
123+
// Copy runtime classes
124+
if (this.copyRuntimeClasses) {
125+
try (FileSystem runtimeRootFs = FileSystemUtil.getOrCreateFileSystem(RuntimeRoot.class.getResource("").toURI())) {
126+
final Path runtimeRoot = runtimeRootFs.getPath(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE);
127+
try (Stream<Path> stream = Files.walk(runtimeRoot)) {
128+
stream.filter(Files::isRegularFile)
129+
.filter(p -> !p.getFileName().toString().equals(Constants.JAVADOWNGRADER_RUNTIME_ROOT))
130+
.forEach(path -> {
131+
final String relative = ClassNameUtil.slashName(runtimeRoot.relativize(path));
132+
final Path dest = outRoot.resolve(Constants.JAVADOWNGRADER_RUNTIME_PACKAGE + relative);
133+
try {
134+
Files.createDirectories(dest.getParent());
135+
Files.copy(path, dest);
136+
} catch (IOException e) {
137+
throw new UncheckedIOException(e);
138+
}
139+
});
140+
}
141+
}
142+
}
143+
}
144+
}
145+
}
146+
147+
public File getInput() {
148+
return this.input;
149+
}
150+
151+
public String getOutputSuffix() {
152+
return this.outputSuffix;
153+
}
154+
155+
public FileCollection getCompileClassPath() {
156+
return this.compileClassPath;
157+
}
158+
159+
public int getTargetVersion() {
160+
return this.targetVersion;
161+
}
162+
163+
public boolean getCopyRuntimeClasses() {
164+
return this.copyRuntimeClasses;
165+
}
166+
167+
public void setInput(final File input) {
168+
this.input = input;
169+
}
170+
171+
public void setOutputSuffix(final String outputSuffix) {
172+
this.outputSuffix = outputSuffix;
173+
}
174+
175+
public void setCompileClassPath(final FileCollection compileClassPath) {
176+
this.compileClassPath = compileClassPath;
177+
}
178+
179+
public void setTargetVersion(final int targetVersion) {
180+
this.targetVersion = targetVersion;
181+
}
182+
183+
public void setCopyRuntimeClasses(final boolean copyRuntimeClasses) {
184+
this.copyRuntimeClasses = copyRuntimeClasses;
185+
}
186+
187+
}

0 commit comments

Comments
 (0)