Skip to content
Open
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
28 changes: 27 additions & 1 deletion log-utils/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,22 @@ version = gitversion.tagOffset

println "Version: $version"

final java11 = sourceSets.create('java11')

Copy link
Member

Choose a reason for hiding this comment

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

SourceSets for multi-release jars dont work in a lot of ides (anything except intellij) so the proper way to do it is to have the java11 code in a sub project.

Copy link
Member

@Jonathing Jonathing Aug 1, 2025

Choose a reason for hiding this comment

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

That is fine. I've seen this done in several different ways so I want to make sure that we just stay consistent moving forward, and I'd like to do it in a way that is as Gradle-friendly as possible.

So, my thinking for the main project is this:

tasks.named('jar', Jar) {
    into('META-INF/versions/11') {
        from project(':log-utils:java11').tasks.named('compileJava')
    }
}

Then, in each of the subprojects, depend on implementation rootProject.

Copy link
Member

Choose a reason for hiding this comment

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

The code block is just a sketch, I'd probably use whatever the cleanest solution is. I'm sure there is a way to reference a subproject with respect to the current project (i.e. subprojects['java-11'] = project(':log-utils:java-11')).

Copy link
Member

Choose a reason for hiding this comment

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

Im fine with whatever way gradle wants it to be done.
I dont know how to do cross project merging.
But because non-intellij (yes, eclipse) IDEs dont separate dependencies/java deps on a sourceset level it is better to split everything into sub-projects.

Similar to how i've split AccessTransformer/ModLauncher/SecureModules (etc) tests into sub projects.

So ya, sub-projects rather then sourcesets if it is a different set of dependencies/java targets.

java {
toolchain.languageVersion = JavaLanguageVersion.of 8
withSourcesJar()

registerFeature(java11.name) {
usingSourceSet java11
capability project.group.toString(), project.name, project.version.toString()
disablePublication()
}
}

configurations {
named(java11.implementationConfigurationName) { extendsFrom implementation }
named(java11.compileOnlyConfigurationName) { extendsFrom compileOnly }
}

dependencies {
Expand All @@ -25,7 +38,10 @@ dependencies {

tasks.named('jar', Jar) {
manifest {
attributes('Automatic-Module-Name': 'net.minecraftforge.utils.logging')
attributes([
'Automatic-Module-Name': 'net.minecraftforge.utils.logging',
'Multi-Release' : 'true'
])
attributes([
'Specification-Title' : projectDisplayName,
'Specification-Vendor' : projectVendor,
Expand All @@ -34,6 +50,16 @@ tasks.named('jar', Jar) {
'Implementation-Vendor' : projectVendor,
'Implementation-Version': project.version
], 'net/minecraftforge/util/logging/')

into('META-INF/versions/11') {
from java11.output
}
}
}

tasks.named(java11.compileJavaTaskName, JavaCompile) {
javaCompiler = javaToolchains.compilerFor {
languageVersion = JavaLanguageVersion.of 11
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.util.logging;

import java.util.Map;

/**
* Utility class for methods that are exclusively used in multi-release form. (Java 11+)
*/
final class MultiReleaseMethods {
static final String INDENT_STRING = " ";

static <K, V> Map.Entry<K, V> mapEntry(K key, V value) {
return Map.entry(key, value);
}

static String getIndentation(byte indent) {
return INDENT_STRING.repeat(indent);
}

private MultiReleaseMethods() { }
}
33 changes: 9 additions & 24 deletions log-utils/src/main/java/net/minecraftforge/util/logging/Log.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

Expand Down Expand Up @@ -88,30 +89,13 @@ public static void pop(byte indent) {
static String getIndentation() {
String ret = INDENT_CACHE[indentLevel];
//noinspection ConstantValue -- IntelliJ skill issue
return ret == null ? INDENT_CACHE[indentLevel] = getIndentation(indentLevel) : ret;
}

private static String getIndentation(byte indent) {
StringBuilder builder = new StringBuilder(INDENT_STRING.length() * indent);
for (int i = 0; i < indent; i++)
builder.append(INDENT_STRING);
return builder.toString();
return ret == null ? INDENT_CACHE[indentLevel] = MultiReleaseMethods.getIndentation(indentLevel) : ret;
}


/* CAPTURING */

private static @UnknownNullability List<CapturedMessage> CAPTURED;

private static final class CapturedMessage {
private final Level level;
private final String message;

private CapturedMessage(Level level, String message) {
this.level = level;
this.message = message;
}
}
private static @UnknownNullability List<Map.Entry<Level, String>> CAPTURED;

/**
* If the log is capturing log messages.
Expand All @@ -136,7 +120,7 @@ public static void capture() {

static void tryCapture(Consumer<String> logger, Level level, String message) {
if (CAPTURED != null)
CAPTURED.add(new CapturedMessage(level, message));
CAPTURED.add(MultiReleaseMethods.mapEntry(level, message));
else
logger.accept(message);
}
Expand All @@ -163,17 +147,18 @@ public static void release() {
/**
* Releases all captured log messages into the given consumer, then stops capturing.
*
* @param consumer The consumer to release the captured log messages to
* @param consumer The consumer to release the captured log messages to. The first input is the {@link Level} of the
* message, the second input is the message itself.
* @see #capture()
*/
public static void release(BiConsumer<Level, String> consumer) {
if (CAPTURED == null) return;

Iterator<CapturedMessage> itor = CAPTURED.iterator();
Iterator<Map.Entry<Level, String>> itor = CAPTURED.iterator();
CAPTURED = null;
while (itor.hasNext()) {
CapturedMessage capture = itor.next();
consumer.accept(capture.level, capture.message);
Map.Entry<Level, String> capture = itor.next();
consumer.accept(capture.getKey(), capture.getValue());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.util.logging;

import java.util.AbstractMap;
import java.util.Map;

/**
* Utility class for methods that are exclusively used in multi-release form. (Java 8)
*/
final class MultiReleaseMethods {
static final String INDENT_STRING = " ";

static <K, V> Map.Entry<K, V> mapEntry(K key, V value) {
return new AbstractMap.SimpleImmutableEntry<>(key, value);
}

static String getIndentation(byte indent) {
StringBuilder builder = new StringBuilder(INDENT_STRING.length() * indent);
for (int i = 0; i < indent; i++) {
builder.append(INDENT_STRING);
}
return builder.toString();
}

private MultiReleaseMethods() { }
}