Skip to content

Commit

Permalink
Merge pull request #142 from juanmuscaria/crucibleGradle
Browse files Browse the repository at this point in the history
Move CrucibleGradle to Crucible's main repo
  • Loading branch information
juanmuscaria committed Feb 6, 2024
2 parents 8b47865 + edccac9 commit 9edb19e
Show file tree
Hide file tree
Showing 31 changed files with 4,799 additions and 1 deletion.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ buildscript {
}
dependencies {
classpath 'com.anatawa12.forge:ForgeGradle:1.2-1.1.0'
classpath 'io.github.cruciblemc:CrucibleGradle:1.2-SNAPSHOT'
}
}

Expand Down
504 changes: 504 additions & 0 deletions buildSrc/LICENSE

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions buildSrc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# CrucibleGradle
Make ForgeGradle do more

## About
This is Crucible's extension over anatawa12's [ForgeGradle-1.2](https://github.com/anatawa12/ForgeGradle-1.2/).
Here we keep all necessary machinery for building Crucible.

This project still needs a lot of proper cleanup since it was more or less a copy/paste of old Cauldron dev plugin.
A lot of tasks still does not cache properly, and we don't have much of an idea of what is going on certain parts of the
FG workflow.

This is basically the bare minimum to get Crucible to build on modern Gradle.

## TODO
* [ ] Document all tasks/add descriptions
* [ ] Fix caching issues
* [ ] Trim down unneeded tasks
* [ ] Clean up old code and deprecated usages
120 changes: 120 additions & 0 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
plugins {
java
`java-gradle-plugin`
`maven-publish`
}

group = "io.github.cruciblemc"
version = "1.2-SNAPSHOT"

repositories {
mavenCentral()
maven("https://maven.minecraftforge.net/") {
name = "forge"
}
}

dependencies {
// TODO? figure a way to use the runtime dependencies of FG?
implementation("org.ow2.asm:asm:9.4")
implementation("org.ow2.asm:asm-tree:9.4")
implementation("com.google.guava:guava:31.1-jre")
implementation("com.opencsv:opencsv:5.7.0")
implementation("com.cloudbees:diff4j:1.3")
implementation("com.github.abrarsyed.jastyle:jAstyle:1.2")
implementation("net.sf.trove4j:trove4j:2.1.0")
implementation("com.github.jponge:lzma-java:1.3")
implementation("com.nothome:javaxdelta:2.0.1")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.anatawa12.forge:SpecialSource:1.11.1")
implementation("org.apache.httpcomponents:httpclient:4.5.14")
implementation("org.apache.httpcomponents:httpmime:4.5.14")
implementation("de.oceanlabs.mcp:RetroGuard:3.6.6")
implementation("de.oceanlabs.mcp:mcinjector:3.2-SNAPSHOT")
implementation("net.minecraftforge:Srg2Source:4.2.7")

api("com.anatawa12.forge:ForgeGradle:1.2-1.1.0")
testImplementation(platform("org.junit:junit-bom:5.9.1"))
testImplementation("org.junit.jupiter:junit-jupiter")

implementation("com.github.tony19:named-regexp:0.2.3")

// Java 9+ syntax
annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2")
compileOnly("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2")

// Lombok
compileOnly("org.projectlombok:lombok:1.18.26")
annotationProcessor("org.projectlombok:lombok:1.18.26")
}

tasks.test {
useJUnitPlatform()
}

tasks.named("compileJava").configure {
this as JavaCompile
sourceCompatibility = "17" // for the IDE support
options.release.set(8)

javaCompiler.set(
javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(17))
}
)
}

gradlePlugin {
plugins {
create("crucible") {
id = "crucible"
implementationClass = "io.github.cruciblemc.forgegradle.CrucibleDevPlugin"
}
}
}

publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
artifactId = base.archivesName.get()

pom {
name.set(project.base.archivesName.get())
description.set("Gradle plugin for Crucible")
url.set("https://github.com/CrucibleMC/CrucibleGradle")

scm {
url.set("https://github.com/CrucibleMC/CrucibleGradle")
connection.set("scm:git:git://github.com/CrucibleMC/CrucibleGradle.git")
developerConnection.set("scm:git:git@github.com:CrucibleMC/CrucibleGradle.git")
}

issueManagement {
system.set("github")
url.set("https://github.com/CrucibleMC/CrucibleGradle/issues")
}

licenses {
license {
name.set("Lesser GNU Public License, Version 2.1")
url.set("https://www.gnu.org/licenses/lgpl-2.1.html")
distribution.set("repo")
}
}

developers {
developer {
id.set("juanmuscaria")
name.set("juanmuscaria")
}
}
}
}
repositories {
maven(buildDir.absolutePath + "/repo") {
name = "filesystem"
}
}
}
}
2 changes: 2 additions & 0 deletions buildSrc/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rootProject.name = "CrucibleGradle"

88 changes: 88 additions & 0 deletions buildSrc/src/main/java/com/juanmuscaria/uncode/ASMCodeRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.juanmuscaria.uncode;

