From 3c8cdcf5de89604edae412fd8457aed1a0a0016a Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Wed, 11 Sep 2024 20:02:46 +0200 Subject: [PATCH 01/11] Bricked GDI But working setup --- .../gradle/common/CommonProjectPlugin.java | 5 ++ .../AccessTransformersExtension.java | 2 +- .../InterfaceInjectionsExtension.java | 73 +++++++++++++++++++ .../base/BaseFilesWithEntriesExtension.java | 2 +- .../InterfaceInjectionPublishing.java | 56 ++++++++++++++ .../extensions/AccessTransformers.groovy | 2 +- .../extensions/InjectedInterfaceData.groovy | 37 ++++++++++ .../extensions/InterfaceInjections.groovy | 70 ++++++++++++++++++ gradle.properties | 2 +- 9 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy diff --git a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java index 6fab4c9f1..414eeee3b 100644 --- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java +++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java @@ -9,6 +9,7 @@ import net.neoforged.gradle.common.extensions.sourcesets.SourceSetDependencyExtensionImpl; import net.neoforged.gradle.common.extensions.sourcesets.SourceSetInheritanceExtensionImpl; import net.neoforged.gradle.common.extensions.subsystems.SubsystemsExtension; +import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing; import net.neoforged.gradle.common.rules.LaterAddedReplacedDependencyRule; import net.neoforged.gradle.common.runs.ide.IdeRunIntegrationManager; import net.neoforged.gradle.common.runs.run.RunManagerImpl; @@ -84,6 +85,7 @@ public void apply(Project project) { project.getExtensions().create(DependencyReplacement.class, "dependencyReplacements", ReplacementLogic.class, project); project.getExtensions().create(NeoGradleProblemReporter.class, PROBLEM_REPORTER_EXTENSION_NAME, NeoGradleProblemReporter.class, problems.forNamespace(PROBLEM_NAMESPACE)); project.getExtensions().create(AccessTransformers.class, "accessTransformers", AccessTransformersExtension.class, project); + project.getExtensions().create(InterfaceInjections.class, "interfaceInjections", InterfaceInjectionsExtension.class, project); project.getExtensions().create(Minecraft.class, "minecraft", MinecraftExtension.class, project); project.getExtensions().create(Mappings.class,"mappings", MappingsExtension.class, project); @@ -131,6 +133,9 @@ public void apply(Project project) { //Set up publishing for access transformer elements AccessTransformerPublishing.setup(project); + //Set up publishing for interface injection elements + InterfaceInjectionPublishing.setup(project); + //Set up the IDE run integration manager IdeRunIntegrationManager.getInstance().setup(project); diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java index 61dcf4637..8db28e8df 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java @@ -13,7 +13,7 @@ import javax.inject.Inject; -public abstract class AccessTransformersExtension extends BaseFilesWithEntriesExtension implements AccessTransformers { +public abstract class AccessTransformersExtension extends BaseFilesWithEntriesExtension implements AccessTransformers { private transient final DependencyHandler projectDependencies; private transient final ArtifactHandler projectArtifacts; diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java new file mode 100644 index 000000000..03142d4ef --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java @@ -0,0 +1,73 @@ +package net.neoforged.gradle.common.extensions; + +import net.neoforged.gradle.common.extensions.base.BaseFilesWithEntriesExtension; +import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing; +import net.neoforged.gradle.dsl.common.extensions.InjectedInterfaceData; +import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ConfigurablePublishArtifact; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.dsl.ArtifactHandler; +import org.gradle.api.artifacts.dsl.DependencyHandler; + +import javax.inject.Inject; +import java.util.Collection; +import java.util.List; + +public abstract class InterfaceInjectionsExtension extends BaseFilesWithEntriesExtension implements InterfaceInjections { + private transient final DependencyHandler projectDependencies; + private transient final ArtifactHandler projectArtifacts; + + @SuppressWarnings("UnstableApiUsage") + @Inject + public InterfaceInjectionsExtension(Project project) { + super(project); + + this.projectDependencies = project.getDependencies(); + this.projectArtifacts = project.getArtifacts(); + + // We have to add these after project evaluation because of dependency replacement making configurations non-lazy; adding them earlier would prevent further addition of dependencies + project.afterEvaluate(p -> { + p.getConfigurations().maybeCreate(InterfaceInjectionPublishing.INTERFACE_INJECTION_CONFIGURATION).fromDependencyCollector(getConsume()); + p.getConfigurations().maybeCreate(InterfaceInjectionPublishing.INTERFACE_INJECTION_API_CONFIGURATION).fromDependencyCollector(getConsumeApi()); + }); + } + + @Override + public void expose(Object path, Action action) { + file(path); + projectArtifacts.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_ELEMENTS_CONFIGURATION, path, action); + } + + @Override + public void expose(Object path) { + expose(path, artifacts -> { + }); + } + + @Override + public void expose(Dependency dependency) { + projectDependencies.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_API_CONFIGURATION, dependency); + } + + @Override + public void inject(String type, String interfaceName) { + inject(type, List.of(interfaceName)); + } + + @Override + public void inject(String type, String... interfaceNames) { + inject(type, List.of(interfaceNames)); + } + + @Override + public void inject(String type, Collection interfaceNames) { + final InjectedInterfaceData data = getProject().getObjects().newInstance(InjectedInterfaceData.class); + + data.getTarget().set(type); + data.getInterfaces().addAll(interfaceNames); + + entry(data); + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java index 47ff86626..49ca27deb 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java @@ -9,7 +9,7 @@ /** * Represents part of an extension which combines a set of files with entries as well as raw addable entries. */ -public abstract class BaseFilesWithEntriesExtension> implements BaseDSLElementWithFilesAndEntries { +public abstract class BaseFilesWithEntriesExtension, TEntry> implements BaseDSLElementWithFilesAndEntries { private final Project project; diff --git a/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java new file mode 100644 index 000000000..b5235461c --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java @@ -0,0 +1,56 @@ +package net.neoforged.gradle.common.interfaceinjection; + +import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.attributes.AttributeContainer; +import org.gradle.api.attributes.Category; +import org.gradle.api.component.AdhocComponentWithVariants; + +public class InterfaceInjectionPublishing { + + public static final String INTERFACE_INJECTION_ELEMENTS_CONFIGURATION = "InterfaceInjectionElements"; + public static final String INTERFACE_INJECTION_API_CONFIGURATION = "InterfaceInjectionApi"; + public static final String INTERFACE_INJECTION_CONFIGURATION = "InterfaceInjection"; + public static final String INTERFACE_INJECTION_CATEGORY = "InterfaceInjection"; + + public static void setup(Project project) { + InterfaceInjections InterfaceInjectionsExtension = project.getExtensions().getByType(InterfaceInjections.class); + + Configuration InterfaceInjectionElements = project.getConfigurations().maybeCreate(INTERFACE_INJECTION_ELEMENTS_CONFIGURATION); + Configuration InterfaceInjectionApi = project.getConfigurations().maybeCreate(INTERFACE_INJECTION_API_CONFIGURATION); + Configuration InterfaceInjection = project.getConfigurations().maybeCreate(INTERFACE_INJECTION_CONFIGURATION); + + InterfaceInjectionApi.setCanBeConsumed(false); + InterfaceInjectionApi.setCanBeResolved(false); + + InterfaceInjection.setCanBeConsumed(false); + InterfaceInjection.setCanBeResolved(true); + + InterfaceInjectionElements.setCanBeConsumed(true); + InterfaceInjectionElements.setCanBeResolved(false); + InterfaceInjectionElements.setCanBeDeclared(false); + + Action action = attributes -> { + attributes.attribute(Category.CATEGORY_ATTRIBUTE, project.getObjects().named(Category.class, INTERFACE_INJECTION_CATEGORY)); + }; + + InterfaceInjectionElements.attributes(action); + InterfaceInjection.attributes(action); + + InterfaceInjection.extendsFrom(InterfaceInjectionApi); + InterfaceInjectionElements.extendsFrom(InterfaceInjectionApi); + + // Now we set up the component, conditionally + AdhocComponentWithVariants java = (AdhocComponentWithVariants) project.getComponents().getByName("java"); + Runnable enable = () -> java.addVariantsFromConfiguration(InterfaceInjectionElements, variant -> { + }); + + InterfaceInjectionElements.getAllDependencies().configureEach(dep -> enable.run()); + InterfaceInjectionElements.getArtifacts().configureEach(artifact -> enable.run()); + + // And add resolved ATs to the extension + InterfaceInjectionsExtension.getFiles().from(InterfaceInjection); + } +} diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy index 33bfe32bd..5f5665351 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy @@ -12,7 +12,7 @@ import org.gradle.api.artifacts.dsl.DependencyCollector * Defines a DSL extension which allows for the specification of access transformers. */ @CompileStatic -interface AccessTransformers extends BaseDSLElementWithFilesAndEntries, Dependencies { +interface AccessTransformers extends BaseDSLElementWithFilesAndEntries, Dependencies { /** * {@return access transformers to add as dependencies} */ diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy new file mode 100644 index 000000000..d4e02c020 --- /dev/null +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy @@ -0,0 +1,37 @@ +package net.neoforged.gradle.dsl.common.extensions + +import groovy.transform.CompileStatic +import net.minecraftforge.gdi.ConfigurableDSLElement +import net.minecraftforge.gdi.annotations.DSLProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Nested + +/** + * Represents the data for injecting interfaces into a target class. + */ +@CompileStatic +abstract class InjectedInterfaceData implements ConfigurableDSLElement { + + /** + * The binary representation of the target class to inject the interfaces into. + * + * @return The target class to inject the interfaces into. + */ + @Input + @DSLProperty + abstract Property 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. + */ + @Nested + @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..710dc8d23 --- /dev/null +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy @@ -0,0 +1,70 @@ +package net.neoforged.gradle.dsl.common.extensions + +import groovy.transform.CompileStatic +import net.minecraftforge.gdi.BaseDSLElementWithFilesAndEntries +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 + +/** + * Defines a DSL extension which allows for the specification of interface injections. + */ +@CompileStatic +interface InterfaceInjections extends BaseDSLElementWithFilesAndEntries, Dependencies { + /** + * {@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) + + /** + * Injects the given interface into the target class. + * + * @param type The binary representation of the target class to inject the interfaces into. + * @param interfaceName The binary representation of the interface to inject. + */ + void inject(String type, String interfaceName) + + /** + * Injects the given interfaces into the target class. + * + * @param type The binary representation of the target class to inject the interfaces into. + * @param interfaceNames The binary representation of the interfaces to inject. + */ + void inject(String type, String... interfaceNames) + + /** + * Injects the given interfaces into the target class. + * + * @param type The binary representation of the target class to inject the interfaces into. + * @param interfaceNames The binary representation of the interfaces to inject. + */ + void inject(String type, Collection interfaceNames) +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 93c152c13..2cd61bc13 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 From 5e6e5b5b510aecfab4eebb417a5c0c861b91a126 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 20 Oct 2024 10:32:59 +0200 Subject: [PATCH 02/11] Initial working version --- README.md | 19 ++ .../gradle/common/CommonProjectPlugin.java | 3 + .../AccessTransformersExtension.java | 15 +- .../common/extensions/IExtensionCreator.java | 13 - .../InterfaceInjectionsExtension.java | 37 +-- .../common/extensions/MinecraftExtension.java | 14 ++ .../base/BaseFilesWithEntriesExtension.java | 30 --- .../definition/CommonRuntimeDefinition.java | 45 ++++ .../tasks/AccessTransformerFileGenerator.java | 44 ---- .../tasks/SourceAccessTransformer.java | 2 +- .../tasks/SourceInterfaceInjection.java | 95 +++++++ .../common/util/CommonRuntimeTaskUtils.java | 48 +--- .../gradle/common/util/ZipFileUpdater.java | 49 ++++ .../extensions/AccessTransformers.groovy | 12 +- .../extensions/InjectedInterfaceData.groovy | 2 +- .../extensions/InterfaceInjections.groovy | 35 +-- .../dsl/common/extensions/Minecraft.groovy | 9 + .../runtime/definition/Definition.groovy | 48 ++++ .../dsl/common/tasks/ArtifactProvider.groovy | 13 +- .../specifications/OutputSpecification.groovy | 16 +- .../gradle/dsl/common/util/Constants.groovy | 2 +- .../gradle/neoform/FunctionalTests.groovy | 7 +- .../definition/NeoFormRuntimeDefinition.java | 2 +- .../extensions/NeoFormRuntimeExtension.java | 101 ++++---- .../runtime/tasks/RecompileSourceJar.java | 39 ++- .../util/NeoFormAccessTaskAdapterUtils.java | 59 +++++ .../util/NeoFormAccessTransformerUtils.java | 33 --- .../neoform/util/NeoFormRuntimeUtils.java | 18 +- .../extensions/DynamicProjectExtension.java | 8 - .../userdev/AccessTransformerTests.groovy | 86 +------ .../userdev/InterfaceInjectionTests.groovy | 231 ++++++++++++++++++ .../definition/UserDevRuntimeDefinition.java | 7 +- .../extension/UserDevRuntimeExtension.java | 13 +- .../UserDevRuntimeSpecification.java | 9 +- .../vanilla/AccessTransformerTests.groovy | 85 ------- .../steps/ApplyAccessTransformerStep.java | 12 +- 36 files changed, 774 insertions(+), 487 deletions(-) delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/IExtensionCreator.java delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java delete mode 100644 common/src/main/java/net/neoforged/gradle/common/runtime/tasks/AccessTransformerFileGenerator.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceInterfaceInjection.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/util/ZipFileUpdater.java create mode 100644 neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java delete mode 100644 neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTransformerUtils.java create mode 100644 userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy diff --git a/README.md b/README.md index 1966294ed..5a5dfd2f5 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,25 @@ dependencies { } ``` +#### Access Transformers +The userdev plugin provides a way to configure access transformers for your mod. +There are two ways to configure access transformers, either by using the DSL or by using a file. + +##### DSL +By using the `entry` method on the `accessTransformers` object, you can add an access transformer entry to your mod. +The system will write and generate the relevant files for you, however you will still need to add them to the file +```groovy +minecraft { + accessTransformers { + entry 'public-f net.neoforged.example.ExampleClass' + } +} +``` + + + + + #### Dependency management by the userdev plugin When this plugin detects a dependency on NeoForge, it will spring into action and create the necessary NeoForm runtime tasks to build a usable Minecraft JAR-file that contains the requested NeoForge version. It additionally (if configured to do so via conventions, which is the default) will create runs for your project, and add the necessary dependencies to the classpath of the run. diff --git a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java index 414eeee3b..689315c7b 100644 --- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java +++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java @@ -22,6 +22,7 @@ import net.neoforged.gradle.common.services.caching.CachedExecutionService; import net.neoforged.gradle.common.tasks.CleanCache; import net.neoforged.gradle.common.tasks.DisplayMappingsLicenseTask; +import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils; import net.neoforged.gradle.common.util.ConfigurationUtils; import net.neoforged.gradle.common.util.run.RunsUtil; import net.neoforged.gradle.dsl.common.extensions.*; @@ -41,7 +42,9 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.problems.Problems; import org.gradle.api.tasks.Delete; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.jvm.tasks.Jar; import org.gradle.plugins.ide.eclipse.EclipsePlugin; import org.gradle.plugins.ide.idea.IdeaPlugin; import org.jetbrains.gradle.ext.IdeaExtPlugin; diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java index 8db28e8df..46ee07b70 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java @@ -1,8 +1,6 @@ package net.neoforged.gradle.common.extensions; -import net.neoforged.gradle.common.CommonProjectPlugin; import net.neoforged.gradle.common.accesstransformers.AccessTransformerPublishing; -import net.neoforged.gradle.common.extensions.base.BaseFilesWithEntriesExtension; import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; import org.gradle.api.Action; import org.gradle.api.Project; @@ -13,14 +11,16 @@ import javax.inject.Inject; -public abstract class AccessTransformersExtension extends BaseFilesWithEntriesExtension implements AccessTransformers { +public abstract class AccessTransformersExtension implements AccessTransformers { private transient final DependencyHandler projectDependencies; private transient final ArtifactHandler projectArtifacts; + private final Project project; + @SuppressWarnings("UnstableApiUsage") @Inject public AccessTransformersExtension(Project project) { - super(project); + this.project = project; this.projectDependencies = project.getDependencies(); this.projectArtifacts = project.getArtifacts(); @@ -32,9 +32,14 @@ public AccessTransformersExtension(Project project) { }); } + @Override + public Project getProject() { + return project; + } + @Override public void expose(Object path, Action action) { - file(path); + getFiles().from(path); projectArtifacts.add(AccessTransformerPublishing.ACCESS_TRANSFORMER_ELEMENTS_CONFIGURATION, path, action); } diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/IExtensionCreator.java b/common/src/main/java/net/neoforged/gradle/common/extensions/IExtensionCreator.java deleted file mode 100644 index 0412df880..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/IExtensionCreator.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.neoforged.gradle.common.extensions; - -import org.gradle.api.Project; - -import java.util.function.Function; - -/** - * Custom function definition for creating an extension. - * - * @param The type of the extension - */ -public interface IExtensionCreator extends Function { -} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java index 03142d4ef..72dfcc2d5 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java @@ -1,8 +1,6 @@ package net.neoforged.gradle.common.extensions; -import net.neoforged.gradle.common.extensions.base.BaseFilesWithEntriesExtension; import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing; -import net.neoforged.gradle.dsl.common.extensions.InjectedInterfaceData; import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections; import org.gradle.api.Action; import org.gradle.api.Project; @@ -12,17 +10,17 @@ import org.gradle.api.artifacts.dsl.DependencyHandler; import javax.inject.Inject; -import java.util.Collection; -import java.util.List; -public abstract class InterfaceInjectionsExtension extends BaseFilesWithEntriesExtension implements InterfaceInjections { +public abstract class InterfaceInjectionsExtension implements InterfaceInjections { private transient final DependencyHandler projectDependencies; private transient final ArtifactHandler projectArtifacts; + private final Project project; + @SuppressWarnings("UnstableApiUsage") @Inject public InterfaceInjectionsExtension(Project project) { - super(project); + this.project = project; this.projectDependencies = project.getDependencies(); this.projectArtifacts = project.getArtifacts(); @@ -34,9 +32,14 @@ public InterfaceInjectionsExtension(Project project) { }); } + @Override + public Project getProject() { + return project; + } + @Override public void expose(Object path, Action action) { - file(path); + getFiles().from(path); projectArtifacts.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_ELEMENTS_CONFIGURATION, path, action); } @@ -50,24 +53,4 @@ public void expose(Object path) { public void expose(Dependency dependency) { projectDependencies.add(InterfaceInjectionPublishing.INTERFACE_INJECTION_API_CONFIGURATION, dependency); } - - @Override - public void inject(String type, String interfaceName) { - inject(type, List.of(interfaceName)); - } - - @Override - public void inject(String type, String... interfaceNames) { - inject(type, List.of(interfaceNames)); - } - - @Override - public void inject(String type, Collection interfaceNames) { - final InjectedInterfaceData data = getProject().getObjects().newInstance(InjectedInterfaceData.class); - - data.getTarget().set(type); - data.getInterfaces().addAll(interfaceNames); - - entry(data); - } } diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/MinecraftExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/MinecraftExtension.java index a770fbfca..1e4ed4cdd 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/MinecraftExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/MinecraftExtension.java @@ -23,11 +23,13 @@ import net.minecraftforge.gdi.ConfigurableDSLElement; import net.neoforged.gradle.common.runtime.naming.NamingChannelProvider; import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; +import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections; import net.neoforged.gradle.dsl.common.extensions.Mappings; import net.neoforged.gradle.dsl.common.extensions.Minecraft; import net.neoforged.gradle.dsl.common.runtime.naming.NamingChannel; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; +import org.jetbrains.annotations.NotNull; import javax.inject.Inject; @@ -35,12 +37,14 @@ public abstract class MinecraftExtension implements ConfigurableDSLElement namingChannelProviders; @Inject public MinecraftExtension(final Project project) { this.project = project; this.accessTransformers = project.getExtensions().getByType(AccessTransformers.class); + this.interfaceInjections = project.getExtensions().getByType(InterfaceInjections.class); this.namingChannelProviders = project.getObjects().domainObjectContainer(NamingChannel.class, name -> project.getObjects().newInstance(NamingChannelProvider.class, project, name)); final String baseName = project.getName().replace(":", "_"); @@ -52,23 +56,33 @@ public MinecraftExtension(final Project project) { })); } + @NotNull @Override public Project getProject() { return project; } + @NotNull @Override public NamedDomainObjectContainer getNamingChannels() { return namingChannelProviders; } + @NotNull @Override public Mappings getMappings() { return project.getExtensions().getByType(Mappings.class); } + @NotNull @Override public AccessTransformers getAccessTransformers() { return this.accessTransformers; } + + @NotNull + @Override + public InterfaceInjections getInterfaceInjections() { + return interfaceInjections; + } } diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java deleted file mode 100644 index 49ca27deb..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.neoforged.gradle.common.extensions.base; - -import net.minecraftforge.gdi.BaseDSLElementWithFilesAndEntries; -import org.gradle.api.Project; - -import javax.inject.Inject; -import java.util.Collections; - -/** - * Represents part of an extension which combines a set of files with entries as well as raw addable entries. - */ -public abstract class BaseFilesWithEntriesExtension, TEntry> implements BaseDSLElementWithFilesAndEntries { - - private final Project project; - - @Inject - public BaseFilesWithEntriesExtension(Project project) { - this.project = project; - } - - @Override - public Project getProject() { - return project; - } - - @Override - public boolean isEmpty() { - return getFiles().isEmpty() && getEntries().getOrElse(Collections.emptyList()).isEmpty(); - } -} diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/definition/CommonRuntimeDefinition.java b/common/src/main/java/net/neoforged/gradle/common/runtime/definition/CommonRuntimeDefinition.java index e88fce31c..379bc4ce7 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/definition/CommonRuntimeDefinition.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/definition/CommonRuntimeDefinition.java @@ -17,6 +17,7 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.Directory; +import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFile; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.MapProperty; @@ -61,6 +62,12 @@ public abstract class CommonRuntimeDefinition versionJson; + @NotNull + private FileCollection additionalRecompileDependencies; + + @NotNull + private FileCollection additionalSources; + protected CommonRuntimeDefinition( @NotNull final S specification, @NotNull final LinkedHashMap> taskOutputs, @@ -79,8 +86,12 @@ protected CommonRuntimeDefinition( this.associatedTaskConsumer = associatedTaskConsumer; this.versionJson = versionJson; + this.additionalRecompileDependencies = specification.getProject().files(); + this.additionalSources = specification.getProject().files(); + this.allDependencies = specification.getProject().files(); this.allDependencies.from(getMinecraftDependenciesConfiguration()); + this.allDependencies.from(this.additionalRecompileDependencies); } @Override @@ -163,6 +174,40 @@ public final ConfigurableFileCollection getAllDependencies() { return allDependencies; } + @NotNull + @Override + public FileCollection getAdditionalRecompileDependencies() { + return additionalRecompileDependencies; + } + + @Override + public void additionalRecompileDependency(Provider dependency) { + final FileCollection files = specification.getProject().files(dependency); + this.additionalRecompileDependencies = this.additionalRecompileDependencies.plus(files); + } + + @Override + public void additionalRecompileDependencies(FileCollection dependencies) { + this.additionalRecompileDependencies = this.additionalRecompileDependencies.plus(dependencies); + } + + @NotNull + @Override + public FileCollection getAdditionalCompileSources() { + return additionalSources; + } + + @Override + public void additionalCompileSource(Provider source) { + final FileCollection files = specification.getProject().files(source); + this.additionalSources = this.additionalSources.plus(files); + } + + @Override + public void additionalCompileSources(FileCollection sources) { + this.additionalSources = this.additionalSources.plus(sources); + } + public void configureRun(RunImpl run) { final MapProperty runtimeInterpolationData = getSpecification().getProject().getObjects().mapProperty(String.class, String.class); buildRunInterpolationData(run, runtimeInterpolationData); diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/AccessTransformerFileGenerator.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/AccessTransformerFileGenerator.java deleted file mode 100644 index 1a5a8f30a..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/AccessTransformerFileGenerator.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.neoforged.gradle.common.runtime.tasks; - -import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.TaskAction; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardOpenOption; - -@CacheableTask -public abstract class AccessTransformerFileGenerator extends DefaultRuntime { - - public AccessTransformerFileGenerator() { - super(); - - getAdditionalTransformers().convention( - getProject().getExtensions().getByType(AccessTransformers.class) - .getEntries() - ); - - getOutputFileName().set(String.format("_script_%s.cfg", getProject().getName())); - } - - @TaskAction - void doCreateAccessTransformerFiles() throws IOException { - final File outputFile = ensureFileWorkspaceReady(getOutput()); - Files.deleteIfExists(outputFile.toPath()); - Files.write(outputFile.toPath(), getAdditionalTransformers().get(), StandardOpenOption.CREATE_NEW); - - if (!Files.exists(outputFile.toPath())) { - Files.createFile(outputFile.toPath()); - } - } - - @Input - @Optional - public abstract ListProperty getAdditionalTransformers(); - -} diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceAccessTransformer.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceAccessTransformer.java index 0b2545fd4..ed98feb9c 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceAccessTransformer.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceAccessTransformer.java @@ -41,7 +41,7 @@ public SourceAccessTransformer() { } builder.append(f.getAbsolutePath()); }); - args.add("--classpath=" + builder.toString()); + args.add("--classpath=" + builder); args.add(inputFile.getAsFile().getAbsolutePath()); args.add(outputFile.getAbsolutePath()); diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceInterfaceInjection.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceInterfaceInjection.java new file mode 100644 index 000000000..0ceb28d4b --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/SourceInterfaceInjection.java @@ -0,0 +1,95 @@ +package net.neoforged.gradle.common.runtime.tasks; + +import com.google.common.collect.Lists; +import net.neoforged.gradle.common.util.ToolUtilities; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import org.apache.commons.io.FileUtils; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.*; + +import java.io.File; +import java.util.List; + +@CacheableTask +public abstract class SourceInterfaceInjection extends DefaultExecute { + + public SourceInterfaceInjection() { + super(); + + setDescription("Runs the interface injection on the decompiled sources."); + + getStubs().convention(getOutputDirectory().map(dir -> dir.file("stubs.jar"))); + + getExecutingJar().set(ToolUtilities.resolveTool(getProject(), getProject().getExtensions().getByType(Subsystems.class).getTools().getJST().get())); + getRuntimeProgramArguments().convention( + getInputFile().map(inputFile -> { + final List args = Lists.newArrayList(); + final File outputFile = ensureFileWorkspaceReady(getOutput()); + final File stubsFile = ensureFileWorkspaceReady(getStubs()); + + args.add("--enable-interface-injection"); + getTransformers().forEach(f -> { + args.add("--interface-injection-data"); + args.add(f.getAbsolutePath()); + }); + + args.add("--interface-injection-stubs"); + args.add(stubsFile.getAbsolutePath()); + + args.add("--libraries-list=" + getLibraries().get().getAsFile().getAbsolutePath()); + + final StringBuilder builder = new StringBuilder(); + getClasspath().forEach(f -> { + if (!builder.isEmpty()) { + builder.append(File.pathSeparator); + } + builder.append(f.getAbsolutePath()); + }); + args.add("--classpath=" + builder); + + args.add(inputFile.getAsFile().getAbsolutePath()); + args.add(outputFile.getAbsolutePath()); + + return args; + } + ) + ); + + getJavaVersion().convention(getProject().getExtensions().getByType(JavaPluginExtension.class).getToolchain().getLanguageVersion()); + getTransformers().finalizeValueOnRead(); + getLogLevel().set(LogLevel.DISABLED); + } + + @Override + public void doExecute() throws Exception { + //We need a separate check here that skips the execute call if there are no transformers. + if (getTransformers().isEmpty()) { + final File output = ensureFileWorkspaceReady(getOutput()); + FileUtils.copyFile(getInputFile().get().getAsFile(), output); + } + + super.doExecute(); + } + + @InputFile + @PathSensitive(PathSensitivity.NONE) + public abstract RegularFileProperty getInputFile(); + + @InputFile + @PathSensitive(PathSensitivity.NONE) + public abstract RegularFileProperty getLibraries(); + + @InputFiles + @Optional + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getClasspath(); + + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getTransformers(); + + @OutputFile + public abstract RegularFileProperty getStubs(); +} diff --git a/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java index 078d43639..171408bf7 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java @@ -1,10 +1,7 @@ package net.neoforged.gradle.common.util; -import net.neoforged.gradle.common.runtime.tasks.BinaryAccessTransformer; -import net.neoforged.gradle.common.runtime.tasks.SourceAccessTransformer; -import net.neoforged.gradle.common.runtime.tasks.AccessTransformerFileGenerator; +import net.neoforged.gradle.common.runtime.tasks.*; import net.neoforged.gradle.dsl.common.runtime.definition.Definition; -import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils; import net.neoforged.gradle.util.StringCapitalizationUtils; @@ -13,8 +10,6 @@ import org.gradle.api.tasks.TaskProvider; import java.io.File; -import java.util.Collection; -import java.util.function.Consumer; public final class CommonRuntimeTaskUtils { @@ -22,48 +17,27 @@ private CommonRuntimeTaskUtils() { throw new IllegalStateException("Can not instantiate an instance of: CommonRuntimeTaskUtils. This is a utility class"); } - public static TaskProvider createSourceAccessTransformer(Definition definition, String namePreFix, File workspaceDirectory, Consumer> dependentTaskConfigurationHandler, FileTree files, Collection data, TaskProvider listLibs, FileCollection additionalClasspathElements) { - final TaskProvider generator; - if (!data.isEmpty()) { - generator = definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), namePreFix + "AccessTransformerGenerator"), AccessTransformerFileGenerator.class, task -> { - task.getOutput().set(new File(workspaceDirectory, "accesstransformers/" + namePreFix + "/_script-access-transformer.cfg")); - task.getAdditionalTransformers().set(data); - }); - dependentTaskConfigurationHandler.accept(generator); - } else { - generator = null; - } - + public static TaskProvider createSourceAccessTransformer(Definition definition, String namePreFix, FileTree files, TaskProvider listLibs, FileCollection additionalClasspathElements) { return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sAccessTransformer", StringCapitalizationUtils.capitalize(namePreFix))), SourceAccessTransformer.class, task -> { task.getTransformers().from(files); - if (generator != null) { - task.getTransformers().from(generator.flatMap(WithOutput::getOutput)); - task.dependsOn(generator); - } task.dependsOn(listLibs); task.getLibraries().set(listLibs.flatMap(WithOutput::getOutput)); task.getClasspath().from(additionalClasspathElements); }); } - public static TaskProvider createBinaryAccessTransformer(Definition definition, String namePreFix, File workspaceDirectory, Consumer> dependentTaskConfigurationHandler, FileTree files, Collection data) { - final TaskProvider generator; - if (!data.isEmpty()) { - generator = definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), namePreFix + "AccessTransformerGenerator"), AccessTransformerFileGenerator.class, task -> { - task.getOutput().set(new File(workspaceDirectory, "accesstransformers/" + namePreFix + "/_script-access-transformer.cfg")); - task.getAdditionalTransformers().set(data); - }); - dependentTaskConfigurationHandler.accept(generator); - } else { - generator = null; - } + public static TaskProvider createSourceInterfaceInjection(Definition definition, String namePreFix, FileTree files, TaskProvider listLibs, FileCollection additionalClasspathElements) { + return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sInterfaceInjection", StringCapitalizationUtils.capitalize(namePreFix))), SourceInterfaceInjection.class, task -> { + task.getTransformers().from(files); + task.dependsOn(listLibs); + task.getLibraries().set(listLibs.flatMap(WithOutput::getOutput)); + task.getClasspath().from(additionalClasspathElements); + }); + } + public static TaskProvider createBinaryAccessTransformer(Definition definition, String namePreFix, File workspaceDirectory, FileTree files) { return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sAccessTransformer", StringCapitalizationUtils.capitalize(namePreFix))), BinaryAccessTransformer.class, task -> { task.getTransformers().from(files); - if (generator != null) { - task.getTransformers().from(generator.flatMap(WithOutput::getOutput)); - task.dependsOn(generator); - } }); } } diff --git a/common/src/main/java/net/neoforged/gradle/common/util/ZipFileUpdater.java b/common/src/main/java/net/neoforged/gradle/common/util/ZipFileUpdater.java new file mode 100644 index 000000000..2a7c55bd7 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/util/ZipFileUpdater.java @@ -0,0 +1,49 @@ +package net.neoforged.gradle.common.util; + +import java.io.*; +import java.nio.file.*; +import java.util.zip.*; + +public class ZipFileUpdater { + + public static void addFileToZip(File zipFile, File fileToAdd, String entryName) throws IOException { + // Temporary zip file + File tempFile = File.createTempFile(zipFile.getName(), null); + tempFile.delete(); + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile)); + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tempFile))) { + + // Copy existing entries to the new zip file + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if (entry.getName().equals(entryName)) { + continue; + } + zos.putNextEntry(new ZipEntry(entry.getName())); + byte[] buffer = new byte[1024]; + int len; + while ((len = zis.read(buffer)) > 0) { + zos.write(buffer, 0, len); + } + zos.closeEntry(); + zis.closeEntry(); + } + + // Add the new file to the zip file + try (InputStream fis = new FileInputStream(fileToAdd)) { + zos.putNextEntry(new ZipEntry(entryName)); + byte[] buffer = new byte[1024]; + int len; + while ((len = fis.read(buffer)) > 0) { + zos.write(buffer, 0, len); + } + zos.closeEntry(); + } + } + + // Replace the old zip file with the new zip file + Files.delete(zipFile.toPath()); + Files.move(tempFile.toPath(), zipFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } +} \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy index 5f5665351..685e34824 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy @@ -2,17 +2,27 @@ 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 +import org.gradle.api.tasks.InputFiles /** * Defines a DSL extension which allows for the specification of access transformers. */ @CompileStatic -interface AccessTransformers extends BaseDSLElementWithFilesAndEntries, Dependencies { +interface AccessTransformers extends Dependencies { + + /** + * {@return access transformer files} + */ + @DSLProperty + ConfigurableFileCollection getFiles(); + /** * {@return access transformers to add as dependencies} */ diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy index d4e02c020..4f6772502 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InjectedInterfaceData.groovy @@ -31,7 +31,7 @@ abstract class InjectedInterfaceData implements ConfigurableDSLElement 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 index 710dc8d23..52bcbbc1b 100644 --- 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 @@ -2,17 +2,26 @@ 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 BaseDSLElementWithFilesAndEntries, Dependencies { +interface InterfaceInjections extends Dependencies { + + /** + * {@return interface injection files} + */ + @DSLProperty + ConfigurableFileCollection getFiles() + /** * {@return interface injections to add as dependencies} */ @@ -43,28 +52,4 @@ interface InterfaceInjections extends BaseDSLElementWithFilesAndEntries action) - - /** - * Injects the given interface into the target class. - * - * @param type The binary representation of the target class to inject the interfaces into. - * @param interfaceName The binary representation of the interface to inject. - */ - void inject(String type, String interfaceName) - - /** - * Injects the given interfaces into the target class. - * - * @param type The binary representation of the target class to inject the interfaces into. - * @param interfaceNames The binary representation of the interfaces to inject. - */ - void inject(String type, String... interfaceNames) - - /** - * Injects the given interfaces into the target class. - * - * @param type The binary representation of the target class to inject the interfaces into. - * @param interfaceNames The binary representation of the interfaces to inject. - */ - void inject(String type, Collection interfaceNames) } \ 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/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/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..7fce0fe78 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 @@ -36,9 +36,9 @@ import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.file.FileCollection; -import org.gradle.api.file.RegularFile; +import org.gradle.api.file.*; import org.gradle.api.provider.Provider; +import org.gradle.api.specs.Spec; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.compile.AbstractCompile; import org.gradle.api.tasks.compile.ForkOptions; @@ -191,18 +191,13 @@ private static TaskProvider 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 createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) { @@ -217,7 +212,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 +349,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 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 neoFormRuntimeTaskProvider = createBuiltIn( @@ -399,8 +377,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 +415,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 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"); @@ -478,7 +469,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 +477,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 +486,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 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 maybeApplyParchment(NeoFormRuntimeDefinition runtimeDefinition, TaskProvider recompileInput, Map symbolicDataSources, @@ -507,7 +524,7 @@ private static TaskProvider maybeApplyParchment(NeoFormRun return recompileInput; } - TaskProvider 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 +560,5 @@ private static TaskProvider maybeApplyParchment(NeoFormRun configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", runtimeDefinition.getSpecification(), neoFormDirectory); }); - - return applyParchmentTask; } } 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 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 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 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/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/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..f0e74743c 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 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(), diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java index 3b7b9eff7..2845d56b4 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java @@ -19,7 +19,7 @@ import net.neoforged.gradle.neoform.runtime.extensions.NeoFormRuntimeExtension; import net.neoforged.gradle.neoform.runtime.tasks.InjectZipContent; import net.neoforged.gradle.neoform.runtime.tasks.Patch; -import net.neoforged.gradle.neoform.util.NeoFormAccessTransformerUtils; +import net.neoforged.gradle.neoform.util.NeoFormAccessTaskAdapterUtils; import net.neoforged.gradle.userdev.runtime.definition.UserDevRuntimeDefinition; import net.neoforged.gradle.userdev.runtime.specification.UserDevRuntimeSpecification; import org.gradle.api.Project; @@ -31,7 +31,6 @@ import org.jetbrains.annotations.Nullable; import java.io.File; -import java.util.Collections; public abstract class UserDevRuntimeExtension extends CommonRuntimeExtension { @@ -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,7 @@ private TaskTreeAdapter createAccessTransformerAdapter(final String accessTransf userDev.matching(filter -> filter.include(accessTransformerDirectory + "/**")); return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> { - final TaskProvider accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "Forges", runtimeWorkspace, dependentTaskConfigurationHandler, accessTransformerFiles, Collections.emptyList(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies()); + final TaskProvider 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/steps/ApplyAccessTransformerStep.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java index 1e5909933..f58514a76 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,11 @@ public TaskProvider 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); - }); - } - final TaskProvider task = CommonRuntimeTaskUtils.createBinaryAccessTransformer( definition, "user", workingDirectory, - additionalTaskConfigurator, - accessTransformerFiles.getFiles().getAsFileTree(), - accessTransformerFiles.getEntries().get() + accessTransformerFiles.getFiles().getAsFileTree() ); task.configure(t -> { From ec5c43709c05131ec5f6354e2084d0a4dbe9338f Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Fri, 16 Aug 2024 09:49:14 +0200 Subject: [PATCH 03/11] Introduce an isolated problem reporter to provide compatibility with gradle. Works around: Gradle/Gradle#30203 --- .../gradle/common/CommonProjectPlugin.java | 6 +- .../dependency/ExtraJarDependencyManager.java | 4 +- .../common/dependency/JarJarArtifacts.java | 6 +- .../extensions/NeoGradleProblemReporter.java | 114 ------------------ .../extensions/problems/IProblemReporter.java | 26 ++++ .../problems/IntegratedProblemReporter.java | 61 ++++++++++ .../problems/IsolatedProblemReporter.java | 28 +++++ .../problems/NeoGradleProblemSpec.java | 85 +++++++++++++ .../ProblemReportingConfigurator.java | 20 +++ .../SourceSetDependencyExtensionImpl.java | 4 +- .../SourceSetInheritanceExtensionImpl.java | 4 +- .../subsystems/IntegrationExtensions.java | 19 +++ .../subsystems/SubsystemsExtension.java | 7 ++ .../gradle/common/runs/run/RunImpl.java | 10 +- .../neoforged/gradle/common/tasks/JarJar.java | 4 +- .../gradle/common/util/run/RunsUtil.java | 6 +- .../extensions/subsystems/Integration.groovy | 20 +++ .../extensions/subsystems/Subsystems.groovy | 5 + gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 20 files changed, 295 insertions(+), 138 deletions(-) delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/NeoGradleProblemReporter.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/problems/IProblemReporter.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/problems/IntegratedProblemReporter.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/problems/IsolatedProblemReporter.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/problems/NeoGradleProblemSpec.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/problems/ProblemReportingConfigurator.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/IntegrationExtensions.java create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy diff --git a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java index 689315c7b..c87bc2302 100644 --- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java +++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java @@ -5,6 +5,7 @@ import net.neoforged.gradle.common.dependency.ExtraJarDependencyManager; import net.neoforged.gradle.common.extensions.*; import net.neoforged.gradle.common.extensions.dependency.replacement.ReplacementLogic; +import net.neoforged.gradle.common.extensions.problems.ProblemReportingConfigurator; import net.neoforged.gradle.common.extensions.repository.IvyRepository; import net.neoforged.gradle.common.extensions.sourcesets.SourceSetDependencyExtensionImpl; import net.neoforged.gradle.common.extensions.sourcesets.SourceSetInheritanceExtensionImpl; @@ -53,8 +54,6 @@ public class CommonProjectPlugin implements Plugin { - public static final String PROBLEM_NAMESPACE = "neoforged.gradle"; - public static final String PROBLEM_REPORTER_EXTENSION_NAME = "neogradleProblems"; private final Problems problems; @@ -86,7 +85,6 @@ public void apply(Project project) { project.getExtensions().create(Repository.class, "ivyDummyRepository", IvyRepository.class, project); project.getExtensions().create(MinecraftArtifactCache.class, "minecraftArtifactCache", MinecraftArtifactCacheExtension.class, project); project.getExtensions().create(DependencyReplacement.class, "dependencyReplacements", ReplacementLogic.class, project); - project.getExtensions().create(NeoGradleProblemReporter.class, PROBLEM_REPORTER_EXTENSION_NAME, NeoGradleProblemReporter.class, problems.forNamespace(PROBLEM_NAMESPACE)); project.getExtensions().create(AccessTransformers.class, "accessTransformers", AccessTransformersExtension.class, project); project.getExtensions().create(InterfaceInjections.class, "interfaceInjections", InterfaceInjectionsExtension.class, project); @@ -96,6 +94,8 @@ public void apply(Project project) { project.getExtensions().create(ExtraJarDependencyManager.class, "clientExtraJarDependencyManager", ExtraJarDependencyManager.class, project); project.getExtensions().create(RunManager.class, "runManager", RunManagerImpl.class, project); + ProblemReportingConfigurator.configureProblemReporting(project, problems); + final ConfigurationData configurationData = project.getExtensions().create(ConfigurationData.class, "configurationData", ConfigurationDataExtension.class, project); OfficialNamingChannelConfigurator.getInstance().configure(project); diff --git a/common/src/main/java/net/neoforged/gradle/common/dependency/ExtraJarDependencyManager.java b/common/src/main/java/net/neoforged/gradle/common/dependency/ExtraJarDependencyManager.java index 784383e8d..463bd8579 100644 --- a/common/src/main/java/net/neoforged/gradle/common/dependency/ExtraJarDependencyManager.java +++ b/common/src/main/java/net/neoforged/gradle/common/dependency/ExtraJarDependencyManager.java @@ -1,7 +1,7 @@ package net.neoforged.gradle.common.dependency; import com.google.common.collect.Maps; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.runtime.tasks.GenerateExtraJar; import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache; import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacement; @@ -77,7 +77,7 @@ private boolean hasMatchingArtifact(ExternalModuleDependency externalModuleDepen private ReplacementResult generateReplacement(final Project project, final Dependency dependency) { final String minecraftVersion = dependency.getVersion(); if (minecraftVersion == null) { - final NeoGradleProblemReporter problemReporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter problemReporter = project.getExtensions().getByType(IProblemReporter.class); throw problemReporter.throwing(spec -> { spec.id("dependencies.extra-jar", "missingVersion") .contextualLabel("Client-Extra Jar: Missing Version") diff --git a/common/src/main/java/net/neoforged/gradle/common/dependency/JarJarArtifacts.java b/common/src/main/java/net/neoforged/gradle/common/dependency/JarJarArtifacts.java index e1ef593a1..55e6529aa 100644 --- a/common/src/main/java/net/neoforged/gradle/common/dependency/JarJarArtifacts.java +++ b/common/src/main/java/net/neoforged/gradle/common/dependency/JarJarArtifacts.java @@ -1,7 +1,7 @@ package net.neoforged.gradle.common.dependency; import net.neoforged.gradle.common.extensions.JarJarExtension; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.dsl.common.dependency.DependencyFilter; import net.neoforged.gradle.dsl.common.dependency.DependencyManagementObject; import net.neoforged.gradle.dsl.common.dependency.DependencyVersionInformationHandler; @@ -42,7 +42,7 @@ public abstract class JarJarArtifacts { private transient final SetProperty includedRootComponents; private transient final SetProperty includedArtifacts; - private final NeoGradleProblemReporter reporter; + private final IProblemReporter reporter; private final DependencyFilter dependencyFilter; private final DependencyVersionInformationHandler dependencyVersionInformationHandler; @@ -74,7 +74,7 @@ public DependencyVersionInformationHandler getDependencyVersionInformationHandle } @Inject - public JarJarArtifacts(NeoGradleProblemReporter reporter) { + public JarJarArtifacts(IProblemReporter reporter) { this.reporter = reporter; dependencyFilter = getObjectFactory().newInstance(DefaultDependencyFilter.class); dependencyVersionInformationHandler = getObjectFactory().newInstance(DefaultDependencyVersionInformationHandler.class); diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/NeoGradleProblemReporter.java b/common/src/main/java/net/neoforged/gradle/common/extensions/NeoGradleProblemReporter.java deleted file mode 100644 index 2879fe5c4..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/NeoGradleProblemReporter.java +++ /dev/null @@ -1,114 +0,0 @@ -package net.neoforged.gradle.common.extensions; - - -import net.neoforged.gradle.common.util.NeoGradleUtils; -import org.gradle.api.Action; -import org.gradle.api.InvalidUserDataException; -import org.gradle.api.logging.Logger; -import org.gradle.api.problems.ProblemReporter; -import org.gradle.api.problems.ProblemSpec; -import org.gradle.api.problems.Severity; - -public class NeoGradleProblemReporter { - - private final ProblemReporter delegate; - - public NeoGradleProblemReporter(ProblemReporter delegate) { - this.delegate = delegate; - } - - public void reporting(Action spec, Logger logger) { - delegate.reporting(problemSpec -> { - final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); - spec.execute(neoGradleProblemSpec); - - final String url = readMeUrl(neoGradleProblemSpec.section); - - problemSpec.id(neoGradleProblemSpec.category, neoGradleProblemSpec.id) - .contextualLabel(neoGradleProblemSpec.contextualLabel) - .solution(neoGradleProblemSpec.solution) - .details(neoGradleProblemSpec.details) - .severity(Severity.WARNING) - .documentedAt(url); - - neoGradleProblemSpec.log(logger); - }); - - - } - - public RuntimeException throwing(Action spec) { - return delegate.throwing(problemSpec -> { - final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); - spec.execute(neoGradleProblemSpec); - - final String url = readMeUrl(neoGradleProblemSpec.section); - - problemSpec.id(neoGradleProblemSpec.category, neoGradleProblemSpec.id) - .contextualLabel(neoGradleProblemSpec.contextualLabel) - .solution(neoGradleProblemSpec.solution) - .details(neoGradleProblemSpec.details) - .severity(Severity.ERROR) - .withException(new InvalidUserDataException( "(%s) %s.\nPotential Solution: %s.\nMore information: %s".formatted( - neoGradleProblemSpec.contextualLabel, - neoGradleProblemSpec.details, - neoGradleProblemSpec.solution, - url - ))) - .documentedAt(url); - }); - } - - private static String readMeUrl(String section) { - final String neogradleVersion = NeoGradleUtils.getNeogradleVersion(); - final String branchMajorVersion = neogradleVersion.split("\\.")[0]; - final String branchMinorVersion = neogradleVersion.split("\\.")[1]; - final String branch = "NG_%s.%s".formatted(branchMajorVersion, branchMinorVersion); - - return "https://github.com/neoforged/NeoGradle/blob/%s/README.md#%s".formatted(branch, section); - } - - public static final class NeoGradleProblemSpec { - private String category; - private String id; - private String contextualLabel; - private String solution; - private String details; - private String section; - - public NeoGradleProblemSpec id(String category, String id) { - this.category = category; - this.id = id; - return this; - } - - public NeoGradleProblemSpec contextualLabel(String contextualLabel) { - this.contextualLabel = contextualLabel; - return this; - } - - public NeoGradleProblemSpec solution(String solution) { - this.solution = solution; - return this; - } - - public NeoGradleProblemSpec details(String details) { - this.details = details; - return this; - } - - public NeoGradleProblemSpec section(String section) { - this.section = section; - return this; - } - - private void log(Logger logger) { - logger.warn("-------------------------------------"); - logger.warn("NeoGradle detected a problem with your project: %s".formatted(contextualLabel)); - logger.warn("Details: %s".formatted(details)); - logger.warn("Potential Solution: %s".formatted(solution)); - logger.warn("More information: %s".formatted(readMeUrl(section))); - logger.warn("-------------------------------------"); - } - } -} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IProblemReporter.java b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IProblemReporter.java new file mode 100644 index 000000000..c8b40861a --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IProblemReporter.java @@ -0,0 +1,26 @@ +package net.neoforged.gradle.common.extensions.problems; + +import org.gradle.api.Action; +import org.gradle.api.logging.Logger; + +/** + * NeoGradle problems reporter API + */ +public interface IProblemReporter { + + /** + * Reports a problem to the user without stopping the build. + * + * @param spec An action that configures the problem spec + * @param logger A logger to log the problem to + */ + void reporting(Action spec, Logger logger); + + /** + * Reports a problem to the user and stops the build. + * + * @param spec An action that configures the problem spec + * @return A runtime exception to throw + */ + RuntimeException throwing(Action spec); +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IntegratedProblemReporter.java b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IntegratedProblemReporter.java new file mode 100644 index 000000000..6548de055 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IntegratedProblemReporter.java @@ -0,0 +1,61 @@ +package net.neoforged.gradle.common.extensions.problems; + +import org.gradle.api.Action; +import org.gradle.api.InvalidUserDataException; +import org.gradle.api.logging.Logger; +import org.gradle.api.problems.Severity; +import org.gradle.api.problems.ProblemReporter; + +import javax.inject.Inject; + +/** + * Implements a problem reporter that integrates with the Gradles own problem reporter, while + * also logging directly to the user where needed + */ +public class IntegratedProblemReporter implements IProblemReporter { + + private final ProblemReporter delegate; + + @Inject + public IntegratedProblemReporter(ProblemReporter delegate) { + this.delegate = delegate; + } + + @Override + public void reporting(Action spec, Logger logger) { + delegate.reporting(problemSpec -> { + final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); + spec.execute(neoGradleProblemSpec); + + problemSpec.id(neoGradleProblemSpec.category(), neoGradleProblemSpec.id()) + .contextualLabel(neoGradleProblemSpec.contextualLabel()) + .solution(neoGradleProblemSpec.solution()) + .details(neoGradleProblemSpec.details()) + .severity(Severity.WARNING) + .documentedAt(neoGradleProblemSpec.documentedAt()); + + neoGradleProblemSpec.log(logger); + }); + } + + @Override + public RuntimeException throwing(Action spec) { + return delegate.throwing(problemSpec -> { + final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); + spec.execute(neoGradleProblemSpec); + + problemSpec.id(neoGradleProblemSpec.category(), neoGradleProblemSpec.id()) + .contextualLabel(neoGradleProblemSpec.contextualLabel()) + .solution(neoGradleProblemSpec.solution()) + .details(neoGradleProblemSpec.details()) + .severity(Severity.ERROR) + .withException(new InvalidUserDataException( "(%s) %s.\nPotential Solution: %s.\nMore information: %s".formatted( + neoGradleProblemSpec.contextualLabel(), + neoGradleProblemSpec.details(), + neoGradleProblemSpec.solution(), + neoGradleProblemSpec.documentedAt() + ))) + .documentedAt(neoGradleProblemSpec.documentedAt()); + }); + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IsolatedProblemReporter.java b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IsolatedProblemReporter.java new file mode 100644 index 000000000..e05187265 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/IsolatedProblemReporter.java @@ -0,0 +1,28 @@ +package net.neoforged.gradle.common.extensions.problems; + +import org.gradle.api.Action; +import org.gradle.api.InvalidUserDataException; +import org.gradle.api.logging.Logger; + +public class IsolatedProblemReporter implements IProblemReporter { + + @Override + public void reporting(Action spec, Logger logger) { + final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); + spec.execute(neoGradleProblemSpec); + neoGradleProblemSpec.log(logger); + } + + @Override + public RuntimeException throwing(Action spec) { + final NeoGradleProblemSpec neoGradleProblemSpec = new NeoGradleProblemSpec(); + spec.execute(neoGradleProblemSpec); + + throw new InvalidUserDataException( "(%s) %s.\nPotential Solution: %s.\nMore information: %s".formatted( + neoGradleProblemSpec.contextualLabel(), + neoGradleProblemSpec.details(), + neoGradleProblemSpec.solution(), + neoGradleProblemSpec.documentedAt() + )); + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/problems/NeoGradleProblemSpec.java b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/NeoGradleProblemSpec.java new file mode 100644 index 000000000..639a5e12f --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/NeoGradleProblemSpec.java @@ -0,0 +1,85 @@ +package net.neoforged.gradle.common.extensions.problems; + +import net.neoforged.gradle.common.util.NeoGradleUtils; +import org.gradle.api.logging.Logger; + +public final class NeoGradleProblemSpec { + private String category; + private String id; + private String contextualLabel; + private String solution; + private String details; + private String section; + + public NeoGradleProblemSpec id(String category, String id) { + this.category = category; + this.id = id; + return this; + } + + public NeoGradleProblemSpec contextualLabel(String contextualLabel) { + this.contextualLabel = contextualLabel; + return this; + } + + public NeoGradleProblemSpec solution(String solution) { + this.solution = solution; + return this; + } + + public NeoGradleProblemSpec details(String details) { + this.details = details; + return this; + } + + public NeoGradleProblemSpec section(String section) { + this.section = section; + return this; + } + + String category() { + return category; + } + + String id() { + return id; + } + + String contextualLabel() { + return contextualLabel; + } + + String solution() { + return solution; + } + + String details() { + return details; + } + + String getSection() { + return section; + } + + String documentedAt() { + return readMeUrl(section); + } + + void log(Logger logger) { + logger.warn("-------------------------------------"); + logger.warn("NeoGradle detected a problem with your project: %s".formatted(contextualLabel)); + logger.warn("Details: %s".formatted(details)); + logger.warn("Potential Solution: %s".formatted(solution)); + logger.warn("More information: %s".formatted(documentedAt())); + logger.warn("-------------------------------------"); + } + + private static String readMeUrl(String section) { + final String neogradleVersion = NeoGradleUtils.getNeogradleVersion(); + final String branchMajorVersion = neogradleVersion.split("\\.")[0]; + final String branchMinorVersion = neogradleVersion.split("\\.")[1]; + final String branch = "NG_%s.%s".formatted(branchMajorVersion, branchMinorVersion); + + return "https://github.com/neoforged/NeoGradle/blob/%s/README.md#%s".formatted(branch, section); + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/problems/ProblemReportingConfigurator.java b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/ProblemReportingConfigurator.java new file mode 100644 index 000000000..537200e45 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/problems/ProblemReportingConfigurator.java @@ -0,0 +1,20 @@ +package net.neoforged.gradle.common.extensions.problems; + +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; +import org.gradle.api.Project; +import org.gradle.api.problems.Problems; + +public class ProblemReportingConfigurator { + + public static final String PROBLEM_NAMESPACE = "neoforged.gradle"; + public static final String PROBLEM_REPORTER_EXTENSION_NAME = "neogradleProblems"; + + public static void configureProblemReporting(Project project, Problems problems) { + final boolean enableGradleProblemReporting = project.getExtensions().getByType(Subsystems.class).getIntegration().getUseGradleProblemReporting().get(); + if (enableGradleProblemReporting) { + project.getExtensions().create(IProblemReporter.class, PROBLEM_REPORTER_EXTENSION_NAME, IntegratedProblemReporter.class, problems.forNamespace(PROBLEM_NAMESPACE)); + } else { + project.getExtensions().create(IProblemReporter.class, PROBLEM_REPORTER_EXTENSION_NAME, IsolatedProblemReporter.class); + } + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetDependencyExtensionImpl.java b/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetDependencyExtensionImpl.java index 56e40cbd5..68211c19f 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetDependencyExtensionImpl.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetDependencyExtensionImpl.java @@ -1,6 +1,6 @@ package net.neoforged.gradle.common.extensions.sourcesets; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.util.SourceSetUtils; import net.neoforged.gradle.dsl.common.extensions.sourceset.SourceSetDependencyExtension; import net.neoforged.gradle.dsl.common.extensions.sourceset.SourceSetInheritanceExtension; @@ -30,7 +30,7 @@ public void on(SourceSet sourceSet) { final Project sourceSetProject = SourceSetUtils.getProject(sourceSet); if (sourceSetProject != project) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(spec -> spec .id("source-set-dependencies", "wrong-project") .contextualLabel("on(SourceSet)") diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetInheritanceExtensionImpl.java b/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetInheritanceExtensionImpl.java index 39ed390ac..02e3b5556 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetInheritanceExtensionImpl.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/sourcesets/SourceSetInheritanceExtensionImpl.java @@ -1,6 +1,6 @@ package net.neoforged.gradle.common.extensions.sourcesets; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.util.SourceSetUtils; import net.neoforged.gradle.dsl.common.extensions.sourceset.SourceSetInheritanceExtension; import org.gradle.api.Project; @@ -29,7 +29,7 @@ public void from(SourceSet sourceSet) { final Project sourceSetProject = SourceSetUtils.getProject(sourceSet); if (sourceSetProject != project) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(spec -> spec .id("source-set-inheritance", "wrong-project") .contextualLabel("from(SourceSet)") diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/IntegrationExtensions.java b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/IntegrationExtensions.java new file mode 100644 index 000000000..622403dcf --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/IntegrationExtensions.java @@ -0,0 +1,19 @@ +package net.neoforged.gradle.common.extensions.subsystems; + +import net.neoforged.gradle.common.extensions.base.WithEnabledProperty; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Integration; +import org.gradle.api.Project; + +import javax.inject.Inject; + +public abstract class IntegrationExtensions extends WithEnabledProperty implements Integration { + + @Inject + public IntegrationExtensions(Project project) { + super(project, "integrations"); + + getUseGradleProblemReporting().set( + getBooleanProperty("gradle-problem-reporting", false, false) + ); + } +} diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java index c5dbd2525..80cf153b9 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java @@ -22,12 +22,14 @@ public abstract class SubsystemsExtension extends WithPropertyLookup implements private final Conventions conventions; private final Parchment parchment; private final Tools tools; + private final Integration integration; @Inject public SubsystemsExtension(Project project) { super(project); + this.integration = project.getObjects().newInstance(IntegrationExtensions.class, project); this.conventions = project.getObjects().newInstance(ConventionsExtension.class, project); this.parchment = project.getObjects().newInstance(ParchmentExtensions.class, project); this.tools = project.getObjects().newInstance(ToolsExtension.class, project); @@ -118,6 +120,11 @@ private void configureParchmentDefaults() { }); } + @Override + public Integration getIntegration() { + return integration; + } + @Override public Conventions getConventions() { return conventions; diff --git a/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java b/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java index 0363637c5..f2c88db22 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java +++ b/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java @@ -2,7 +2,7 @@ import com.google.common.collect.Multimap; import net.minecraftforge.gdi.ConfigurableDSLElement; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.runtime.definition.CommonRuntimeDefinition; import net.neoforged.gradle.common.util.ConfigurationUtils; import net.neoforged.gradle.common.util.SourceSetUtils; @@ -202,7 +202,7 @@ public void overrideArguments(ListProperty arguments) { @Deprecated public ListProperty getProgramArguments() { - getProject().getExtensions().getByType(NeoGradleProblemReporter.class) + getProject().getExtensions().getByType(IProblemReporter.class) .reporting(problem -> problem .id("deprecated-method", "Deprecated method") .contextualLabel("Run.getProgramArguments()") @@ -471,7 +471,7 @@ private void configureFromSDKs() { } }, () -> unconfiguredSourceSets.add(sourceSet)); } catch (MultipleDefinitionsFoundException e) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(problem -> problem .id("multiple-definitions-found", "Multiple runtime definitions found") .contextualLabel("Run: " + this.getName()) @@ -584,7 +584,7 @@ private void configureRunSpecification() { @Deprecated @Override public final void configure(final @NotNull String name) { - getProject().getExtensions().getByType(NeoGradleProblemReporter.class) + getProject().getExtensions().getByType(IProblemReporter.class) .reporting(problem -> problem .id("deprecated-method", "Deprecated method") .contextualLabel("Run.configure(String)") @@ -647,7 +647,7 @@ private Provider> getRunTypesByName(String name) { .toList() ))).map(types -> { if (types.isEmpty()) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(problem -> problem .id("run-type-not-found", "Run type not found") .contextualLabel("The run type '%s' was not found".formatted(name)) diff --git a/common/src/main/java/net/neoforged/gradle/common/tasks/JarJar.java b/common/src/main/java/net/neoforged/gradle/common/tasks/JarJar.java index 557b85bc3..a34a97024 100644 --- a/common/src/main/java/net/neoforged/gradle/common/tasks/JarJar.java +++ b/common/src/main/java/net/neoforged/gradle/common/tasks/JarJar.java @@ -2,7 +2,7 @@ import net.neoforged.gradle.common.dependency.JarJarArtifacts; import net.neoforged.gradle.common.dependency.ResolvedJarJarArtifact; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.manifest.DefaultInheritManifest; import net.neoforged.gradle.common.manifest.InheritManifest; import net.neoforged.gradle.dsl.common.dependency.DependencyFilter; @@ -34,7 +34,7 @@ public abstract class JarJar extends Jar { public JarJar() { this.artifacts = getProject().getObjects().newInstance(JarJarArtifacts.class, - getProject().getExtensions().getByType(NeoGradleProblemReporter.class) + getProject().getExtensions().getByType(IProblemReporter.class) ); this.jarJarCopySpec = this.getMainSpec().addChild(); this.jarJarCopySpec.into("META-INF/jarjar"); diff --git a/common/src/main/java/net/neoforged/gradle/common/util/run/RunsUtil.java b/common/src/main/java/net/neoforged/gradle/common/util/run/RunsUtil.java index 142c7a376..061b1809b 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/run/RunsUtil.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/run/RunsUtil.java @@ -3,7 +3,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import net.neoforged.gradle.common.extensions.IdeManagementExtension; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; +import net.neoforged.gradle.common.extensions.problems.IProblemReporter; import net.neoforged.gradle.common.runs.run.RunImpl; import net.neoforged.gradle.common.tasks.RenderDocDownloaderTask; import net.neoforged.gradle.common.util.ClasspathUtils; @@ -160,7 +160,7 @@ public static void setupModSources(Project project, Run run, boolean isInternal) //Warn the user if no source sets are configured if (run.getModSources().all().get().isEmpty()) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(problemSpec -> problemSpec.id("runs", "noSourceSetsConfigured") .contextualLabel("Run: " + run.getName()) @@ -201,7 +201,7 @@ public static void setupDevLoginSupport(Project project, Run run) { //Set the main class to the dev login tool run.getMainClass().set(devLogin.getMainClass()); } else if (!run.getIsClient().get() && runsDevLogin.getIsEnabled().get()) { - final NeoGradleProblemReporter reporter = project.getExtensions().getByType(NeoGradleProblemReporter.class); + final IProblemReporter reporter = project.getExtensions().getByType(IProblemReporter.class); throw reporter.throwing(spec -> spec .id("runs", "dev-login-not-supported") .contextualLabel("Run: " + run.getName()) 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/gradle.properties b/gradle.properties index 2cd61bc13..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 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 From 91907c67fcb6eb17b02fc6ba7d907e7ae3267f79 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 20 Oct 2024 12:22:08 +0200 Subject: [PATCH 04/11] Several fixes for broken tests --- .../net/neoforged/gradle/common/util/NeoGradleUtils.java | 1 - .../userdev/runtime/extension/UserDevRuntimeExtension.java | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/net/neoforged/gradle/common/util/NeoGradleUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/NeoGradleUtils.java index 0036a40bb..694e83a96 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/NeoGradleUtils.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/NeoGradleUtils.java @@ -1,6 +1,5 @@ package net.neoforged.gradle.common.util; -import net.neoforged.gradle.common.extensions.NeoGradleProblemReporter; import org.jetbrains.annotations.NotNull; import java.io.IOException; diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java index 2845d56b4..93df61cff 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java @@ -138,6 +138,11 @@ private TaskTreeAdapter createAccessTransformerAdapter(final String accessTransf userDev.matching(filter -> filter.include(accessTransformerDirectory + "/**")); return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> { + if (accessTransformerFiles.isEmpty()) { + // No access transformers found, so we don't need to do anything + return null; + } + final TaskProvider 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)); From fa28bfba882d186fbd548b45436ac49b85fe5516 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 20 Oct 2024 14:48:52 +0200 Subject: [PATCH 05/11] Fix vanilla user transformer not detecting missing files and not running. --- .../common/util/CommonRuntimeTaskUtils.java | 4 +-- .../extensions/VanillaRuntimeExtension.java | 34 ++++++++++--------- .../steps/ApplyAccessTransformerStep.java | 5 ++- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java index 171408bf7..050f7a2c8 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java @@ -9,8 +9,6 @@ import org.gradle.api.file.FileTree; import org.gradle.api.tasks.TaskProvider; -import java.io.File; - public final class CommonRuntimeTaskUtils { private CommonRuntimeTaskUtils() { @@ -35,7 +33,7 @@ public static TaskProvider createSourceInter }); } - public static TaskProvider createBinaryAccessTransformer(Definition definition, String namePreFix, File workspaceDirectory, FileTree files) { + public static TaskProvider createBinaryAccessTransformer(Definition definition, String namePreFix, FileTree files) { return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sAccessTransformer", StringCapitalizationUtils.capitalize(namePreFix))), BinaryAccessTransformer.class, task -> { task.getTransformers().from(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 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 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 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 f58514a76..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 @@ -24,10 +24,13 @@ public TaskProvider buildTask(VanillaRuntimeDefinition defini final Minecraft minecraftExtension = definition.getSpecification().getProject().getExtensions().getByType(Minecraft.class); final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers(); + if (accessTransformerFiles.getFiles().isEmpty()) { + return null; + } + final TaskProvider task = CommonRuntimeTaskUtils.createBinaryAccessTransformer( definition, "user", - workingDirectory, accessTransformerFiles.getFiles().getAsFileTree() ); From b85c040b4118736cff0a51d2f271326ec1323d66 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Mon, 21 Oct 2024 18:52:23 +0200 Subject: [PATCH 06/11] Fix a copy paste error caused by replacement --- .../common/interfaceinjection/InterfaceInjectionPublishing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java index b5235461c..db957e41d 100644 --- a/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java +++ b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java @@ -13,7 +13,7 @@ public class InterfaceInjectionPublishing { public static final String INTERFACE_INJECTION_ELEMENTS_CONFIGURATION = "InterfaceInjectionElements"; public static final String INTERFACE_INJECTION_API_CONFIGURATION = "InterfaceInjectionApi"; public static final String INTERFACE_INJECTION_CONFIGURATION = "InterfaceInjection"; - public static final String INTERFACE_INJECTION_CATEGORY = "InterfaceInjection"; + public static final String INTERFACE_INJECTION_CATEGORY = "interfaceInjection"; public static void setup(Project project) { InterfaceInjections InterfaceInjectionsExtension = project.getExtensions().getByType(InterfaceInjections.class); From 24c0adc34887d99555ffb1364e77026e4ee9e06d Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Mon, 28 Oct 2024 14:43:44 +0100 Subject: [PATCH 07/11] Lowercase completely --- .../common/interfaceinjection/InterfaceInjectionPublishing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java index db957e41d..4f3f52973 100644 --- a/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java +++ b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java @@ -13,7 +13,7 @@ public class InterfaceInjectionPublishing { public static final String INTERFACE_INJECTION_ELEMENTS_CONFIGURATION = "InterfaceInjectionElements"; public static final String INTERFACE_INJECTION_API_CONFIGURATION = "InterfaceInjectionApi"; public static final String INTERFACE_INJECTION_CONFIGURATION = "InterfaceInjection"; - public static final String INTERFACE_INJECTION_CATEGORY = "interfaceInjection"; + public static final String INTERFACE_INJECTION_CATEGORY = "interfaceinjection"; public static void setup(Project project) { InterfaceInjections InterfaceInjectionsExtension = project.getExtensions().getByType(InterfaceInjections.class); From e6f6b78d23be8a79ca780522cf22cd6f3650235d Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Thu, 31 Oct 2024 11:37:07 +0100 Subject: [PATCH 08/11] Swap back to detached configurations for internal configurations. This prevents a CME caused by the resolution of the dependent projects configurations triggering the creation of new configuration in its configuration container. Creating detached configurations, and using them either as tool resolvers, or as carriers for dependency objects which are then attached to an outer configuration should prevent this. Added tests that cover the new corver cases, and validate publishing logic --- .../gradle/common/CommonProjectPlugin.java | 4 + .../conventions/ConventionConfigurator.java | 17 ++- .../replacement/ReplacementLogic.java | 60 ++++----- .../gradle/common/runs/run/RunImpl.java | 2 +- .../common/util/ConfigurationUtils.java | 122 +++++++----------- .../common/util/TaskDependencyUtils.java | 2 +- .../replacement/DependencyReplacement.groovy | 7 +- examples/multi_neoforge_root/api/build.gradle | 16 +++ .../multi_neoforge_root/api/gradle.properties | 3 + .../api/runs/junit/junit_jvm_args.txt | 24 ++++ .../api/runs/junit/junit_test_args.txt | 36 ++++++ .../gradle/apitest/FunctionalTests.java | 11 ++ examples/multi_neoforge_root/build.gradle | 11 ++ .../multi_neoforge_root/gradle.properties | 3 + .../gradle/libs.versions.toml | 6 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + .../multi_neoforge_root/main/build.gradle | 15 +++ .../main/gradle.properties | 3 + .../net/neoforged/gradle/main/ApiTests.java | 13 ++ examples/multi_neoforge_root/settings.gradle | 18 +++ examples/runs/build.gradle | 8 +- .../extensions/NeoFormRuntimeExtension.java | 9 +- .../gradle/userdev/FunctionalTests.groovy | 74 +++++++++++ .../gradle/userdev/MultiProjectTests.groovy | 96 ++++++++++++++ .../dependency/UserDevDependencyManager.java | 4 +- .../definition/UserDevRuntimeDefinition.java | 6 +- 27 files changed, 446 insertions(+), 131 deletions(-) create mode 100644 examples/multi_neoforge_root/api/build.gradle create mode 100644 examples/multi_neoforge_root/api/gradle.properties create mode 100644 examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt create mode 100644 examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt create mode 100644 examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java create mode 100644 examples/multi_neoforge_root/build.gradle create mode 100644 examples/multi_neoforge_root/gradle.properties create mode 100644 examples/multi_neoforge_root/gradle/libs.versions.toml create mode 100644 examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.properties create mode 100644 examples/multi_neoforge_root/main/build.gradle create mode 100644 examples/multi_neoforge_root/main/gradle.properties create mode 100644 examples/multi_neoforge_root/main/src/main/java/net/neoforged/gradle/main/ApiTests.java create mode 100644 examples/multi_neoforge_root/settings.gradle diff --git a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java index c87bc2302..4f21ff6c7 100644 --- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java +++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java @@ -153,6 +153,10 @@ public void apply(Project project) { //Set up reporting tasks project.getTasks().register("runs", RunsReport.class); + //Configure sdk configurations + final SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); + sourceSets.all(ConfigurationUtils::getSdkConfiguration); + //Needs to be before after evaluate ConventionConfigurator.configureConventions(project); diff --git a/common/src/main/java/net/neoforged/gradle/common/conventions/ConventionConfigurator.java b/common/src/main/java/net/neoforged/gradle/common/conventions/ConventionConfigurator.java index 996208619..e2decf863 100644 --- a/common/src/main/java/net/neoforged/gradle/common/conventions/ConventionConfigurator.java +++ b/common/src/main/java/net/neoforged/gradle/common/conventions/ConventionConfigurator.java @@ -10,6 +10,7 @@ import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.Runs; import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.SourceSets; import net.neoforged.gradle.dsl.common.extensions.subsystems.conventions.ide.IDEA; +import net.neoforged.gradle.dsl.common.runs.run.Run; import net.neoforged.gradle.dsl.common.runs.run.RunManager; import org.gradle.StartParameter; import org.gradle.TaskExecutionRequest; @@ -62,11 +63,23 @@ private static void configureRunConventions(Project project, Conventions convent final Configuration runRuntimeConfiguration = project.getConfigurations().maybeCreate(configurations.getRunRuntimeConfigurationName().get()); project.getExtensions().configure(RunManager.class, runContainer -> runContainer.configureAll(run -> { + run.getDependencies().getRuntime().add(runRuntimeConfiguration); + })); + + final RunManager runManager = project.getExtensions().getByType(RunManager.class); + project.getConfigurations().addRule("Create run specific runtime configuration", configurationName -> { + if (!configurationName.endsWith(configurations.getPerRunRuntimeConfigurationPostFix().get())) + return; + + final String runName = configurationName.substring(0, configurationName.length() - configurations.getPerRunRuntimeConfigurationPostFix().get().length()); + if (runManager.findByName(runName) == null) + return; + + final Run run = runManager.getByName(runName); final Configuration runSpecificRuntimeConfiguration = project.getConfigurations().maybeCreate(ConfigurationUtils.getRunName(run, configurations.getPerRunRuntimeConfigurationPostFix().get())); - run.getDependencies().getRuntime().add(runRuntimeConfiguration); run.getDependencies().getRuntime().add(runSpecificRuntimeConfiguration); - })); + }); } private static void configureIDEConventions(Project project, Conventions conventions) { diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/dependency/replacement/ReplacementLogic.java b/common/src/main/java/net/neoforged/gradle/common/extensions/dependency/replacement/ReplacementLogic.java index 2d14ce9de..23be5b835 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/dependency/replacement/ReplacementLogic.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/dependency/replacement/ReplacementLogic.java @@ -5,6 +5,7 @@ import net.minecraftforge.gdi.ConfigurableDSLElement; import net.neoforged.gradle.common.extensions.IdeManagementExtension; import net.neoforged.gradle.common.tasks.ArtifactFromOutput; +import net.neoforged.gradle.common.util.ConfigurationUtils; import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacement; import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacementHandler; import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.ReplacementAware; @@ -13,7 +14,6 @@ import net.neoforged.gradle.dsl.common.extensions.repository.Repository; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils; -import net.neoforged.gradle.common.util.ConfigurationUtils; import org.gradle.api.GradleException; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; @@ -27,6 +27,7 @@ import javax.inject.Inject; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * Defines the implementation of the @{link DependencyReplacement} extension. @@ -39,7 +40,7 @@ public abstract class ReplacementLogic implements ConfigurableDSLElement> dependencyReplacementInformation = HashBasedTable.create(); private final Table repositoryEntries = HashBasedTable.create(); - private final Table originalDependencyLookup = HashBasedTable.create(); + private final Map originalDependencyLookup = new ConcurrentHashMap<>(); private final NamedDomainObjectContainer dependencyReplacementHandlers; private final List whenDependencyReplaced = new ArrayList<>(); @@ -59,9 +60,7 @@ public ReplacementLogic(Project project) { public void whenDependencyReplaced(DependencyReplacedCallback dependencyAction) { this.whenDependencyReplaced.add(dependencyAction); - for (Table.Cell dependencyConfigurationDependencyCell : this.originalDependencyLookup.cellSet()) { - dependencyAction.apply(dependencyConfigurationDependencyCell.getRowKey(), dependencyConfigurationDependencyCell.getColumnKey(), dependencyConfigurationDependencyCell.getValue()); - } + this.originalDependencyLookup.forEach(dependencyAction::apply); } @Override @@ -113,18 +112,9 @@ public NamedDomainObjectContainer getReplacementHa @NotNull @Override - public Dependency optionallyConvertBackToOriginal(@NotNull final Dependency dependency, Configuration configuration) { - final Dependency originalDependency = originalDependencyLookup.get(dependency, configuration); - if (originalDependency == null && !configuration.getExtendsFrom().isEmpty()) { - //Check if we have a parent configuration that might have the original dependency. - for (Configuration parentConfiguration : configuration.getExtendsFrom()) { - return optionallyConvertBackToOriginal(dependency, parentConfiguration); - } - } else if (originalDependency != null) { - return originalDependency; - } - - return dependency; + public Dependency optionallyConvertBackToOriginal(@NotNull final Dependency dependency) { + final Dependency originalDependency = originalDependencyLookup.get(dependency); + return Objects.requireNonNullElse(originalDependency, dependency); } /** @@ -273,36 +263,34 @@ private void handleDependencyReplacement(Configuration configuration, Dependency //Find the configurations that the dependency should be replaced in. final List targetConfigurations = ConfigurationUtils.findReplacementConfigurations(project, configuration); + //Create a dependency from the tasks that copies the raw jar to the repository. + //The sources jar is not needed here. + final ConfigurableFileCollection replacedFiles = createDependencyFromTask(rawTask); + final Dependency replacedDependency = project.getDependencies().create(replacedFiles); + final Dependency localRepoDependency = newRepoEntry.getDependency(); + //For each configuration that we target we now need to add the new dependencies to. for (Configuration targetConfiguration : targetConfigurations) { try { - //Create a dependency from the tasks that copies the raw jar to the repository. - //The sources jar is not needed here. - final Provider replacedFiles = createDependencyFromTask(rawTask); - //Add the new dependency to the target configuration. final DependencySet targetDependencies = targetConfiguration == configuration ? configuredSet : targetConfiguration.getDependencies(); - final Provider replacedDependencies = replacedFiles - .map(files -> project.getDependencies().create(files)); - final Provider newRepoDependency = project.provider(newRepoEntry::getDependency); - //Add the new dependency to the target configuration. - targetDependencies.addLater(replacedDependencies); - targetDependencies.addLater(newRepoDependency); - - //Keep track of the original dependency, so we can convert back if needed. - originalDependencyLookup.put(newRepoEntry.getDependency(), targetConfiguration, dependency); - - for (DependencyReplacedCallback dependencyReplacedCallback : this.whenDependencyReplaced) { - dependencyReplacedCallback.apply(newRepoEntry.getDependency(), targetConfiguration, dependency); - } + targetDependencies.add(replacedDependency); + targetDependencies.add(localRepoDependency); } catch (Exception exception) { throw new GradleException("Failed to add the replaced dependency to the configuration " + targetConfiguration.getName() + ": " + exception.getMessage(), exception); } } + + //Keep track of the original dependency, so we can convert back if needed. + originalDependencyLookup.put(localRepoDependency, dependency); + + for (DependencyReplacedCallback dependencyReplacedCallback : this.whenDependencyReplaced) { + dependencyReplacedCallback.apply(localRepoDependency, dependency); + } } /** @@ -475,7 +463,7 @@ Entry createDummyDependency(final Dependency dependency, final ReplacementResult return entry; } - public Provider createDependencyFromTask(TaskProvider task) { - return task.map(taskWithOutput -> project.files(taskWithOutput.getOutput())); + public ConfigurableFileCollection createDependencyFromTask(TaskProvider task) { + return project.files(task.flatMap(WithOutput::getOutput)); } } diff --git a/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java b/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java index f2c88db22..c542ce32f 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java +++ b/common/src/main/java/net/neoforged/gradle/common/runs/run/RunImpl.java @@ -483,7 +483,7 @@ private void configureFromSDKs() { }); final DependencyReplacement replacementLogic = project.getExtensions().getByType(DependencyReplacement.class); - replacementLogic.whenDependencyReplaced((virtualDependency, targetConfiguration, originalDependency) -> { + replacementLogic.whenDependencyReplaced((virtualDependency, originalDependency) -> { if (unconfiguredSourceSets.isEmpty()) { return; } diff --git a/common/src/main/java/net/neoforged/gradle/common/util/ConfigurationUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/ConfigurationUtils.java index 1bfd25927..14cd9f7d2 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/ConfigurationUtils.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/ConfigurationUtils.java @@ -1,10 +1,8 @@ package net.neoforged.gradle.common.util; import net.neoforged.gradle.dsl.common.extensions.dependency.replacement.DependencyReplacement; -import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; import net.neoforged.gradle.dsl.common.runs.run.Run; import org.apache.commons.lang3.StringUtils; -import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.StringGroovyMethods; import org.gradle.api.Action; import org.gradle.api.Project; @@ -17,7 +15,10 @@ import org.gradle.util.internal.GUtil; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.function.Function; public class ConfigurationUtils { @@ -28,6 +29,21 @@ private ConfigurationUtils() { throw new IllegalStateException("Can not instantiate an instance of: ConfigurationUtils. This is a utility class"); } + /** + * Extends the dependencies and dependency constraints of the target configuration with the dependencies and dependency constraints of the given configurations. + * + * @param project The project to create the configuration for + * @param target The configuration to extend + * @param configurations The configurations to extend from + */ + public static void extendsFrom(final Project project, final Configuration target, final Configuration... configurations) { + for (Configuration configuration : configurations) { + //We treat each configuration as a dependency collector in and of it-self, and copy the dependencies and dependency constraints to the target configuration. + target.getDependencies().addAllLater(project.provider(configuration::getDependencies)); + target.getDependencyConstraints().addAllLater(project.provider(configuration::getDependencyConstraints)); + } + } + /** * Creates a configuration that can be resolved, but not consumed. * @@ -37,18 +53,10 @@ private ConfigurationUtils() { * @return The detached configuration */ public static Configuration temporaryConfiguration(final Project project, final String context, final Dependency... dependencies) { - final Configuration configuration = project.getConfigurations().maybeCreate("neoGradleInternal" + StringGroovyMethods.capitalize(context)); - - if (configuration.getDependencies().isEmpty()) { - DefaultGroovyMethods.addAll(configuration.getDependencies(), dependencies); - - configuration.setCanBeConsumed(false); - configuration.setCanBeResolved(true); - - final DependencyReplacement dependencyReplacement = project.getExtensions().getByType(DependencyReplacement.class); - dependencyReplacement.handleConfiguration(configuration); - } + final Configuration configuration = project.getConfigurations().detachedConfiguration(dependencies); + final DependencyReplacement dependencyReplacement = project.getExtensions().getByType(DependencyReplacement.class); + dependencyReplacement.handleConfiguration(configuration); return configuration; } @@ -62,23 +70,13 @@ public static Configuration temporaryConfiguration(final Project project, final * @return The detached configuration */ public static Configuration temporaryConfiguration(final Project project, final String context, final Action processor) { - final String name = "neoGradleInternal" + StringGroovyMethods.capitalize(context); - final boolean exists = project.getConfigurations().getNames().contains(name); + final Configuration config = project.getConfigurations().detachedConfiguration(); + processor.execute(config); - final Configuration configuration = project.getConfigurations().maybeCreate("neoGradleInternal" + StringGroovyMethods.capitalize(context)); + final DependencyReplacement dependencyReplacement = project.getExtensions().getByType(DependencyReplacement.class); + dependencyReplacement.handleConfiguration(config); - if (!exists) { - processor.execute(configuration); - - configuration.setCanBeConsumed(false); - configuration.setCanBeResolved(true); - - final DependencyReplacement dependencyReplacement = project.getExtensions().getByType(DependencyReplacement.class); - dependencyReplacement.handleConfiguration(configuration); - } - - - return configuration; + return config; } /** @@ -100,17 +98,9 @@ public static boolean isUnhandledConfiguration(Configuration configuration) { * @return The detached configuration */ public static Configuration temporaryUnhandledConfiguration(final ConfigurationContainer configurations, final String context, final Dependency... dependencies) { - final Configuration configuration = configurations.maybeCreate("neoGradleInternalUnhandled" + StringGroovyMethods.capitalize(context)); - UNHANDLED_CONFIGURATIONS.add(configuration); - - if (configuration.getDependencies().isEmpty()) { - configuration.getDependencies().addAll(Arrays.asList(dependencies)); - configuration.setCanBeConsumed(false); - configuration.setCanBeResolved(true); - } - - - return configuration; + final Configuration config = configurations.detachedConfiguration(dependencies); + UNHANDLED_CONFIGURATIONS.add(config); + return config; } /** @@ -122,23 +112,10 @@ public static Configuration temporaryUnhandledConfiguration(final ConfigurationC * @return The detached configuration */ public static Configuration temporaryUnhandledConfiguration(final ConfigurationContainer configurations, final String context, final Provider> dependencies) { - final String name = "neoGradleInternalUnhandled" + StringGroovyMethods.capitalize(context); - if (configurations.findByName(name) != null) { - return configurations.getByName(name); - } - - final Configuration configuration = configurations.create(name); - UNHANDLED_CONFIGURATIONS.add(configuration); - - if (configuration.getDependencies().isEmpty()) { - configuration.getDependencies().addAllLater(dependencies); - - configuration.setCanBeConsumed(false); - configuration.setCanBeResolved(true); - } - - - return configuration; + final Configuration config = configurations.detachedConfiguration(); + config.getDependencies().addAllLater(dependencies); + UNHANDLED_CONFIGURATIONS.add(config); + return config; } /** @@ -150,25 +127,16 @@ public static Configuration temporaryUnhandledConfiguration(final ConfigurationC * @return The detached configuration */ public static Configuration temporaryUnhandledNotTransitiveConfiguration(final ConfigurationContainer configurations, final String context, final Dependency... dependencies) { - final Configuration configuration = configurations.maybeCreate("neoGradleInternalUnhandled" + StringGroovyMethods.capitalize(context)); - UNHANDLED_CONFIGURATIONS.add(configuration); - - if (configuration.getDependencies().isEmpty()) { - DefaultGroovyMethods.addAll(configuration.getDependencies(), dependencies); - - configuration.setCanBeConsumed(false); - configuration.setCanBeResolved(true); - configuration.setTransitive(false); - } - - - return configuration; + final Configuration config = configurations.detachedConfiguration(dependencies); + config.setTransitive(false); + UNHANDLED_CONFIGURATIONS.add(config); + return config; } /** * Creates a provider that will resolve a temporary configuration containing the given dependency. */ - public static Provider getArtifactProvider(Project project, String context, Provider dependencyNotationProvider) { + public static Provider getArtifactProvider(Project project, String context, Provider dependencyNotationProvider) { return dependencyNotationProvider.flatMap(dependencyNotation -> { Configuration configuration = temporaryUnhandledNotTransitiveConfiguration(project.getConfigurations(), context, project.getDependencies().create(dependencyNotation)); return configuration.getElements().map(files -> files.iterator().next().getAsFile()); @@ -186,7 +154,6 @@ public static List findReplacementConfigurations(final Project pr resultContainer.add(configuration); } - return new ArrayList<>(resultContainer); } @@ -237,9 +204,11 @@ public static List findRuntimeOnlyConfigurationFromSourceSetRepla final Set supers = getAllSuperConfigurations(runtimeClasspath); if (supers.contains(runtimeOnly) && supers.contains(configuration)) { - final Configuration reallyRuntimeOnly = project.getConfigurations().maybeCreate(getSourceSetName(sourceSet, "%s%s".formatted(NEOGRADLE_RUNTIME_REPLACEMENT, StringUtils.capitalize(sourceSet.getName())))); - runtimeClasspath.extendsFrom(reallyRuntimeOnly); - targets.add(reallyRuntimeOnly); + final Configuration detachedRuntimeOnly = project.getConfigurations().detachedConfiguration(); + + extendsFrom(project, runtimeClasspath, detachedRuntimeOnly); + + targets.add(detachedRuntimeOnly); } }); @@ -314,6 +283,5 @@ public static String getTaskBaseName(final SourceSet sourceSet) { return sourceSet.getName().equals(SourceSet.MAIN_SOURCE_SET_NAME) ? "" : GUtil.toCamelCase(sourceSet.getName()); } - private static Set UNHANDLED_CONFIGURATIONS = new HashSet(); - + private static final Set UNHANDLED_CONFIGURATIONS = new HashSet(); } diff --git a/common/src/main/java/net/neoforged/gradle/common/util/TaskDependencyUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/TaskDependencyUtils.java index 70430297e..588e060f0 100644 --- a/common/src/main/java/net/neoforged/gradle/common/util/TaskDependencyUtils.java +++ b/common/src/main/java/net/neoforged/gradle/common/util/TaskDependencyUtils.java @@ -237,7 +237,7 @@ private void processConfiguration(Configuration configuration) { //Grab the original dependencies if we have a replacement extension final DependencyReplacement replacement = project.getExtensions().findByType(DependencyReplacement.class); final Set operatingSet = replacement == null ? dependencies : dependencies.stream() - .map(dependency -> replacement.optionallyConvertBackToOriginal(dependency, configuration)) + .map(replacement::optionallyConvertBackToOriginal) .collect(Collectors.toSet()); this.runtimes.stream() 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/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 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 May not be a lambda as this causes issues: In plugin 'net.neoforged.gradle.neoform.NeoFormProjectPlugin' property 'options.compilerArgumentProviders.$0' was implemented by the Java lambda 'net.neoforged.gradle.neoform.runtime.extensions.NeoFormRuntimeExtension$$Lambda$2667/0x000000010105ab98'. + task.getOptions().getCompilerArgumentProviders().add(new CommandLineArgumentProvider() { + @Override + public Iterable asArguments() { + return settings.getArgs().get(); + } + }); task.getJavaVersion().set( definition.getVersionJson() 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/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/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 f0e74743c..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 @@ -137,9 +137,9 @@ protected void buildRunInterpolationData(RunImpl run, @NotNull MapProperty Date: Sat, 2 Nov 2024 11:26:42 +0100 Subject: [PATCH 09/11] Fix first integration test that failed because of a difference with how Ingredients are constructed. --- .../extensions/IdeManagementExtension.java | 33 ++++++++++++------- .../runs/ide/IdeRunIntegrationManager.java | 8 ++--- .../neoforged/gradle/userdev/RunTests.groovy | 3 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java index 04edda606..28e1b066d 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/IdeManagementExtension.java @@ -13,6 +13,7 @@ import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.gradle.plugins.ide.idea.model.IdeaModel; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.gradle.ext.IdeaExtPlugin; import org.jetbrains.gradle.ext.ProjectSettings; import org.jetbrains.gradle.ext.TaskTriggersConfig; @@ -261,22 +262,30 @@ private void onCommonEclipse(final BiConsumer toPerform) if (!isEclipseImport()) { return; } - - //Grab the eclipse model so we can extend it. -> Done on the root project so that the model is available to all subprojects. - //And so that post sync tasks are only ran once for all subprojects. - EclipseModel model = project.getExtensions().findByType(EclipseModel.class); - if (model == null) { - model = rootProject.getExtensions().findByType(EclipseModel.class); - if (model == null) { - return; - } - } - + + EclipseModel model = getEclipseModel(); + if (model == null) return; + //Configure the project, passing the model and the relevant project. Which does not need to be the root, but can be. toPerform.accept(project, model); }); } - + + /** + * Get the eclipse model from the extensions project. + * + * @return the eclipse model, or {@code null} if not found + */ + public @Nullable EclipseModel getEclipseModel() { + //Grab the eclipse model so we can extend it. -> Done on the root project so that the model is available to all subprojects. + //And so that post sync tasks are only ran once for all subprojects. + EclipseModel model = project.getExtensions().findByType(EclipseModel.class); + if (model == null) { + model = project.getRootProject().getExtensions().findByType(EclipseModel.class); + } + return model; + } + /** * Applies the specified configuration action to configure gradle run projects only. * diff --git a/common/src/main/java/net/neoforged/gradle/common/runs/ide/IdeRunIntegrationManager.java b/common/src/main/java/net/neoforged/gradle/common/runs/ide/IdeRunIntegrationManager.java index a973d8129..b49798d65 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runs/ide/IdeRunIntegrationManager.java +++ b/common/src/main/java/net/neoforged/gradle/common/runs/ide/IdeRunIntegrationManager.java @@ -267,7 +267,7 @@ public void eclipse(Project project, EclipseModel eclipse) { return; final TaskProvider ideBeforeRunTask = getOrCreateIdeBeforeRunTask(project, runImpl); - final List> copyProcessResourcesTasks = createEclipseCopyResourcesTasks(eclipse, run); + final List> copyProcessResourcesTasks = createEclipseCopyResourcesTasks(run); ideBeforeRunTask.configure(task -> copyProcessResourcesTasks.forEach(task::dependsOn)); try { @@ -327,7 +327,7 @@ public void vscode(Project project, EclipseModel eclipse) final RunImpl runImpl = (RunImpl) run; final TaskProvider ideBeforeRunTask = getOrCreateIdeBeforeRunTask(project, runImpl); - final List> copyProcessResourcesTasks = createEclipseCopyResourcesTasks(eclipse, run); + final List> copyProcessResourcesTasks = createEclipseCopyResourcesTasks(run); ideBeforeRunTask.configure(task -> copyProcessResourcesTasks.forEach(task::dependsOn)); final LaunchConfiguration cfg = launchWriter.createGroup("NG - " + project.getName(), WritingMode.REMOVE_EXISTING) @@ -410,7 +410,7 @@ private List> createIntelliJCopyResourcesTasks(Run run) { return intelliJResourcesTask; } - private List> createEclipseCopyResourcesTasks(EclipseModel eclipse, Run run) { + private List> createEclipseCopyResourcesTasks(Run run) { final List> copyProcessResources = new ArrayList<>(); for (SourceSet sourceSet : run.getModSources().all().get().values()) { final Project sourceSetProject = SourceSetUtils.getProject(sourceSet); @@ -425,7 +425,7 @@ private List> createEclipseCopyResourcesTasks(EclipseModel eclip eclipseResourcesTask = sourceSetProject.getTasks().register(taskName, Copy.class, task -> { final TaskProvider defaultProcessResources = sourceSetProject.getTasks().named(sourceSet.getProcessResourcesTaskName(), ProcessResources.class); task.from(defaultProcessResources.map(ProcessResources::getDestinationDir)); - Path outputDir = eclipse.getClasspath().getDefaultOutputDir().toPath(); + Path outputDir = sourceSetProject.getExtensions().getByType(IdeManagementExtension.class).getEclipseModel().getClasspath().getDefaultOutputDir().toPath(); if (outputDir.endsWith("default")) { // sometimes it has default value from org.gradle.plugins.ide.eclipse.internal.EclipsePluginConstants#DEFAULT_PROJECT_OUTPUT_PATH // which has /default on end that is not present in the final outputDir in eclipse/buildship 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()) ); } } From ac144c3a43ff5443fb5c016da4d46411c5a7fe1a Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sat, 2 Nov 2024 23:13:29 +0100 Subject: [PATCH 10/11] Fix config cache recompile issues. --- .../extensions/NeoFormRuntimeExtension.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) 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 14e035f63..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; @@ -36,9 +39,9 @@ import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.file.*; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.RegularFile; import org.gradle.api.provider.Provider; -import org.gradle.api.specs.Spec; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.compile.AbstractCompile; import org.gradle.api.tasks.compile.ForkOptions; @@ -456,13 +459,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { ForkOptions forkOptions = task.getOptions().getForkOptions(); forkOptions.setMemoryMaximumSize(maxMemory); forkOptions.setJvmArgs(settings.getJvmArgs().get()); - //noinspection Convert2Lambda -> May not be a lambda as this causes issues: In plugin 'net.neoforged.gradle.neoform.NeoFormProjectPlugin' property 'options.compilerArgumentProviders.$0' was implemented by the Java lambda 'net.neoforged.gradle.neoform.runtime.extensions.NeoFormRuntimeExtension$$Lambda$2667/0x000000010105ab98'. - task.getOptions().getCompilerArgumentProviders().add(new CommandLineArgumentProvider() { - @Override - public Iterable asArguments() { - return settings.getArgs().get(); - } - }); + task.getOptions().getCompilerArgumentProviders().add(new CustomCompilerArgsProvider(settings.getArgs())); task.getJavaVersion().set( definition.getVersionJson() @@ -568,4 +565,11 @@ private static TaskProvider maybeApplyParchment(NeoFormRun configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", runtimeDefinition.getSpecification(), neoFormDirectory); }); } + + public record CustomCompilerArgsProvider(Provider> args) implements CommandLineArgumentProvider { + @Override + public Iterable asArguments() { + return args.get(); + } + } } From be3f1bfe4331b4c2a7215bb2cd4d6b3874029242 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 3 Nov 2024 10:16:44 +0100 Subject: [PATCH 11/11] Update README.md Merging since all tests pass. --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 5a5dfd2f5..e3eb5f34a 100644 --- a/README.md +++ b/README.md @@ -30,22 +30,28 @@ dependencies { #### Access Transformers The userdev plugin provides a way to configure access transformers for your mod. -There are two ways to configure access transformers, either by using the DSL or by using a file. - -##### DSL -By using the `entry` method on the `accessTransformers` object, you can add an access transformer entry to your mod. -The system will write and generate the relevant files for you, however you will still need to add them to the file +You need to create an access transformer configuration file in your resources directory, and then configure the userdev plugin to use it. ```groovy -minecraft { - accessTransformers { - entry 'public-f net.neoforged.example.ExampleClass' +userdev { + accessTransformer { + file 'src/main/resources/META-INF/accesstransformer.cfg' } } ``` +The path here is up to you, and does not need to be included in your final jar. - - - +#### Interface Injections +The userdev plugin provides a way to configure interface injections for your mod. +This allows you to have a decompiled minecraft artifact that contains the interfaces you want to inject via mixins already statically applied. +The advantage of this approach is that you can use the interfaces in your code, and the mixins will be applied to the interfaces, and not the classes that implement them. +```groovy +userdev { + interfaceInjection { + file 'src/main/resources/META-INF/interfaceinjection.json' + } +} +``` +You can find more information on the format of the file [here](https://github.com/neoforged/JavaSourceTransformer?tab=readme-ov-file#interface-injection). #### Dependency management by the userdev plugin When this plugin detects a dependency on NeoForge, it will spring into action and create the necessary NeoForm runtime tasks to build a usable Minecraft JAR-file that contains the requested NeoForge version.