getTarget();
+
+ /**
+ * The binary representation of the interfaces to inject.
+ *
+ * Generics are copied verbatim. If you need the generics to reference a class, please use its fully qualified name (e.g. java/util/function/Supplier).
+ *
+ *
+ * @return The interfaces to inject.
+ */
+ @Input
+ @DSLProperty
+ abstract ListProperty getInterfaces();
+}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy
new file mode 100644
index 000000000..52bcbbc1b
--- /dev/null
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy
@@ -0,0 +1,55 @@
+package net.neoforged.gradle.dsl.common.extensions
+
+import groovy.transform.CompileStatic
+import net.minecraftforge.gdi.BaseDSLElementWithFilesAndEntries
+import net.minecraftforge.gdi.annotations.DSLProperty
+import org.gradle.api.Action
+import org.gradle.api.artifacts.ConfigurablePublishArtifact
+import org.gradle.api.artifacts.Dependency
+import org.gradle.api.artifacts.dsl.Dependencies
+import org.gradle.api.artifacts.dsl.DependencyCollector
+import org.gradle.api.file.ConfigurableFileCollection
+
+/**
+ * Defines a DSL extension which allows for the specification of interface injections.
+ */
+@CompileStatic
+interface InterfaceInjections extends Dependencies {
+
+ /**
+ * {@return interface injection files}
+ */
+ @DSLProperty
+ ConfigurableFileCollection getFiles()
+
+ /**
+ * {@return interface injections to add as dependencies}
+ */
+ DependencyCollector getConsume()
+
+ /**
+ * {@return interface injections to add as dependencies and also expose to consumers}
+ */
+ DependencyCollector getConsumeApi()
+
+ /**
+ * Publishes a transitive dependency on the given access transformer in the published interface injections of this component.
+ *
+ * @param dependency to expose to consumers
+ */
+ void expose(Dependency dependency)
+
+ /**
+ * Publishes the provided access transformer as an artifact.
+ *
+ * @param path access transformer file to publish
+ */
+ void expose(Object path)
+
+ /**
+ * Publishes the provided access transformer as an artifact and configures it with the provided action.
+ * @param path access transformer file to publish
+ * @param action configures the published artifact
+ */
+ void expose(Object path, Action action)
+}
\ No newline at end of file
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy
index 7d69592e9..dfb7e0fd4 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy
@@ -47,4 +47,13 @@ interface Minecraft extends BaseDSLElement {
@NotNull
@DSLProperty
AccessTransformers getAccessTransformers();
+
+ /**
+ * Gives access to the interface injections configuration extension.
+ *
+ * @return The interface injections configuration extension.
+ */
+ @NotNull
+ @DSLProperty
+ InterfaceInjections getInterfaceInjections();
}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy
index 97ed9e006..68f27e6ca 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy
@@ -3,7 +3,6 @@ package net.neoforged.gradle.dsl.common.extensions.dependency.replacement
import groovy.transform.CompileStatic
import net.minecraftforge.gdi.BaseDSLElement
import net.minecraftforge.gdi.annotations.DSLProperty
-import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
@@ -34,12 +33,11 @@ interface DependencyReplacement extends BaseDSLElement {
/**
* Optionally converts the given dependency back to the original dependency it replaced.
*
- * @param dependency The dependency to optionally convert back.
* @param configuration The configuration the given dependency can be found it resides in.
* @return The original dependency if it can be converted back, otherwise the given dependency.
*/
@NotNull
- Dependency optionallyConvertBackToOriginal(Dependency dependency, Configuration configuration)
+ Dependency optionallyConvertBackToOriginal(Dependency dependency)
/**
* Invoked when a dependency is replaced.
@@ -57,9 +55,8 @@ interface DependencyReplacement extends BaseDSLElement {
* Invoked when a dependency is replaced.
*
* @param virtualDependency The virtual dependency.
- * @param targetConfiguration The target configuration in which the virtual dependency resides.
* @param originalDependency The original dependency.
*/
- void apply(Dependency virtualDependency, Configuration targetConfiguration, Dependency originalDependency);
+ void apply(Dependency virtualDependency, Dependency originalDependency);
}
}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy
new file mode 100644
index 000000000..193b210c4
--- /dev/null
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy
@@ -0,0 +1,20 @@
+package net.neoforged.gradle.dsl.common.extensions.subsystems
+
+import net.minecraftforge.gdi.BaseDSLElement
+import org.gradle.api.provider.Property
+
+/**
+ * Defines the integration settings for NeoGradle.
+ */
+interface Integration extends BaseDSLElement {
+
+ /**
+ * @return whether the integration is enabled
+ */
+ Property getIsEnabled();
+
+ /**
+ * @return whether to use Gradle problem reporting
+ */
+ Property getUseGradleProblemReporting();
+}
\ No newline at end of file
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy
index c1966251f..1005930f8 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy
@@ -12,6 +12,11 @@ import org.gradle.api.tasks.Nested
@CompileStatic
interface Subsystems extends BaseDSLElement {
+ /**
+ * @return settings for the integration subsystem
+ */
+ Integration getIntegration();
+
/**
* @return settings for the decompiler subsystem
*/
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy
index aba4c5f88..83d5682e2 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy
@@ -10,6 +10,8 @@ import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
+import org.gradle.api.file.RegularFile
+import org.gradle.api.provider.Provider
import org.gradle.api.tasks.TaskProvider
import org.jetbrains.annotations.NotNull
@@ -98,4 +100,50 @@ interface Definition {
*/
@NotNull
ConfigurableFileCollection getAllDependencies()
+
+ /**
+ * A collection of files which need to be added to the recompile classpath,
+ * for the recompile phase to succeed.
+ *
+ * @return The file collection with the additional jars which need to be added
+ */
+ @NotNull
+ FileCollection getAdditionalRecompileDependencies();
+
+ /**
+ * Adds a dependency to the recompile classpath.
+ *
+ * @param dependency The dependency to add.
+ */
+ void additionalRecompileDependency(Provider dependency);
+
+ /**
+ * Adds dependencies to the recompile classpath.
+ *
+ * @param dependencies The dependencies to add.
+ */
+ void additionalRecompileDependencies(FileCollection dependencies);
+
+ /**
+ * A collection of source files which need to be added to the compile sources.
+ * These sources are removed again from the compiled jar.
+ *
+ * @return The file collection with the additional sources which need to be added
+ */
+ @NotNull
+ FileCollection getAdditionalCompileSources();
+
+ /**
+ * Adds a source to the compile sources.
+ *
+ * @param source The source to add.
+ */
+ void additionalCompileSource(Provider source);
+
+ /**
+ * Adds sources to the compile sources.
+ *
+ * @param sources The sources to add.
+ */
+ void additionalCompileSources(FileCollection sources);
}
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy
index 3cd866e85..a4bd93213 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy
@@ -2,12 +2,14 @@ package net.neoforged.gradle.dsl.common.tasks
import groovy.transform.CompileStatic
import net.minecraftforge.gdi.annotations.DSLProperty
+import org.gradle.api.InvalidUserDataException
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import java.nio.file.Files
import java.nio.file.Path
+import java.util.stream.Collectors
@CacheableTask
@CompileStatic
@@ -20,7 +22,16 @@ abstract class ArtifactProvider extends NeoGradleBase implements WithOutput {
@TaskAction
void doProvide() throws Exception {
final Path output = ensureFileWorkspaceReady(getOutput()).toPath();
- final Path source = getInputFiles().getSingleFile().toPath();
+
+ final File inputFile;
+ try {
+ inputFile = getInputFiles().getSingleFile()
+ } catch (final IllegalStateException e) {
+ throw new InvalidUserDataException("There where either none or multiple input files provided. " + getInputFiles().files
+ .stream().map { file -> file.absolutePath }.collect(Collectors.joining()), e)
+ }
+
+ final Path source = inputFile.toPath();
if (!Files.exists(source)) {
throw new IllegalStateException("Source file does not exist: " + source);
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy
index 4e518c877..0791fb963 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy
@@ -1,9 +1,11 @@
package net.neoforged.gradle.dsl.common.tasks.specifications
import net.minecraftforge.gdi.annotations.DSLProperty
+import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
@@ -12,7 +14,6 @@ import org.gradle.api.tasks.OutputFile
*/
trait OutputSpecification implements ProjectSpecification {
-
/**
* The output file of this task as configured.
* If not set, then it is derived from the output file name and the working directory of the task.
@@ -25,7 +26,7 @@ trait OutputSpecification implements ProjectSpecification {
abstract RegularFileProperty getOutput();
/**
- * The name of the output file name for this step.
+ * The name of the output file name for this task.
* Can be left out, if and only if the output is set directly.
*
* @return The name of the output file.
@@ -34,4 +35,15 @@ trait OutputSpecification implements ProjectSpecification {
@Optional
@DSLProperty
abstract Property getOutputFileName();
+
+ /**
+ * The output directory for this step, also doubles as working directory for this task.
+ * Can be left out, if and only if the output is set directly.
+ *
+ * @return The output and working directory for this task.
+ */
+ @Internal
+ @Optional
+ @DSLProperty
+ abstract DirectoryProperty getOutputDirectory();
}
\ No newline at end of file
diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy
index 5a61a5c3d..a511811e2 100644
--- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy
+++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy
@@ -27,7 +27,7 @@ class Constants {
public static final String DEFAULT_PARCHMENT_GROUP = "org.parchmentmc.data"
public static final String DEFAULT_PARCHMENT_ARTIFACT_PREFIX = "parchment-"
public static final String DEFAULT_PARCHMENT_MAVEN_URL = "https://maven.parchmentmc.org/"
- public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.55"
+ public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.67"
public static final String DEVLOGIN_TOOL_ARTIFACT = "net.covers1624:DevLogin:0.1.0.4"
public static final String RENDERNURSE_TOOL_ARTIFACT = "net.neoforged:render-nurse:0.0.12";
public static final String DEVLOGIN_MAIN_CLASS = "net.covers1624.devlogin.DevLogin"
diff --git a/examples/multi_neoforge_root/api/build.gradle b/examples/multi_neoforge_root/api/build.gradle
new file mode 100644
index 000000000..eaa539247
--- /dev/null
+++ b/examples/multi_neoforge_root/api/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+ id 'net.neoforged.gradle.userdev'
+ id 'java-library'
+}
+
+
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ api(libs.neoforge)
+ }
+
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/api/gradle.properties b/examples/multi_neoforge_root/api/gradle.properties
new file mode 100644
index 000000000..5adc99987
--- /dev/null
+++ b/examples/multi_neoforge_root/api/gradle.properties
@@ -0,0 +1,3 @@
+net.neoforged.gradle.caching.cacheDirectory=/tmp/spock_multiple_projects_w_0_tempDir7138874024286350570/.ng-cache
+org.gradle.console=rich
+org.gradle.jvmargs=-Xmx8000m
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt b/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt
new file mode 100644
index 000000000..c688e6ee5
--- /dev/null
+++ b/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt
@@ -0,0 +1,24 @@
+-p
+/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/bootstraplauncher/2.0.2/1a2d076cbc33b0520cbacd591224427b2a20047d/bootstraplauncher-2.0.2.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/securejarhandler/3.0.8/c0ef95cecd8699a0449053ac7d9c160748d902cd/securejarhandler-3.0.8.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.7/e86dda4696d3c185fcc95d8d311904e7ce38a53f/asm-commons-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.7/c0655519f24d92af2202cb681cd7c1569df6ead6/asm-util-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.7/e4a258b7eb96107106c0599f0061cfc1832fe07a/asm-analysis-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.7/e446a17b175bfb733b87c5c2560ccb4e57d69f1a/asm-tree-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.7/73d7b3086e14beb604ced229c302feff6449723/asm-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/net.neoforged/JarJarFileSystems/0.4.1/78f59f89defcd032ed788b151ca6a0d40ace796a/JarJarFileSystems-0.4.1.jar
+--add-modules
+ALL-MODULE-PATH
+--add-opens
+java.base/java.util.jar=cpw.mods.securejarhandler
+--add-opens
+java.base/java.lang.invoke=cpw.mods.securejarhandler
+--add-exports
+java.base/sun.security.util=cpw.mods.securejarhandler
+--add-exports
+jdk.naming.dns/com.sun.jndi.dns=java.naming
+-p
+/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/bootstraplauncher/2.0.2/1a2d076cbc33b0520cbacd591224427b2a20047d/bootstraplauncher-2.0.2.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/securejarhandler/3.0.8/c0ef95cecd8699a0449053ac7d9c160748d902cd/securejarhandler-3.0.8.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.7/e86dda4696d3c185fcc95d8d311904e7ce38a53f/asm-commons-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.7/c0655519f24d92af2202cb681cd7c1569df6ead6/asm-util-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.7/e4a258b7eb96107106c0599f0061cfc1832fe07a/asm-analysis-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.7/e446a17b175bfb733b87c5c2560ccb4e57d69f1a/asm-tree-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.7/73d7b3086e14beb604ced229c302feff6449723/asm-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/net.neoforged/JarJarFileSystems/0.4.1/78f59f89defcd032ed788b151ca6a0d40ace796a/JarJarFileSystems-0.4.1.jar
+--add-modules
+ALL-MODULE-PATH
+--add-opens
+java.base/java.util.jar=cpw.mods.securejarhandler
+--add-opens
+java.base/java.lang.invoke=cpw.mods.securejarhandler
+--add-exports
+java.base/sun.security.util=cpw.mods.securejarhandler
+--add-exports
+jdk.naming.dns/com.sun.jndi.dns=java.naming
diff --git a/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt b/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt
new file mode 100644
index 000000000..b38c35c7b
--- /dev/null
+++ b/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt
@@ -0,0 +1,36 @@
+--launchTarget
+forgejunituserdev
+--version
+21.3.6-beta
+--assetIndex
+asset-index
+--assetsDir
+/var/home/marchermans/.gradle/caches/minecraft/assets
+--gameDir
+.
+--fml.neoForgeVersion
+21.3.6-beta
+--fml.fmlVersion
+4.0.31
+--fml.mcVersion
+1.21.3
+--fml.neoFormVersion
+20241023.131943
+--launchTarget
+forgejunituserdev
+--version
+21.3.6-beta
+--assetIndex
+asset-index
+--assetsDir
+/var/home/marchermans/.gradle/caches/minecraft/assets
+--gameDir
+.
+--fml.neoForgeVersion
+21.3.6-beta
+--fml.fmlVersion
+4.0.31
+--fml.mcVersion
+1.21.3
+--fml.neoFormVersion
+20241023.131943
diff --git a/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java b/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java
new file mode 100644
index 000000000..8a21ee205
--- /dev/null
+++ b/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java
@@ -0,0 +1,11 @@
+
+ package net.neoforged.gradle.apitest;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ System.out.println(Minecraft.getInstance().getClass().toString());
+ }
+ }
+
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/build.gradle b/examples/multi_neoforge_root/build.gradle
new file mode 100644
index 000000000..939da3986
--- /dev/null
+++ b/examples/multi_neoforge_root/build.gradle
@@ -0,0 +1,11 @@
+plugins {
+ id 'net.neoforged.gradle.userdev'
+}
+
+
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/gradle.properties b/examples/multi_neoforge_root/gradle.properties
new file mode 100644
index 000000000..5adc99987
--- /dev/null
+++ b/examples/multi_neoforge_root/gradle.properties
@@ -0,0 +1,3 @@
+net.neoforged.gradle.caching.cacheDirectory=/tmp/spock_multiple_projects_w_0_tempDir7138874024286350570/.ng-cache
+org.gradle.console=rich
+org.gradle.jvmargs=-Xmx8000m
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/gradle/libs.versions.toml b/examples/multi_neoforge_root/gradle/libs.versions.toml
new file mode 100644
index 000000000..336928333
--- /dev/null
+++ b/examples/multi_neoforge_root/gradle/libs.versions.toml
@@ -0,0 +1,6 @@
+[versions]
+ # Neoforge Settings
+ neoforge = "+"
+
+ [libraries]
+ neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar b/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..e6441136f
Binary files /dev/null and b/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.properties b/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..66cd5a0e4
--- /dev/null
+++ b/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/multi_neoforge_root/main/build.gradle b/examples/multi_neoforge_root/main/build.gradle
new file mode 100644
index 000000000..82b8061bf
--- /dev/null
+++ b/examples/multi_neoforge_root/main/build.gradle
@@ -0,0 +1,15 @@
+plugins {
+ id 'net.neoforged.gradle.userdev'
+}
+
+
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ implementation project(':api')
+ }
+
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/main/gradle.properties b/examples/multi_neoforge_root/main/gradle.properties
new file mode 100644
index 000000000..5adc99987
--- /dev/null
+++ b/examples/multi_neoforge_root/main/gradle.properties
@@ -0,0 +1,3 @@
+net.neoforged.gradle.caching.cacheDirectory=/tmp/spock_multiple_projects_w_0_tempDir7138874024286350570/.ng-cache
+org.gradle.console=rich
+org.gradle.jvmargs=-Xmx8000m
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/main/src/main/java/net/neoforged/gradle/main/ApiTests.java b/examples/multi_neoforge_root/main/src/main/java/net/neoforged/gradle/main/ApiTests.java
new file mode 100644
index 000000000..d78521200
--- /dev/null
+++ b/examples/multi_neoforge_root/main/src/main/java/net/neoforged/gradle/main/ApiTests.java
@@ -0,0 +1,13 @@
+
+ package net.neoforged.gradle.main;
+
+ import net.minecraft.client.Minecraft;
+ import net.neoforged.gradle.apitest.FunctionalTests;
+
+ public class ApiTests {
+ public static void main(String[] args) {
+ System.out.println(Minecraft.getInstance().getClass().toString());
+ FunctionalTests.main(args);
+ }
+ }
+
\ No newline at end of file
diff --git a/examples/multi_neoforge_root/settings.gradle b/examples/multi_neoforge_root/settings.gradle
new file mode 100644
index 000000000..92bbc41c1
--- /dev/null
+++ b/examples/multi_neoforge_root/settings.gradle
@@ -0,0 +1,18 @@
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ mavenLocal()
+ maven { url = 'https://maven.neoforged.net/' }
+ }
+
+ includeBuild '../../'
+}
+
+plugins {
+ id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
+}
+
+rootProject.name = 'multi_neoforge_root'
+
+include 'api'
+include 'main'
diff --git a/examples/runs/build.gradle b/examples/runs/build.gradle
index 59e95bbe4..a3285d87a 100644
--- a/examples/runs/build.gradle
+++ b/examples/runs/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'net.neoforged.gradle.userdev'
+// id 'net.neoforged.gradle.userdev'
}
java {
@@ -9,11 +9,13 @@ java {
}
dependencies {
- implementation 'net.neoforged:neoforge:+'
+ //implementation 'net.neoforged:neoforge:+'
}
+/*
runs {
client {
// no modsource
}
-}
\ No newline at end of file
+}
+*/
diff --git a/gradle.properties b/gradle.properties
index 93c152c13..c63c272fd 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,7 +10,7 @@ group=net.neoforged.gradle
java_version=17
#Dependency versions
-groovy_version=3.0.21
+groovy_version=3.0.22
commons_io_version=2.11.0
commons_codec_version=1.15
gson_version=2.9.0
@@ -26,7 +26,7 @@ diffpatch_version=2.0.0.35
jarjar_version=0.4.1
jetbrains_annotations_version=23.0.0
gradle_idea_extension_version=1.1.6
-groovy_dsl_improver_version=1.0.15
+groovy_dsl_improver_version=1.0.16
eclipse_launch_configs_version=0.1.3
vscode_launch_configs_version=1.0.8
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6f7a6eb33..66cd5a0e4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/neoform/src/functionalTest/groovy/net/neoforged/gradle/neoform/FunctionalTests.groovy b/neoform/src/functionalTest/groovy/net/neoforged/gradle/neoform/FunctionalTests.groovy
index 566c0c706..329758c91 100644
--- a/neoform/src/functionalTest/groovy/net/neoforged/gradle/neoform/FunctionalTests.groovy
+++ b/neoform/src/functionalTest/groovy/net/neoforged/gradle/neoform/FunctionalTests.groovy
@@ -79,16 +79,13 @@ class FunctionalTests extends BuilderBasedTestSpecification {
}
}
- minecraft {
- accessTransformers {
- entry "public net.minecraft.client.Minecraft LOGGER # searchRegistry"
- }
- }
+ minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
dependencies {
implementation 'net.minecraft:neoform_client:${NEOFORM_VERSION}'
}
""")
+ it.file("src/main/resources/META-INF/accesstransformer.cfg", """public-f net.minecraft.client.Minecraft LOGGER""")
it.file("src/main/java/net/neoforged/gradle/neoform/FunctionalTests.java", """
package net.neoforged.gradle.neoform;
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java
index 85c869176..468a655c0 100644
--- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java
+++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java
@@ -47,7 +47,7 @@ public NeoFormRuntimeDefinition(@NotNull NeoFormRuntimeSpecification specificati
this.assetsTaskProvider = assetsTaskProvider;
this.nativesTaskProvider = nativesTaskProvider;
- this.getAllDependencies().from(getSpecification().getAdditionalRecompileDependencies());
+ this.additionalRecompileDependencies(specification.getAdditionalRecompileDependencies());
}
@Override
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java
index 10702831e..2b79b0b5c 100644
--- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java
+++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java
@@ -23,7 +23,10 @@
import net.neoforged.gradle.dsl.common.tasks.ArtifactProvider;
import net.neoforged.gradle.dsl.common.tasks.WithOutput;
import net.neoforged.gradle.dsl.common.tasks.specifications.OutputSpecification;
-import net.neoforged.gradle.dsl.common.util.*;
+import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils;
+import net.neoforged.gradle.dsl.common.util.DistributionType;
+import net.neoforged.gradle.dsl.common.util.GameArtifact;
+import net.neoforged.gradle.dsl.common.util.NamingConstants;
import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1;
import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2;
import net.neoforged.gradle.neoform.runtime.definition.NeoFormRuntimeDefinition;
@@ -43,6 +46,7 @@
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.ForkOptions;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
+import org.gradle.process.CommandLineArgumentProvider;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
@@ -191,18 +195,13 @@ private static TaskProvider extends Runtime> createDecompile(NeoFormRuntimeSpe
}
private static String getDecompilerLogLevelArg(DecompilerLogLevel logLevel, String version) {
- switch (logLevel) {
- case TRACE:
- return "trace";
- case INFO:
- return "info";
- case WARN:
- return "warn";
- case ERROR:
- return "error";
- default:
- throw new GradleException("LogLevel " + logLevel + " not supported by " + version);
- }
+ return switch (logLevel) {
+ case TRACE -> "trace";
+ case INFO -> "info";
+ case WARN -> "warn";
+ case ERROR -> "error";
+ default -> throw new GradleException("LogLevel " + logLevel + " not supported by " + version);
+ };
}
private TaskProvider extends Runtime> createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) {
@@ -217,7 +216,7 @@ private static void buildArguments(final RuntimeArguments arguments, final NeoFo
step.getValues().forEach((key, value) -> {
if (value.startsWith("{") && value.endsWith("}")) {
Optional> dependentTask;
- if (!Objects.equals(key, "input") || !alternativeInputProvider.isPresent()) {
+ if (!Objects.equals(key, "input") || alternativeInputProvider.isEmpty()) {
dependentTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, value, tasks);
} else {
dependentTask = alternativeInputProvider;
@@ -354,24 +353,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
Optional> adaptedInput = Optional.empty();
if (spec.getPreTaskTypeAdapters().containsKey(step.getName())) {
- final String inputArgumentMarker = step.getValue("input");
- if (inputArgumentMarker == null) {
- throw new IllegalStateException("Can not change input chain on: " + step.getName() + " it has no input to transform!");
- }
-
- Optional> inputTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, inputArgumentMarker, taskOutputs);
-
- if (!spec.getPreTaskTypeAdapters().get(step.getName()).isEmpty() && inputTask.isPresent()) {
- for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(step.getName())) {
- final TaskProvider extends Runtime> modifiedTree = taskTreeAdapter.adapt(definition, inputTask.get(), neoFormDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), taskProvider -> taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)));
- if (modifiedTree != null) {
- modifiedTree.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task));
- inputTask = Optional.of(modifiedTree);
- }
- }
-
- adaptedInput = inputTask;
- }
+ adaptedInput = adaptPreTaskInput(definition, step, spec, taskOutputs, neoFormDirectory, symbolicDataSources, adaptedInput);
}
TaskProvider extends WithOutput> neoFormRuntimeTaskProvider = createBuiltIn(
@@ -399,8 +381,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
Optional> finalAdaptedInput = adaptedInput;
neoFormRuntimeTaskProvider.configure((WithOutput neoFormRuntimeTask) -> {
- if (neoFormRuntimeTask instanceof Runtime) {
- final Runtime runtimeTask = (Runtime) neoFormRuntimeTask;
+ if (neoFormRuntimeTask instanceof Runtime runtimeTask) {
configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, taskOutputs, step, runtimeTask, finalAdaptedInput);
}
});
@@ -438,22 +419,36 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
remapTask,
symbolicDataSources,
neoFormDirectory,
- context.getLibrariesTask().flatMap(WithOutput::getOutput)
+ Objects.requireNonNull(context.getLibrariesTask()).flatMap(WithOutput::getOutput)
);
- final FileCollection recompileDependencies = spec.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration()));
+ recompileInput = adaptPreTaskInput(
+ definition,
+ "recompile",
+ spec,
+ neoFormDirectory,
+ symbolicDataSources,
+ Optional.of(recompileInput),
+ Optional.of(recompileInput)
+ ).orElseThrow(() -> new IllegalStateException("No input for recompile task due to pre-task adapters"));
+ final FileCollection recompileDependencies = definition.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration()));
+ final TaskProvider extends WithOutput> extractionSource = recompileInput;
final TaskProvider unpackSources = spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "unzipSources"), UnpackZip.class, task -> {
task.getInput().from(
- recompileInput.flatMap(WithOutput::getOutput)
+ extractionSource.flatMap(WithOutput::getOutput)
.map(sourceJar -> task.getArchiveOperations().zipTree(sourceJar).matching(sp -> sp.include("**/*.java")).getAsFileTree())
);
+ task.getInput().from(
+ definition.getAdditionalCompileSources()
+ );
});
unpackSources.configure(neoFormRuntimeTask -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, neoFormRuntimeTask));
final TaskProvider recompileTask = spec.getProject()
.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "recompile"), RecompileSourceJar.class, task -> {
task.setSource(unpackSources.flatMap(UnpackZip::getUnpackingTarget));
+ task.getAdditionalInputFiles().from(definition.getAdditionalCompileSources());
task.getCompileClasspath().setFrom(recompileDependencies);
task.getStepName().set("recompile");
@@ -464,7 +459,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
ForkOptions forkOptions = task.getOptions().getForkOptions();
forkOptions.setMemoryMaximumSize(maxMemory);
forkOptions.setJvmArgs(settings.getJvmArgs().get());
- task.getOptions().getCompilerArgumentProviders().add(settings.getArgs()::get);
+ task.getOptions().getCompilerArgumentProviders().add(new CustomCompilerArgsProvider(settings.getArgs()));
task.getJavaVersion().set(
definition.getVersionJson()
@@ -478,7 +473,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
final TaskProvider packTask = spec.getProject()
.getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "packRecomp"), PackJar.class, task -> {
- task.getInputFiles().from(recompileInput.flatMap(WithOutput::getOutput).map(task.getArchiveOperations()::zipTree).map(zipTree -> zipTree.matching(sp -> sp.exclude("**/*.java"))));
+ task.getInputFiles().from(extractionSource.flatMap(WithOutput::getOutput).map(task.getArchiveOperations()::zipTree).map(zipTree -> zipTree.matching(sp -> sp.exclude("**/*.java"))));
task.getInputFiles().from(recompileTask.flatMap(AbstractCompile::getDestinationDirectory));
});
packTask.configure(neoFormRuntimeTask -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, neoFormRuntimeTask));
@@ -486,7 +481,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
taskOutputs.put(recompileTask.getName(), packTask);
definition.getSourceJarTask().configure(task -> {
- task.getInputFiles().from(recompileInput);
+ task.getInputFiles().from(extractionSource.flatMap(WithOutput::getOutput));
task.dependsOn(remapTask);
});
definition.getRawJarTask().configure(task -> {
@@ -495,6 +490,32 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) {
});
}
+ private static Optional> adaptPreTaskInput(NeoFormRuntimeDefinition definition, NeoFormConfigConfigurationSpecV1.Step step, NeoFormRuntimeSpecification spec, LinkedHashMap> taskOutputs, File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput) {
+ final String inputArgumentMarker = step.getValue("input");
+ if (inputArgumentMarker == null) {
+ throw new IllegalStateException("Can not change input chain on: " + step.getName() + " it has no input to transform!");
+ }
+
+ Optional> inputTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, inputArgumentMarker, taskOutputs);
+
+ return adaptPreTaskInput(definition, step.getName(), spec, neoFormDirectory, symbolicDataSources, adaptedInput, inputTask);
+ }
+
+ private static Optional> adaptPreTaskInput(NeoFormRuntimeDefinition definition, String stepName, NeoFormRuntimeSpecification spec, File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput, Optional> inputTask) {
+ if (!spec.getPreTaskTypeAdapters().get(stepName).isEmpty() && inputTask.isPresent()) {
+ for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(stepName)) {
+ final TaskProvider extends Runtime> modifiedTree = taskTreeAdapter.adapt(definition, inputTask.get(), neoFormDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), taskProvider -> taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)));
+ if (modifiedTree != null) {
+ modifiedTree.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task));
+ inputTask = Optional.of(modifiedTree);
+ }
+ }
+
+ adaptedInput = inputTask;
+ }
+ return adaptedInput;
+ }
+
private static TaskProvider extends WithOutput> maybeApplyParchment(NeoFormRuntimeDefinition runtimeDefinition,
TaskProvider extends WithOutput> recompileInput,
Map symbolicDataSources,
@@ -507,7 +528,7 @@ private static TaskProvider extends WithOutput> maybeApplyParchment(NeoFormRun
return recompileInput;
}
- TaskProvider extends Runtime> applyParchmentTask = project.getTasks().register(CommonRuntimeUtils.buildTaskName(runtimeDefinition, "applyParchment"), DefaultExecute.class, task -> {
+ return project.getTasks().register(CommonRuntimeUtils.buildTaskName(runtimeDefinition, "applyParchment"), DefaultExecute.class, task -> {
// Provide the mappings via artifact
File mappingFile = ToolUtilities.resolveTool(project, parchment.getParchmentArtifact().get());
String conflictPrefix = parchment.getConflictPrefix().get();
@@ -543,7 +564,12 @@ private static TaskProvider extends WithOutput> maybeApplyParchment(NeoFormRun
configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", runtimeDefinition.getSpecification(), neoFormDirectory);
});
+ }
- return applyParchmentTask;
+ public record CustomCompilerArgsProvider(Provider> args) implements CommandLineArgumentProvider {
+ @Override
+ public Iterable asArguments() {
+ return args.get();
+ }
}
}
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java
index 9b2a5a2df..c7b760ad7 100644
--- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java
+++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java
@@ -9,16 +9,14 @@
import net.neoforged.gradle.dsl.common.runtime.tasks.RuntimeMultiArguments;
import org.gradle.api.GradleException;
import org.gradle.api.file.ConfigurableFileCollection;
+import org.gradle.api.file.FileTree;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.api.services.ServiceReference;
-import org.gradle.api.tasks.CacheableTask;
-import org.gradle.api.tasks.Internal;
-import org.gradle.api.tasks.Nested;
-import org.gradle.api.tasks.Optional;
+import org.gradle.api.tasks.*;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.internal.jvm.Jvm;
import org.gradle.jvm.toolchain.JavaLanguageVersion;
@@ -133,6 +131,10 @@ public Property getJavaVersion() {
@ServiceReference(CachedExecutionService.NAME)
public abstract Property getCacheService();
+ @InputFiles
+ @PathSensitive(PathSensitivity.NONE)
+ public abstract ConfigurableFileCollection getAdditionalInputFiles();
+
@Override
protected void compile(InputChanges inputs) {
try {
@@ -142,7 +144,7 @@ protected void compile(InputChanges inputs) {
ICacheableJob.Default.directory(
getDestinationDirectory(),
() -> {
- super.compile(inputs);
+ doCachedCompile(inputs);
}
)
).execute();
@@ -150,4 +152,31 @@ protected void compile(InputChanges inputs) {
throw new GradleException("Failed to recompile!", e);
}
}
+
+ private void doCachedCompile(InputChanges inputs) {
+ super.compile(inputs);
+
+ final FileTree outputTree = getDestinationDirectory().get().getAsFileTree();
+ outputTree.matching(pattern -> pattern.include(fileTreeElement -> {
+ final String relativePath = fileTreeElement.getRelativePath().getPathString();
+ if (!relativePath.endsWith(".class")) {
+ return false;
+ }
+
+ final String sourceFilePath;
+ if (!relativePath.contains("$")) {
+ sourceFilePath = relativePath.substring(0, relativePath.length() - ".class".length()) + ".java";
+ } else {
+ sourceFilePath = relativePath.substring(0, relativePath.indexOf('$')) + ".java";
+ }
+
+ return !getAdditionalInputFiles()
+ .getAsFileTree()
+ .matching(sp1 -> sp1.include(sourceFilePath))
+ .getFiles().isEmpty();
+ })).forEach(file -> {
+ getLogger().debug("Removing additional source file: {}", file);
+ file.delete();
+ });
+ }
}
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java
new file mode 100644
index 000000000..bec0ae202
--- /dev/null
+++ b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java
@@ -0,0 +1,59 @@
+package net.neoforged.gradle.neoform.util;
+
+import net.neoforged.gradle.common.runtime.tasks.SourceAccessTransformer;
+import net.neoforged.gradle.common.runtime.tasks.SourceInterfaceInjection;
+import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils;
+import net.neoforged.gradle.dsl.common.extensions.AccessTransformers;
+import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections;
+import net.neoforged.gradle.dsl.common.extensions.Minecraft;
+import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter;
+import net.neoforged.gradle.dsl.common.tasks.WithOutput;
+import org.gradle.api.Project;
+import org.gradle.api.tasks.TaskProvider;
+
+public class NeoFormAccessTaskAdapterUtils {
+
+ private NeoFormAccessTaskAdapterUtils() {
+ throw new IllegalStateException("Can not instantiate an instance of: McpAccessTransformerUtils. This is a utility class");
+ }
+
+ public static TaskTreeAdapter createAccessTransformerAdapter(final Project project) {
+ final Minecraft minecraftExtension = project.getExtensions().getByType(Minecraft.class);
+ final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers();
+
+ return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> {
+ if (accessTransformerFiles.getFiles().isEmpty()) {
+ return null;
+ }
+
+ final TaskProvider extends SourceAccessTransformer> accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "User", accessTransformerFiles.getFiles().getAsFileTree(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies());
+ accessTransformerTask.configure(task -> task.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput)));
+ accessTransformerTask.configure(task -> task.dependsOn(previousTasksOutput));
+ return accessTransformerTask;
+ };
+ }
+
+ public static TaskTreeAdapter createInterfaceInjectionAdapter(final Project project) {
+ final Minecraft minecraftExtension = project.getExtensions().getByType(Minecraft.class);
+ final InterfaceInjections interfaceInjectionFiles = minecraftExtension.getInterfaceInjections();
+
+ return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> {
+ if (interfaceInjectionFiles.getFiles().isEmpty()) {
+ return null;
+ }
+
+ final TaskProvider extends SourceInterfaceInjection> interfaceInjectionTask = CommonRuntimeTaskUtils.createSourceInterfaceInjection(definition, "User", interfaceInjectionFiles.getFiles().getAsFileTree(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies());
+ interfaceInjectionTask.configure(task -> task.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput)));
+ interfaceInjectionTask.configure(task -> task.dependsOn(previousTasksOutput));
+
+ //Register the stubs
+ definition.additionalCompileSources(
+ project.zipTree(
+ interfaceInjectionTask.flatMap(SourceInterfaceInjection::getStubs)
+ )
+ );
+
+ return interfaceInjectionTask;
+ };
+ }
+}
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTransformerUtils.java b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTransformerUtils.java
deleted file mode 100644
index b1d8af389..000000000
--- a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTransformerUtils.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package net.neoforged.gradle.neoform.util;
-
-import net.neoforged.gradle.common.runtime.tasks.SourceAccessTransformer;
-import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils;
-import net.neoforged.gradle.dsl.common.extensions.AccessTransformers;
-import net.neoforged.gradle.dsl.common.extensions.Minecraft;
-import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter;
-import net.neoforged.gradle.dsl.common.tasks.WithOutput;
-import org.gradle.api.Project;
-import org.gradle.api.tasks.TaskProvider;
-
-public class NeoFormAccessTransformerUtils {
-
- private NeoFormAccessTransformerUtils() {
- throw new IllegalStateException("Can not instantiate an instance of: McpAccessTransformerUtils. This is a utility class");
- }
-
- public static TaskTreeAdapter createAccessTransformerAdapter(final Project project) {
- final Minecraft minecraftExtension = project.getExtensions().getByType(Minecraft.class);
- final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers();
-
- return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> {
- if (accessTransformerFiles.getFiles().isEmpty() && accessTransformerFiles.getEntries().get().isEmpty()) {
- return null;
- }
-
- final TaskProvider extends SourceAccessTransformer> accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "User", runtimeWorkspace, dependentTaskConfigurationHandler, accessTransformerFiles.getFiles().getAsFileTree(), accessTransformerFiles.getEntries().get(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies());
- accessTransformerTask.configure(task -> task.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput)));
- accessTransformerTask.configure(task -> task.dependsOn(previousTasksOutput));
- return accessTransformerTask;
- };
- }
-}
diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java
index 5162f5bd3..10d5d1fed 100644
--- a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java
+++ b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java
@@ -23,21 +23,6 @@ private NeoFormRuntimeUtils() {
throw new IllegalStateException("Can not instantiate an instance of: NeoFormRuntimeUtils. This is a utility class");
}
- @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- public static Provider getTaskInputFor(final NeoFormRuntimeSpecification spec, final Map> tasks, NeoFormConfigConfigurationSpecV1.Step step, final String defaultInputTask, final Optional> adaptedInput, Task task) {
- if (adaptedInput.isPresent()) {
- task.dependsOn(adaptedInput);
- return adaptedInput.get().flatMap(t -> t.getOutput().getAsFile());
- }
-
- final String inputValue = step.getValue("input");
- if (inputValue == null) {
- return getInputForTaskFrom(spec, "{" + defaultInputTask + "Output}", tasks, task);
- }
-
- return getInputForTaskFrom(spec, inputValue, tasks, task);
- }
-
public static Provider getTaskInputFor(final NeoFormRuntimeSpecification spec, final Map> tasks, NeoFormConfigConfigurationSpecV1.Step step, Task task) {
final String inputValue = step.getValue("input");
if (inputValue == null) {
@@ -128,6 +113,7 @@ public static Optional> getInputTaskForTaskFr
}
public static void configureDefaultRuntimeSpecBuilder(Project project, NeoFormRuntimeSpecification.Builder builder) {
- builder.withPostTaskAdapter("decompile", NeoFormAccessTransformerUtils.createAccessTransformerAdapter(project));
+ builder.withPostTaskAdapter("decompile", NeoFormAccessTaskAdapterUtils.createAccessTransformerAdapter(project));
+ builder.withPreTaskAdapter("recompile", NeoFormAccessTaskAdapterUtils.createInterfaceInjectionAdapter(project));
}
}
diff --git a/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java b/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java
index 62abe7cdf..84e247d9f 100644
--- a/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java
+++ b/platform/src/main/java/net/neoforged/gradle/platform/extensions/DynamicProjectExtension.java
@@ -12,7 +12,6 @@
import net.neoforged.gradle.common.extensions.IdeManagementExtension;
import net.neoforged.gradle.common.extensions.JarJarExtension;
import net.neoforged.gradle.common.runtime.extensions.CommonRuntimeExtension;
-import net.neoforged.gradle.common.runtime.tasks.AccessTransformerFileGenerator;
import net.neoforged.gradle.common.runtime.tasks.DefaultExecute;
import net.neoforged.gradle.common.runtime.tasks.DownloadAssets;
import net.neoforged.gradle.common.tasks.JarJar;
@@ -726,10 +725,6 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re
CommonRuntimeExtension.configureCommonRuntimeTaskParameters(task, runtimeDefinition, workingDirectory);
});
- final TaskProvider generateAts = project.getTasks().register("generateAccessTransformers", AccessTransformerFileGenerator.class, task -> {
- CommonRuntimeExtension.configureCommonRuntimeTaskParameters(task, runtimeDefinition, workingDirectory);
- });
-
final TaskProvider packPatches = project.getTasks().register("packPatches", PackJar.class, task -> {
task.getInputFiles().from(project.fileTree(patches).matching(filterable -> {
filterable.include("**/*.patch");
@@ -785,9 +780,6 @@ public void runtime(final String neoFormVersion, Directory patches, Directory re
task.from(createUserdevJson.flatMap(WithOutput::getOutput), spec -> {
spec.rename(name -> "config.json");
});
- task.from(generateAts.flatMap(WithOutput::getOutput), spec -> {
- spec.into("ats/");
- });
task.from(accessTransformers.getFiles(), spec -> {
spec.into("ats/");
});
diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy
index 09b8aa2eb..5220a8989 100644
--- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy
+++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy
@@ -4,6 +4,8 @@ package net.neoforged.gradle.userdev
import net.neoforged.trainingwheels.gradle.functional.BuilderBasedTestSpecification
import org.gradle.testkit.runner.TaskOutcome
+import java.util.zip.ZipFile
+
class AccessTransformerTests extends BuilderBasedTestSpecification {
@Override
@@ -96,90 +98,6 @@ class AccessTransformerTests extends BuilderBasedTestSpecification {
initialRun.task(":build").outcome == TaskOutcome.SUCCESS
}
- def "the userdev runtime supports loading ats from the script"() {
- given:
- def project = create("userdev_supports_ats_in_scripts", {
- it.build("""
- java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
- }
-
- minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper'
-
- dependencies {
- implementation 'net.neoforged:neoforge:+'
- }
- """)
- it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
- package net.neoforged.gradle.userdev;
-
- import net.minecraft.client.Minecraft;
-
- public class FunctionalTests {
- public static void main(String[] args) {
- System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString());
- }
- }
- """)
- it.withToolchains()
- it.withGlobalCacheDirectory(tempDir)
- })
-
- when:
- def initialRun = project.run {
- it.tasks('build')
- }
-
- then:
- initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
- initialRun.task(":build").outcome == TaskOutcome.SUCCESS
- }
-
- def "the userdev runtime supports loading ats from the script and the file"() {
- given:
- def project = create("userdev_supports_ats_in_script_and_file", {
- it.build("""
- java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
- }
- minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
- minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft LOGGER # LOGGER'
-
- dependencies {
- implementation 'net.neoforged:neoforge:+'
- }
- """)
- it.file("src/main/resources/META-INF/accesstransformer.cfg", """public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper""")
- it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
- package net.neoforged.gradle.userdev;
-
- import net.minecraft.client.Minecraft;
-
- public class FunctionalTests {
- public static void main(String[] args) {
- System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString());
- System.out.println(Minecraft.LOGGER.getClass().toString());
- }
- }
- """)
- it.withToolchains()
- it.withGlobalCacheDirectory(tempDir)
- })
-
- when:
- def initialRun = project.run {
- it.tasks('build')
- }
-
- then:
- initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
- initialRun.task(":build").outcome == TaskOutcome.SUCCESS
- }
-
def "the userdev runtime supports loading ats from multiple files"() {
given:
def project = create("userdev_supports_ats_in_multiple_distinctly_named_files", {
diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy
index 8f33dc9cd..99342f1ef 100644
--- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy
+++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy
@@ -407,4 +407,78 @@ class FunctionalTests extends BuilderBasedTestSpecification {
run.task(':clean').outcome == TaskOutcome.SUCCESS
run.task(':build').outcome == TaskOutcome.SUCCESS
}
+
+ def "a mod with userdev does not expose the userdev artifact to consumers"() {
+ given:
+ def project = create("gradle_multi_sourceset", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ api 'net.neoforged:neoforge:+'
+ }
+
+ publishing {
+ repositories {
+ maven {
+ // change to point to your repo, e.g. http://my.org/repo
+ url = layout.buildDirectory.dir('repo')
+ }
+ }
+
+ publications {
+ maven(MavenPublication) {
+ groupId = 'org.gradle.sample'
+ artifactId = 'library'
+ version = '1.1'
+
+ from components.java
+ }
+ }
+ }
+
+ """)
+ it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
+ package net.neoforged.gradle.userdev;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ System.out.println(Minecraft.getInstance().getClass().toString());
+ }
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ it.plugin("java-library")
+ it.plugin("maven-publish")
+ })
+
+ when:
+ def run = project.run {
+ it.tasks('clean', 'build', 'publish')
+ it.stacktrace()
+ }
+
+ then:
+ run.task(':clean').outcome == TaskOutcome.SUCCESS
+ run.task(':build').outcome == TaskOutcome.SUCCESS
+ run.task(':publish').outcome == TaskOutcome.SUCCESS
+
+ and:
+ def repo = run.file("build/repo/org/gradle/sample/library/1.1")
+ def moduleFile = new File(repo, "library-1.1.module")
+ def pomFile = new File(repo, "library-1.1.pom")
+
+ then:
+ moduleFile.exists()
+ pomFile.exists()
+ !moduleFile.text.contains("neoforged")
+ !pomFile.text.contains("neoforged")
+ }
}
diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy
new file mode 100644
index 000000000..4916ab65b
--- /dev/null
+++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy
@@ -0,0 +1,231 @@
+package net.neoforged.gradle.userdev
+
+
+import net.neoforged.trainingwheels.gradle.functional.BuilderBasedTestSpecification
+import org.gradle.testkit.runner.TaskOutcome
+
+class InterfaceInjectionTests extends BuilderBasedTestSpecification {
+
+ @Override
+ protected void configurePluginUnderTest() {
+ pluginUnderTest = "net.neoforged.gradle.userdev";
+ injectIntoAllProject = true;
+ }
+
+ def "the userdev runtime supports loading iis from a file"() {
+ given:
+ def project = create("userdev_supports_iis_from_file", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json')
+
+ dependencies {
+ implementation 'net.neoforged:neoforge:+'
+ }
+ """)
+ it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""")
+ it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
+ package net.neoforged.gradle.userdev;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ Minecraft.getInstance().doSomething();
+ }
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """
+ package com.example.examplemod;
+
+ public interface MyInjectedInterface {
+ default void doSomething() { };
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ })
+
+ when:
+ def initialRun = project.run {
+ it.tasks('build')
+ it.debug()
+ }
+
+ then:
+ initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
+ initialRun.task(":build").outcome == TaskOutcome.SUCCESS
+ }
+
+ def "the userdev runtime supports loading iis from a file after the dependencies block"() {
+ given:
+ def project = create("userdev_supports_iis_from_file", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ implementation 'net.neoforged:neoforge:+'
+ }
+
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json')
+ """)
+ it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""")
+ it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
+ package net.neoforged.gradle.userdev;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ Minecraft.getInstance().doSomething();
+ }
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """
+ package com.example.examplemod;
+
+ public interface MyInjectedInterface {
+ default void doSomething() { };
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ })
+
+ when:
+ def initialRun = project.run {
+ it.tasks('build')
+ }
+
+ then:
+ initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
+ initialRun.task(":build").outcome == TaskOutcome.SUCCESS
+ }
+
+ def "the userdev runtime supports loading iis from multiple files"() {
+ given:
+ def project = create("userdev_supports_iis_in_multiple_distinctly_named_files", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json')
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis2.json')
+
+ dependencies {
+ implementation 'net.neoforged:neoforge:+'
+ }
+ """)
+ it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""")
+ it.file("src/main/resources/META-INF/iis2.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MySecondaryInterface"]}""")
+ it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
+ package net.neoforged.gradle.userdev;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ Minecraft.getInstance().doSomething();
+ Minecraft.getInstance().doSecondSomething();
+ }
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """
+ package com.example.examplemod;
+
+ public interface MyInjectedInterface {
+ default void doSomething() { };
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MySecondaryInterface.java", """
+ package com.example.examplemod;
+
+ public interface MySecondaryInterface {
+ default void doSecondSomething() { };
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ })
+
+ when:
+ def initialRun = project.run {
+ it.tasks('build')
+ }
+
+ then:
+ initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
+ initialRun.task(":build").outcome == TaskOutcome.SUCCESS
+ }
+
+ def "the userdev runtime supports loading iis from multiple files named the same in different directories"() {
+ given:
+ def project = create("userdev_supports_iis_in_files_named_the_same", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json')
+ minecraft.interfaceInjections.file rootProject.file('src/main/resources/iis.json')
+
+ dependencies {
+ implementation 'net.neoforged:neoforge:+'
+ }
+ """)
+ it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""")
+ it.file("src/main/resources/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MySecondaryInterface"]}""")
+ it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """
+ package net.neoforged.gradle.userdev;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ Minecraft.getInstance().doSomething();
+ Minecraft.getInstance().doSecondSomething();
+ }
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """
+ package com.example.examplemod;
+
+ public interface MyInjectedInterface {
+ default void doSomething() { };
+ }
+ """)
+ it.file("src/main/java/com/example/examplemod/MySecondaryInterface.java", """
+ package com.example.examplemod;
+
+ public interface MySecondaryInterface {
+ default void doSecondSomething() { };
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ })
+
+ when:
+ def initialRun = project.run {
+ it.tasks('build')
+ }
+
+ then:
+ initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS
+ initialRun.task(":build").outcome == TaskOutcome.SUCCESS
+ }
+}
diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy
index bcf06bc12..9f42ddb41 100644
--- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy
+++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy
@@ -105,6 +105,102 @@ class MultiProjectTests extends BuilderBasedTestSpecification {
run.output.contains("Caused by: net.neoforged.fml.ModLoadingException: Loading errors encountered:")
}
+ def "multiple projects with neoforge dependencies from version catalogs should be able to build"() {
+ given:
+ def rootProject = create("multi_neoforge_root", {
+ it.file("gradle/libs.versions.toml",
+ """
+ [versions]
+ # Neoforge Settings
+ neoforge = "+"
+
+ [libraries]
+ neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" }
+ """.trim())
+
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ })
+
+ def apiProject = create(rootProject, "api", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ api(libs.neoforge)
+ }
+ """)
+ it.file("src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java", """
+ package net.neoforged.gradle.apitest;
+
+ import net.minecraft.client.Minecraft;
+
+ public class FunctionalTests {
+ public static void main(String[] args) {
+ System.out.println(Minecraft.getInstance().getClass().toString());
+ }
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ it.plugin(this.pluginUnderTest)
+ it.plugin("java-library")
+ })
+
+ def mainProject = create(rootProject,"main", {
+ it.build("""
+ java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
+ }
+
+ dependencies {
+ implementation project(':api')
+ //We still need a neoforge dependency here, because neoforge is not exposed from the api project. Compile would faile.
+ implementation libs.neoforge
+ }
+ """)
+ it.file("src/main/java/net/neoforged/gradle/main/ApiTests.java", """
+ package net.neoforged.gradle.main;
+
+ import net.minecraft.client.Minecraft;
+ import net.neoforged.gradle.apitest.FunctionalTests;
+
+ public class ApiTests {
+ public static void main(String[] args) {
+ System.out.println(Minecraft.getInstance().getClass().toString());
+ FunctionalTests.main(args);
+ }
+ }
+ """)
+ it.withToolchains()
+ it.withGlobalCacheDirectory(tempDir)
+ it.plugin(this.pluginUnderTest)
+ })
+
+ when:
+ def run = rootProject.run {
+ it.tasks(':main:build')
+ it.stacktrace()
+ it.debug()
+ }
+
+ then:
+ run.task(':main:build').outcome == TaskOutcome.SUCCESS
+ }
+
def "multiple projects with neoforge dependencies should be able to run the game when renderDoc is enabled"() {
given:
def rootProject = create("multi_neoforge_root_renderdoc", {
diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy
index a774678c3..ab8c0709d 100644
--- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy
+++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy
@@ -425,13 +425,14 @@ class RunTests extends BuilderBasedTestSpecification {
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+ import net.minecraft.core.registries.Registries;
@ExtendWith(EphemeralTestServerProvider.class)
public class TestTest {
@Test
public void testIngredient(MinecraftServer server) { // required to load tags
Assertions.assertTrue(
- Ingredient.of(ItemTags.AXES).test(Items.DIAMOND_AXE.getDefaultInstance())
+ Ingredient.of(server.registryAccess().lookupOrThrow(Registries.ITEM).getOrThrow(ItemTags.AXES)).test(Items.DIAMOND_AXE.getDefaultInstance())
);
}
}
diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java b/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java
index 2acf60c3c..aa3fe835f 100644
--- a/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java
+++ b/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java
@@ -85,8 +85,8 @@ private void registerReplacementHandler(Project project) {
"NeoForgeUserDevAdditionalReplacementDependenciesFor" + runtimeDefinition.getSpecification().getIdentifier(),
configuration -> {
configuration.setDescription("Additional dependencies for the NeoForge UserDev replacement for " + runtimeDefinition.getSpecification().getIdentifier());
- configuration.extendsFrom(runtimeDefinition.getNeoFormRuntimeDefinition().getMinecraftDependenciesConfiguration());
- configuration.extendsFrom(runtimeDefinition.getAdditionalUserDevDependencies());
+ ConfigurationUtils.extendsFrom(project, configuration, runtimeDefinition.getNeoFormRuntimeDefinition().getMinecraftDependenciesConfiguration());
+ ConfigurationUtils.extendsFrom(project, configuration, runtimeDefinition.getAdditionalUserDevDependencies());
}
);
diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java
index d942398d7..629fa672e 100644
--- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java
+++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java
@@ -18,6 +18,7 @@
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
+import org.gradle.api.file.FileCollection;
import org.gradle.api.file.FileTree;
import org.gradle.api.file.RegularFile;
import org.gradle.api.provider.MapProperty;
@@ -102,12 +103,16 @@ public TaskProvider extends WithOutput> getListLibrariesTaskProvider() {
return neoformRuntimeDefinition.getListLibrariesTaskProvider();
}
+ @Override
+ public @NotNull FileCollection getAdditionalRecompileDependencies() {
+ return neoformRuntimeDefinition.getAdditionalRecompileDependencies();
+ }
+
@Override
protected void buildRunInterpolationData(RunImpl run, @NotNull MapProperty interpolationData) {
neoformRuntimeDefinition.buildRunInterpolationData(run, interpolationData);
if (userdevConfiguration.getModules() != null && !userdevConfiguration.getModules().get().isEmpty()) {
- final String name = String.format("moduleResolverForgeUserDev%s", getSpecification().getVersionedName());
final Configuration modulesCfg = ConfigurationUtils
.temporaryUnhandledConfiguration(
getSpecification().getProject().getConfigurations(),
@@ -132,9 +137,9 @@ protected void buildRunInterpolationData(RunImpl run, @NotNull MapProperty {
@@ -65,10 +64,12 @@ public UserDevRuntimeExtension(Project project) {
.withDistributionType(DistributionType.JOINED)
.withAdditionalDependencies(getProject().files(userDevAdditionalDependenciesConfiguration));
- final TaskTreeAdapter atAdapter = createAccessTransformerAdapter(userDevProfile.getAccessTransformerDirectory().get(), userDevJar)
- .andThen(NeoFormAccessTransformerUtils.createAccessTransformerAdapter(getProject()));
+ final TaskTreeAdapter atAndIISAdapter = createAccessTransformerAdapter(userDevProfile.getAccessTransformerDirectory().get(), userDevJar)
+ .andThen(NeoFormAccessTaskAdapterUtils.createAccessTransformerAdapter(getProject()));
- builder.withPostTaskAdapter("decompile", atAdapter);
+ builder.withPostTaskAdapter("decompile", atAndIISAdapter);
+
+ builder.withPreTaskAdapter("recompile", NeoFormAccessTaskAdapterUtils.createInterfaceInjectionAdapter(getProject()));
builder.withPostTaskAdapter("patch", createPatchAdapter(userDevJar, userDevProfile.getSourcePatchesDirectory().get()));
@@ -137,7 +138,12 @@ private TaskTreeAdapter createAccessTransformerAdapter(final String accessTransf
userDev.matching(filter -> filter.include(accessTransformerDirectory + "/**"));
return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> {
- final TaskProvider extends SourceAccessTransformer> accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "Forges", runtimeWorkspace, dependentTaskConfigurationHandler, accessTransformerFiles, Collections.emptyList(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies());
+ if (accessTransformerFiles.isEmpty()) {
+ // No access transformers found, so we don't need to do anything
+ return null;
+ }
+
+ final TaskProvider extends SourceAccessTransformer> accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "Forges", accessTransformerFiles, definition.getListLibrariesTaskProvider(), definition.getAllDependencies());
accessTransformerTask.configure(task -> task.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput)));
accessTransformerTask.configure(task -> task.dependsOn(previousTasksOutput));
return accessTransformerTask;
diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java
index 2b962ad4e..ca760eced 100644
--- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java
+++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java
@@ -51,7 +51,14 @@ public UserDevRuntimeSpecification(Project project,
String forgeName,
String forgeVersion,
Artifact artifact) {
- super(project, "neoForge", version, distribution, preTaskTypeAdapters, postTypeAdapters, taskCustomizers, UserDevRuntimeExtension.class);
+ super(project,
+ "neoForge",
+ version,
+ distribution,
+ preTaskTypeAdapters,
+ postTypeAdapters,
+ taskCustomizers,
+ UserDevRuntimeExtension.class);
this.userDevArchive = userDevArchive;
this.profile = profile;
this.forgeGroup = forgeGroup;
diff --git a/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy b/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy
index df0c1106c..da7a72370 100644
--- a/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy
+++ b/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy
@@ -54,91 +54,6 @@ class AccessTransformerTests extends BuilderBasedTestSpecification {
initialRun.task(":build").outcome == TaskOutcome.SUCCESS
}
- def "the vanilla runtime supports loading ats from the script"() {
- given:
- def project = create("vanilla_supports_ats_in_scripts", {
- it.build("""
- java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
- }
-
- minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper'
-
- dependencies {
- implementation 'net.minecraft:client:+'
- }
- """)
- it.file("src/main/java/net/neoforged/gradle/vanilla/FunctionalTests.java", """
- package net.neoforged.gradle.vanilla;
-
- import net.minecraft.client.Minecraft;
-
- public class FunctionalTests {
- public static void main(String[] args) {
- System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString());
- }
- }
- """)
- it.withToolchains()
- it.withGlobalCacheDirectory(tempDir)
- })
-
- when:
- def initialRun = project.run {
- it.tasks('build')
- }
-
- then:
- initialRun.task(":clientApplyUserAccessTransformer").outcome == TaskOutcome.SUCCESS
- initialRun.task(":build").outcome == TaskOutcome.SUCCESS
- }
-
- def "the vanilla runtime supports loading ats from the script and the file"() {
- given:
- def project = create("vanilla_supports_ats_in_script_and_file", {
- it.build("""
- java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(21)
- }
- }
- minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg')
- minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft LOGGER # LOGGER'
-
- dependencies {
- implementation 'net.minecraft:client:+'
- }
- """)
- it.file("src/main/resources/META-INF/accesstransformer.cfg", """public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper""")
- it.file("src/main/java/net/neoforged/gradle/vanilla/FunctionalTests.java", """
- package net.neoforged.gradle.vanilla;
-
- import net.minecraft.client.Minecraft;
-
- public class FunctionalTests {
- public static void main(String[] args) {
- System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString());
- System.out.println(Minecraft.LOGGER.getClass().toString());
- }
- }
- """)
- it.withToolchains()
- it.withGlobalCacheDirectory(tempDir)
- })
-
- when:
- def initialRun = project.run {
- it.tasks('build')
-
- }
-
- then:
- initialRun.task(":clientApplyUserAccessTransformer").outcome == TaskOutcome.SUCCESS
- initialRun.task(":build").outcome == TaskOutcome.SUCCESS
- }
-
def "the vanilla runtime supports loading ats from multiple files"() {
given:
def project = create("vanilla_supports_ats_in_multiple_distinctly_named_files", {
diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java
index 2658acd6b..2f643a8b9 100644
--- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java
+++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java
@@ -165,26 +165,28 @@ protected void bakeDefinition(VanillaRuntimeDefinition definition) {
)
);
- task.configure((Runtime mcpRuntimeTask) -> configureCommonRuntimeTaskParameters(mcpRuntimeTask, step.getName(), spec, runtimeWorkingDirectory));
-
- if (!spec.getPostTypeAdapters().containsKey(step.getName())) {
- definition.getTasks().put(task.getName(), task);
- } else {
- int taskPostAdapterIndex = 0;
- for (TaskTreeAdapter taskTreeAdapter : spec.getPostTypeAdapters().get(step.getName())) {
- final AtomicInteger additionalPostAdapterTasks = new AtomicInteger(0);
- final int currentPostAdapterIndex = taskPostAdapterIndex++;
- final TaskProvider extends Runtime> taskProvider = taskTreeAdapter.adapt(definition, task, vanillaDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), dependentTaskProvider -> dependentTaskProvider.configure(additionalTask -> configureCommonRuntimeTaskParameters(additionalTask, step.getName() + "PostAdapter" + currentPostAdapterIndex + "-" + additionalPostAdapterTasks.getAndIncrement(), spec, runtimeWorkingDirectory)));
- if (taskProvider != null) {
- taskProvider.configure(adaptedTask -> configureCommonRuntimeTaskParameters(adaptedTask, step.getName() + "PostAdapter" + currentPostAdapterIndex + "-" + additionalPostAdapterTasks.getAndIncrement(), spec, runtimeWorkingDirectory));
- task = taskProvider;
+ if (task != null) {
+ task.configure((Runtime mcpRuntimeTask) -> configureCommonRuntimeTaskParameters(mcpRuntimeTask, step.getName(), spec, runtimeWorkingDirectory));
+
+ if (!spec.getPostTypeAdapters().containsKey(step.getName())) {
+ definition.getTasks().put(task.getName(), task);
+ } else {
+ int taskPostAdapterIndex = 0;
+ for (TaskTreeAdapter taskTreeAdapter : spec.getPostTypeAdapters().get(step.getName())) {
+ final AtomicInteger additionalPostAdapterTasks = new AtomicInteger(0);
+ final int currentPostAdapterIndex = taskPostAdapterIndex++;
+ final TaskProvider extends Runtime> taskProvider = taskTreeAdapter.adapt(definition, task, vanillaDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), dependentTaskProvider -> dependentTaskProvider.configure(additionalTask -> configureCommonRuntimeTaskParameters(additionalTask, step.getName() + "PostAdapter" + currentPostAdapterIndex + "-" + additionalPostAdapterTasks.getAndIncrement(), spec, runtimeWorkingDirectory)));
+ if (taskProvider != null) {
+ taskProvider.configure(adaptedTask -> configureCommonRuntimeTaskParameters(adaptedTask, step.getName() + "PostAdapter" + currentPostAdapterIndex + "-" + additionalPostAdapterTasks.getAndIncrement(), spec, runtimeWorkingDirectory));
+ task = taskProvider;
+ }
}
+
+ definition.getTasks().put(task.getName(), task);
}
- definition.getTasks().put(task.getName(), task);
+ currentInput = task;
}
-
- currentInput = task;
}
final TaskProvider extends WithOutput> sourcesTask = Iterators.getLast(definition.getTasks().values().iterator());
diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java
index 1e5909933..32771d3eb 100644
--- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java
+++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java
@@ -3,7 +3,6 @@
import net.neoforged.gradle.common.runtime.tasks.BinaryAccessTransformer;
import net.neoforged.gradle.dsl.common.util.GameArtifact;
import net.neoforged.gradle.util.StringCapitalizationUtils;
-import net.neoforged.gradle.common.runtime.tasks.NoopRuntime;
import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils;
import net.neoforged.gradle.dsl.common.extensions.AccessTransformers;
import net.neoforged.gradle.dsl.common.extensions.Minecraft;
@@ -25,20 +24,14 @@ public TaskProvider extends Runtime> buildTask(VanillaRuntimeDefinition defini
final Minecraft minecraftExtension = definition.getSpecification().getProject().getExtensions().getByType(Minecraft.class);
final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers();
- if (accessTransformerFiles.getFiles().isEmpty() && (!accessTransformerFiles.getEntries().isPresent() || accessTransformerFiles.getEntries().get().isEmpty())) {
- return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sAccessTransformer", StringCapitalizationUtils.capitalize("user"))), NoopRuntime.class, task -> {
- task.getInput().set(inputProvidingTask.flatMap(WithOutput::getOutput));
- task.dependsOn(inputProvidingTask);
- });
+ if (accessTransformerFiles.getFiles().isEmpty()) {
+ return null;
}
final TaskProvider extends BinaryAccessTransformer> task = CommonRuntimeTaskUtils.createBinaryAccessTransformer(
definition,
"user",
- workingDirectory,
- additionalTaskConfigurator,
- accessTransformerFiles.getFiles().getAsFileTree(),
- accessTransformerFiles.getEntries().get()
+ accessTransformerFiles.getFiles().getAsFileTree()
);
task.configure(t -> {