import com.google.common.io.ByteStreams;
import com.juanmuscaria.uncode.cleaners.ClassCleaner;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ASMCodeRemover {

private static final Logger logger
= LoggerFactory.getLogger(ASMCodeRemover.class);

/**
* Removes all the code, assets, and private elements from given jar,
* keeping only public classes, methods and fields with no code body.
*
* @param jarFile the jar file to process
* @param outputFile the output file
* @param overwrite if the output file should be overwritten if it already exists
* @return a map with jarEntry-reason for all entries from the input jar that where removed (resources and class files)
* @throws IOException if an I/O error occurs
*/
public static Map<String, String> removeContent(Path jarFile, Path outputFile, boolean overwrite) throws IOException {
if (!Files.exists(jarFile)) {
throw new IllegalArgumentException("Input file does not exist");
} else if (!Files.isReadable(jarFile)) {
throw new IllegalArgumentException("Input file is not readable");
} else if (Files.exists(outputFile) && !overwrite) {
throw new IllegalArgumentException("Output file already exists");
}

var failedEntries = new LinkedHashMap<String, String>();
try (var out = new ZipOutputStream(Files.newOutputStream(outputFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING))) {
try (var zip = new ZipFile(jarFile.toFile())) {
Collections.list(zip.entries()).iterator().forEachRemaining(entry -> {
if (entry.getName().endsWith(".class")) {
try {
var classBytes = processClass(ByteStreams.toByteArray(zip.getInputStream(entry)));
var newEntry = new ZipEntry(entry.getName());
out.putNextEntry(newEntry);
out.write(classBytes);
out.closeEntry();
} catch (Exception e) {
failedEntries.put(entry.getName(), e.getMessage());
logger.debug("Failed to process class: {}", entry.getName());
logger.debug("Exception:", e);
}
} else if (!entry.getName().endsWith("/")) {
failedEntries.put(entry.getName(), "Not a class file");
}
});
} catch (ZipException e) {
throw new IllegalArgumentException("Input file is corrupted or not a valid jar file: " + e.getMessage(), e);
}
}

return failedEntries;
}

/**
* Removes all the code from a class, keeping only its public members without any code body.
*
* @param classBytes input class bytes
* @return the processed class bytes
* @throws IllegalArgumentException if the class is not readable by the current ASM version,
* if the class is synthetic or if the class is not public
*/
public static byte[] processClass(byte[] classBytes) throws IllegalArgumentException {
var classReader = new ClassReader(classBytes);
var classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
classReader.accept(new ClassCleaner(classWriter), 0);
return classWriter.toByteArray();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.juanmuscaria.uncode.cleaners;

import org.objectweb.asm.*;

/**
* Remove most attributes from a class, leaving only public methods without a body, public fields and inner classes.
* All synthetic members are removed, classes produced by this is not intended to by loaded by the jvm.
*/
public class ClassCleaner extends ClassVisitor {

public ClassCleaner(ClassVisitor cv) {
super(Opcodes.ASM9, cv);
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// We only want public classes
if ((access & Opcodes.ACC_PUBLIC) == 0) {
throw new IllegalArgumentException("Class is not public, skipping");
} else if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
throw new IllegalArgumentException("Class is synthetic (compiler generated), skipping");
}
super.visit(version, access, name, signature, superName, interfaces);
}

@Override
public void visitSource(String source, String debug) {
// Warns this class was touched by the code killer
super.visitSource("uncoded", null);
}

@Override
public ModuleVisitor visitModule(String name, int access, String version) {
// NO-OP - Remove element
return null;
}

@Override
public void visitNestHost(String nestHost) {
// NO-OP - Remove element
}

@Override
public void visitOuterClass(String owner, String name, String descriptor) {
super.visitOuterClass(owner, name, descriptor);
}

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return super.visitAnnotation(descriptor, visible);
}

@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
}

@Override
public void visitAttribute(Attribute attribute) {
// NO-OP - Remove element
}

@Override
public void visitNestMember(String nestMember) {
// NO-OP - Remove element
}

@Override
public void visitPermittedSubclass(String permittedSubclass) {
// NO-OP - Remove element
}

@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// We only want public classes
if ((access & Opcodes.ACC_PUBLIC) == 0) {
return;
} else if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
return;
}
super.visitInnerClass(name, outerName, innerName, access);
}

@Override
public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
// NO-OP - Remove element
return null;
}

@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
// We only want public fields
if ((access & Opcodes.ACC_PUBLIC) == 0) {
return null;
} else if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
return null;
}
// Keep the initial value if it's a constant
return new FieldCleaner(super.visitField(access, name, descriptor, signature, (access & Opcodes.ACC_STATIC) != 0 ? value : null));
}

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
// We only want public methods
if ((access & Opcodes.ACC_PUBLIC) == 0) {
return null;
} else if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
return null;
}
return new MethodCleaner(super.visitMethod(access, name, descriptor, signature, exceptions), name, descriptor);
}

@Override
public void visitEnd() {
super.visitEnd();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.juanmuscaria.uncode.cleaners;

import org.objectweb.asm.*;

/**
* Cleans annotation and attributes from a field.
*/
public class FieldCleaner extends FieldVisitor {

public FieldCleaner(FieldVisitor fieldVisitor) {
super(Opcodes.ASM9, fieldVisitor);
}

@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return super.visitAnnotation(descriptor, visible);
}

@Override
public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
}

@Override
public void visitAttribute(Attribute attribute) {
// NO-OP
}

@Override
public void visitEnd() {
super.visitEnd();
}
}
Loading

0 comments on commit 9edb19e

Please sign in to comment.