Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create an MRJ to set the classloader name #978

Merged
merged 4 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 51 additions & 39 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.slf4j.LoggerFactory
buildscript {
dependencies {
classpath 'org.kohsuke:github-api:1.135'
classpath 'com.guardsquare:proguard-gradle:' + (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11) ? '7.4.0-beta02' : '7.1.0')
classpath 'com.guardsquare:proguard-gradle:7.5.0'
}
}

Expand Down Expand Up @@ -59,6 +59,7 @@ sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/legacyJava']
}
java17
}

configurations {
Expand Down Expand Up @@ -164,7 +165,7 @@ shadowJar {
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

// Renaming in the shadow jar task doesnt seem to work, so do it here
task getSat4jAbout(type: Copy) {
tasks.register('getSat4jAbout', Copy) {
dependsOn project.configurations.include
duplicatesStrategy = DuplicatesStrategy.EXCLUDE

Expand All @@ -181,19 +182,21 @@ task getSat4jAbout(type: Copy) {
into layout.buildDirectory.dir("sat4j")
}

task fatJar(type: ShadowJar, dependsOn: getSat4jAbout) {
tasks.register('fatJar', ShadowJar) {
dependsOn getSat4jAbout
from sourceSets.main.output
from project(":minecraft").sourceSets.main.output
from getSat4jAbout.destinationDir
from("LICENSE") {
rename { "${it}_${project.base.archivesName.get()}"}
rename { "${it}_${project.base.archivesName.get()}" }
}

manifest {
attributes (
attributes(
'Main-Class': 'net.fabricmc.loader.impl.launch.server.FabricServerLauncher',
'Fabric-Loom-Remap': 'false',
'Automatic-Module-Name': 'net.fabricmc.loader'
'Automatic-Module-Name': 'net.fabricmc.loader',
'Multi-Release': 'true'
)
}

Expand All @@ -218,28 +221,46 @@ task fatJar(type: ShadowJar, dependsOn: getSat4jAbout) {
outputs.upToDateWhen { false }
}

File proguardFile = file("build/libs/fabric-loader-${version}.jar")
File proguardTmpFile = file("build/tmp/fabric-loader-${version}.jar")

import proguard.gradle.ProGuardTask
task proguardJar(type: ProGuardTask, dependsOn: fatJar) {

tasks.register('proguardJar', ProGuardTask) {
dependsOn fatJar
def classpath = project(":minecraft").configurations.compileClasspath

inputs.files(fatJar, classpath)
outputs.files(proguardFile)
outputs.files(proguardTmpFile)

doFirst {
classpath.resolve().forEach {
libraryjars it
}
}

libraryjars JavaVersion.current().java9Compatible ? "${System.getProperty('java.home')}/jmods" : "${System.getProperty('java.home')}/lib/rt.jar"
def java8 = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
}.get()
libraryjars java8.metadata.installationPath.file("jre/lib/rt.jar")

injars fatJar.archiveFile
outjars proguardFile
outjars proguardTmpFile
configuration file("proguard.conf")
}
build.dependsOn proguardJar

// As proguard does not support MRJ's we must add the MRJ classes to the final jar
// Use a Zip task to not alter the manifest
tasks.register('finalJar', Zip) {
from zipTree(proguardTmpFile)
dependsOn(proguardJar)
into('META-INF/versions/17') {
from sourceSets.java17.output
}
destinationDirectory = file("build/libs")
archiveExtension = "jar"
}

build.dependsOn finalJar

tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
Expand All @@ -252,19 +273,19 @@ sourcesJar {
}

// useful for creating test mod jar
task testJar(type: Jar) {
tasks.register('testJar', Jar) {
archiveClassifier = "test"
from sourceSets.test.output
}

task copyJson() {
tasks.register('copyJson') {
def inJson = file('src/main/resources/fabric-installer.json')
def inLwJson = file('src/main/resources/fabric-installer.launchwrapper.json')

def outJson = file("build/libs/${project.base.archivesName.get()}-${version}.json")
def outLwJson = file("build/libs/${project.base.archivesName.get()}-${version}.launchwrapper.json")

inputs.files (inJson, inLwJson)
inputs.files(inJson, inLwJson)
outputs.files(outJson, outLwJson)

doLast {
Expand All @@ -277,13 +298,7 @@ tasks.build.dependsOn "copyJson"

tasks.withType(JavaCompile).configureEach {
it.options.encoding = "UTF-8"

// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
// We'll use that if it's available, but otherwise we'll use the older option.
if (JavaVersion.current().isJava9Compatible()) {
it.options.release = 8
}
it.options.release = it.name.contains("Java17") ? 17 : 8
}

javadoc {
Expand All @@ -310,7 +325,7 @@ javadoc {
failOnError false
}

task javadocJar(type: Jar) {
tasks.register('javadocJar', Jar) {
dependsOn javadoc
from javadoc.destinationDir
archiveClassifier = 'javadoc'
Expand All @@ -337,33 +352,30 @@ tasks.withType(GenerateModuleMetadata) {
enabled = false
}

def signingEnabled = ENV.SIGNING_SERVER
File proguardFileSigned = null
File signedJar = file("build/libs/fabric-loader-${version}-signed.jar")

if (signingEnabled) {
proguardFileSigned = file("build/libs/fabric-loader-${version}-signed.jar")
remoteSign {
requestUrl = ENV.SIGNING_SERVER
pgpAuthKey = ENV.SIGNING_PGP_KEY
jarAuthKey = ENV.SIGNING_JAR_KEY

remoteSign {
requestUrl = ENV.SIGNING_SERVER
pgpAuthKey = ENV.SIGNING_PGP_KEY
jarAuthKey = ENV.SIGNING_JAR_KEY
useDummyForTesting = ENV.SIGNING_SERVER == null

sign(proguardFile, proguardFileSigned, "proguard").configure {
dependsOn proguardJar
}
sign(finalJar.archiveFile.get().getAsFile(), signedJar, "final").configure {
dependsOn finalJar
}

afterEvaluate {
sign publishing.publications.mavenJava
}
afterEvaluate {
sign publishing.publications.mavenJava
}
}

publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(signingEnabled ? proguardFileSigned : proguardFile) {
builtBy(signingEnabled ? signProguard : proguardJar)
artifact(signedJar) {
builtBy(tasks.signFinal)
classifier = null
}
artifact(sourcesJar)
Expand Down
2 changes: 1 addition & 1 deletion minecraft/minecraft-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ dependencies {
productionRuntimeMods "net.fabricmc.fabric-api:fabric-api:0.89.3+1.20.2:testmod"
}

def loaderJarTask = project(":").tasks.proguardJar
def loaderJarTask = project(":").tasks.finalJar

// This is very far beyond loom's API if you copy this, you're on your own.
task runProductionAutoTestClient(type: JavaExec, dependsOn: [loaderJarTask, remapJar]) {
Expand Down
16 changes: 10 additions & 6 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ pluginManagement {
gradlePluginPortal()
}
}

plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}

rootProject.name='fabric-loader'

if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
throw new UnsupportedOperationException("Fabric Loader requires Java 17+ to build.")
}

include "minecraft"
include "junit"

if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
include "minecraft:minecraft-test"
} else {
println("Minecraft test sub project requires java 17 or higher!")
}
include "minecraft:minecraft-test"
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2016 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.loader.impl.mrj;

import java.security.SecureClassLoader;

public abstract class AbstractSecureClassLoader extends SecureClassLoader {
public AbstractSecureClassLoader(String name, ClassLoader parent) {
super(name, parent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.SecureClassLoader;
import java.util.Enumeration;
import java.util.Objects;

import net.fabricmc.api.EnvType;
import net.fabricmc.loader.impl.game.GameProvider;
import net.fabricmc.loader.impl.launch.knot.KnotClassDelegate.ClassLoaderAccess;
import net.fabricmc.loader.impl.mrj.AbstractSecureClassLoader;

// class name referenced by string constant in net.fabricmc.loader.impl.util.LoaderUtil.verifyNotInTargetCl
final class KnotClassLoader extends SecureClassLoader implements ClassLoaderAccess {
final class KnotClassLoader extends AbstractSecureClassLoader implements ClassLoaderAccess {
private static final class DynamicURLClassLoader extends URLClassLoader {
private DynamicURLClassLoader(URL[] urls) {
super(urls, new DummyClassLoader());
Expand All @@ -51,7 +51,7 @@ public void addURL(URL url) {
private final KnotClassDelegate<KnotClassLoader> delegate;

KnotClassLoader(boolean isDevelopment, EnvType envType, GameProvider provider) {
super(new DynamicURLClassLoader(new URL[0]));
super("knot", new DynamicURLClassLoader(new URL[0]));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps something like 'fabric-knot' would be more clear.

It would be nice to encourage a naming convention that makes it clear which classloader is being specified, AND where it comes from.

Copy link
Contributor

@sfPlayer1 sfPlayer1 Oct 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sort of a feature, calling it anything fabric would just mislead people with their mod issues, while the information itself is mostly useful and actionable for those that mostly tend to know what Knot is.

this.originalLoader = getClass().getClassLoader();
this.urlLoader = (DynamicURLClassLoader) getParent();
this.delegate = new KnotClassDelegate<>(isDevelopment, envType, this, originalLoader, provider);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2016 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.loader.impl.mrj;

import java.security.SecureClassLoader;

public abstract class AbstractSecureClassLoader extends SecureClassLoader {
public AbstractSecureClassLoader(String name, ClassLoader parent) {
super(parent);
}
}
Loading