From 173aeb9029ae5104b5b5358b9198fe2d5e5ad540 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Sun, 3 Nov 2024 10:17:03 +0100 Subject: [PATCH] [Feature/Fix]: Interface injections, and minor bugfixes (#249) --- README.md | 25 ++ .../gradle/common/CommonProjectPlugin.java | 18 +- .../conventions/ConventionConfigurator.java | 17 +- .../dependency/ExtraJarDependencyManager.java | 4 +- .../common/dependency/JarJarArtifacts.java | 6 +- .../AccessTransformersExtension.java | 15 +- .../common/extensions/IExtensionCreator.java | 13 - .../extensions/IdeManagementExtension.java | 33 ++- .../InterfaceInjectionsExtension.java | 56 +++++ .../common/extensions/MinecraftExtension.java | 14 ++ .../extensions/NeoGradleProblemReporter.java | 114 --------- .../base/BaseFilesWithEntriesExtension.java | 30 --- .../replacement/ReplacementLogic.java | 60 ++--- .../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 + .../InterfaceInjectionPublishing.java | 56 +++++ .../runs/ide/IdeRunIntegrationManager.java | 8 +- .../gradle/common/runs/run/RunImpl.java | 12 +- .../definition/CommonRuntimeDefinition.java | 45 ++++ .../tasks/AccessTransformerFileGenerator.java | 44 ---- .../tasks/SourceAccessTransformer.java | 2 +- .../tasks/SourceInterfaceInjection.java | 95 +++++++ .../neoforged/gradle/common/tasks/JarJar.java | 4 +- .../common/util/CommonRuntimeTaskUtils.java | 50 +--- .../common/util/ConfigurationUtils.java | 122 ++++----- .../gradle/common/util/NeoGradleUtils.java | 1 - .../common/util/TaskDependencyUtils.java | 2 +- .../gradle/common/util/ZipFileUpdater.java | 49 ++++ .../gradle/common/util/run/RunsUtil.java | 6 +- .../extensions/AccessTransformers.groovy | 12 +- .../extensions/InjectedInterfaceData.groovy | 37 +++ .../extensions/InterfaceInjections.groovy | 55 +++++ .../dsl/common/extensions/Minecraft.groovy | 9 + .../replacement/DependencyReplacement.groovy | 7 +- .../extensions/subsystems/Integration.groovy | 20 ++ .../extensions/subsystems/Subsystems.groovy | 5 + .../runtime/definition/Definition.groovy | 48 ++++ .../dsl/common/tasks/ArtifactProvider.groovy | 13 +- .../specifications/OutputSpecification.groovy | 16 +- .../gradle/dsl/common/util/Constants.groovy | 2 +- 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 +- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/neoform/FunctionalTests.groovy | 7 +- .../definition/NeoFormRuntimeDefinition.java | 2 +- .../extensions/NeoFormRuntimeExtension.java | 110 +++++---- .../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 +------ .../gradle/userdev/FunctionalTests.groovy | 74 ++++++ .../userdev/InterfaceInjectionTests.groovy | 231 ++++++++++++++++++ .../gradle/userdev/MultiProjectTests.groovy | 96 ++++++++ .../neoforged/gradle/userdev/RunTests.groovy | 3 +- .../dependency/UserDevDependencyManager.java | 4 +- .../definition/UserDevRuntimeDefinition.java | 13 +- .../extension/UserDevRuntimeExtension.java | 18 +- .../UserDevRuntimeSpecification.java | 9 +- .../vanilla/AccessTransformerTests.groovy | 85 ------- .../extensions/VanillaRuntimeExtension.java | 34 +-- .../steps/ApplyAccessTransformerStep.java | 13 +- 84 files changed, 1764 insertions(+), 737 deletions(-) delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/IExtensionCreator.java create mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/NeoGradleProblemReporter.java delete mode 100644 common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.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 common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.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 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 create mode 100644 dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy 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 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..e3eb5f34a 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,31 @@ dependencies { } ``` +#### Access Transformers +The userdev plugin provides a way to configure access transformers for your mod. +You need to create an access transformer configuration file in your resources directory, and then configure the userdev plugin to use it. +```groovy +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. 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 6fab4c9f1..4f21ff6c7 100644 --- a/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java +++ b/common/src/main/java/net/neoforged/gradle/common/CommonProjectPlugin.java @@ -5,10 +5,12 @@ 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; 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; @@ -21,6 +23,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.*; @@ -40,7 +43,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; @@ -49,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; @@ -82,8 +85,8 @@ 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); project.getExtensions().create(Minecraft.class, "minecraft", MinecraftExtension.class, project); project.getExtensions().create(Mappings.class,"mappings", MappingsExtension.class, project); @@ -91,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); @@ -131,6 +136,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); @@ -145,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/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/AccessTransformersExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/AccessTransformersExtension.java index 61dcf4637..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/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/extensions/InterfaceInjectionsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java new file mode 100644 index 000000000..72dfcc2d5 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/InterfaceInjectionsExtension.java @@ -0,0 +1,56 @@ +package net.neoforged.gradle.common.extensions; + +import net.neoforged.gradle.common.interfaceinjection.InterfaceInjectionPublishing; +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; + +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) { + this.project = 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 Project getProject() { + return project; + } + + @Override + public void expose(Object path, Action action) { + getFiles().from(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); + } +} 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/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/base/BaseFilesWithEntriesExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/BaseFilesWithEntriesExtension.java deleted file mode 100644 index 47ff86626..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> 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/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/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/interfaceinjection/InterfaceInjectionPublishing.java b/common/src/main/java/net/neoforged/gradle/common/interfaceinjection/InterfaceInjectionPublishing.java new file mode 100644 index 000000000..4f3f52973 --- /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/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/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..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 @@ -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()) @@ -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; } @@ -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/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/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/CommonRuntimeTaskUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/CommonRuntimeTaskUtils.java index 078d43639..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 @@ -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; @@ -12,58 +9,33 @@ import org.gradle.api.file.FileTree; import org.gradle.api.tasks.TaskProvider; -import java.io.File; -import java.util.Collection; -import java.util.function.Consumer; - public final class CommonRuntimeTaskUtils { 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, 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/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/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/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/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/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/AccessTransformers.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/AccessTransformers.groovy index 33bfe32bd..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 new file mode 100644 index 000000000..4f6772502 --- /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. + */ + @Input + @DSLProperty + abstract ListProperty getInterfaces(); +} diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy new file mode 100644 index 000000000..52bcbbc1b --- /dev/null +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/InterfaceInjections.groovy @@ -0,0 +1,55 @@ +package net.neoforged.gradle.dsl.common.extensions + +import groovy.transform.CompileStatic +import net.minecraftforge.gdi.BaseDSLElementWithFilesAndEntries +import net.minecraftforge.gdi.annotations.DSLProperty +import org.gradle.api.Action +import org.gradle.api.artifacts.ConfigurablePublishArtifact +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.dsl.Dependencies +import org.gradle.api.artifacts.dsl.DependencyCollector +import org.gradle.api.file.ConfigurableFileCollection + +/** + * Defines a DSL extension which allows for the specification of interface injections. + */ +@CompileStatic +interface InterfaceInjections extends Dependencies { + + /** + * {@return interface injection files} + */ + @DSLProperty + ConfigurableFileCollection getFiles() + + /** + * {@return interface injections to add as dependencies} + */ + DependencyCollector getConsume() + + /** + * {@return interface injections to add as dependencies and also expose to consumers} + */ + DependencyCollector getConsumeApi() + + /** + * Publishes a transitive dependency on the given access transformer in the published interface injections of this component. + * + * @param dependency to expose to consumers + */ + void expose(Dependency dependency) + + /** + * Publishes the provided access transformer as an artifact. + * + * @param path access transformer file to publish + */ + void expose(Object path) + + /** + * Publishes the provided access transformer as an artifact and configures it with the provided action. + * @param path access transformer file to publish + * @param action configures the published artifact + */ + void expose(Object path, Action action) +} \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy index 7d69592e9..dfb7e0fd4 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/Minecraft.groovy @@ -47,4 +47,13 @@ interface Minecraft extends BaseDSLElement { @NotNull @DSLProperty AccessTransformers getAccessTransformers(); + + /** + * Gives access to the interface injections configuration extension. + * + * @return The interface injections configuration extension. + */ + @NotNull + @DSLProperty + InterfaceInjections getInterfaceInjections(); } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy index 97ed9e006..68f27e6ca 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/dependency/replacement/DependencyReplacement.groovy @@ -3,7 +3,6 @@ package net.neoforged.gradle.dsl.common.extensions.dependency.replacement import groovy.transform.CompileStatic import net.minecraftforge.gdi.BaseDSLElement import net.minecraftforge.gdi.annotations.DSLProperty -import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency @@ -34,12 +33,11 @@ interface DependencyReplacement extends BaseDSLElement { /** * Optionally converts the given dependency back to the original dependency it replaced. * - * @param dependency The dependency to optionally convert back. * @param configuration The configuration the given dependency can be found it resides in. * @return The original dependency if it can be converted back, otherwise the given dependency. */ @NotNull - Dependency optionallyConvertBackToOriginal(Dependency dependency, Configuration configuration) + Dependency optionallyConvertBackToOriginal(Dependency dependency) /** * Invoked when a dependency is replaced. @@ -57,9 +55,8 @@ interface DependencyReplacement extends BaseDSLElement { * Invoked when a dependency is replaced. * * @param virtualDependency The virtual dependency. - * @param targetConfiguration The target configuration in which the virtual dependency resides. * @param originalDependency The original dependency. */ - void apply(Dependency virtualDependency, Configuration targetConfiguration, Dependency originalDependency); + void apply(Dependency virtualDependency, Dependency originalDependency); } } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy new file mode 100644 index 000000000..193b210c4 --- /dev/null +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Integration.groovy @@ -0,0 +1,20 @@ +package net.neoforged.gradle.dsl.common.extensions.subsystems + +import net.minecraftforge.gdi.BaseDSLElement +import org.gradle.api.provider.Property + +/** + * Defines the integration settings for NeoGradle. + */ +interface Integration extends BaseDSLElement { + + /** + * @return whether the integration is enabled + */ + Property getIsEnabled(); + + /** + * @return whether to use Gradle problem reporting + */ + Property getUseGradleProblemReporting(); +} \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy index c1966251f..1005930f8 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Subsystems.groovy @@ -12,6 +12,11 @@ import org.gradle.api.tasks.Nested @CompileStatic interface Subsystems extends BaseDSLElement { + /** + * @return settings for the integration subsystem + */ + Integration getIntegration(); + /** * @return settings for the decompiler subsystem */ diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy index aba4c5f88..83d5682e2 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/definition/Definition.groovy @@ -10,6 +10,8 @@ import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.jetbrains.annotations.NotNull @@ -98,4 +100,50 @@ interface Definition { */ @NotNull ConfigurableFileCollection getAllDependencies() + + /** + * A collection of files which need to be added to the recompile classpath, + * for the recompile phase to succeed. + * + * @return The file collection with the additional jars which need to be added + */ + @NotNull + FileCollection getAdditionalRecompileDependencies(); + + /** + * Adds a dependency to the recompile classpath. + * + * @param dependency The dependency to add. + */ + void additionalRecompileDependency(Provider dependency); + + /** + * Adds dependencies to the recompile classpath. + * + * @param dependencies The dependencies to add. + */ + void additionalRecompileDependencies(FileCollection dependencies); + + /** + * A collection of source files which need to be added to the compile sources. + * These sources are removed again from the compiled jar. + * + * @return The file collection with the additional sources which need to be added + */ + @NotNull + FileCollection getAdditionalCompileSources(); + + /** + * Adds a source to the compile sources. + * + * @param source The source to add. + */ + void additionalCompileSource(Provider source); + + /** + * Adds sources to the compile sources. + * + * @param sources The sources to add. + */ + void additionalCompileSources(FileCollection sources); } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy index 3cd866e85..a4bd93213 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/ArtifactProvider.groovy @@ -2,12 +2,14 @@ package net.neoforged.gradle.dsl.common.tasks import groovy.transform.CompileStatic import net.minecraftforge.gdi.annotations.DSLProperty +import org.gradle.api.InvalidUserDataException import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.provider.Property import org.gradle.api.tasks.* import java.nio.file.Files import java.nio.file.Path +import java.util.stream.Collectors @CacheableTask @CompileStatic @@ -20,7 +22,16 @@ abstract class ArtifactProvider extends NeoGradleBase implements WithOutput { @TaskAction void doProvide() throws Exception { final Path output = ensureFileWorkspaceReady(getOutput()).toPath(); - final Path source = getInputFiles().getSingleFile().toPath(); + + final File inputFile; + try { + inputFile = getInputFiles().getSingleFile() + } catch (final IllegalStateException e) { + throw new InvalidUserDataException("There where either none or multiple input files provided. " + getInputFiles().files + .stream().map { file -> file.absolutePath }.collect(Collectors.joining()), e) + } + + final Path source = inputFile.toPath(); if (!Files.exists(source)) { throw new IllegalStateException("Source file does not exist: " + source); diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy index 4e518c877..0791fb963 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/OutputSpecification.groovy @@ -1,9 +1,11 @@ package net.neoforged.gradle.dsl.common.tasks.specifications import net.minecraftforge.gdi.annotations.DSLProperty +import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile @@ -12,7 +14,6 @@ import org.gradle.api.tasks.OutputFile */ trait OutputSpecification implements ProjectSpecification { - /** * The output file of this task as configured. * If not set, then it is derived from the output file name and the working directory of the task. @@ -25,7 +26,7 @@ trait OutputSpecification implements ProjectSpecification { abstract RegularFileProperty getOutput(); /** - * The name of the output file name for this step. + * The name of the output file name for this task. * Can be left out, if and only if the output is set directly. * * @return The name of the output file. @@ -34,4 +35,15 @@ trait OutputSpecification implements ProjectSpecification { @Optional @DSLProperty abstract Property getOutputFileName(); + + /** + * The output directory for this step, also doubles as working directory for this task. + * Can be left out, if and only if the output is set directly. + * + * @return The output and working directory for this task. + */ + @Internal + @Optional + @DSLProperty + abstract DirectoryProperty getOutputDirectory(); } \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy index 5a61a5c3d..a511811e2 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy @@ -27,7 +27,7 @@ class Constants { public static final String DEFAULT_PARCHMENT_GROUP = "org.parchmentmc.data" public static final String DEFAULT_PARCHMENT_ARTIFACT_PREFIX = "parchment-" public static final String DEFAULT_PARCHMENT_MAVEN_URL = "https://maven.parchmentmc.org/" - public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.55" + public static final String JST_TOOL_ARTIFACT = "net.neoforged.jst:jst-cli-bundle:1.0.67" public static final String DEVLOGIN_TOOL_ARTIFACT = "net.covers1624:DevLogin:0.1.0.4" public static final String RENDERNURSE_TOOL_ARTIFACT = "net.neoforged:render-nurse:0.0.12"; public static final String DEVLOGIN_MAIN_CLASS = "net.covers1624.devlogin.DevLogin" diff --git a/examples/multi_neoforge_root/api/build.gradle b/examples/multi_neoforge_root/api/build.gradle new file mode 100644 index 000000000..eaa539247 --- /dev/null +++ b/examples/multi_neoforge_root/api/build.gradle @@ -0,0 +1,16 @@ +plugins { + id 'net.neoforged.gradle.userdev' + id 'java-library' +} + + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + api(libs.neoforge) + } + \ No newline at end of file diff --git a/examples/multi_neoforge_root/api/gradle.properties b/examples/multi_neoforge_root/api/gradle.properties new file mode 100644 index 000000000..5adc99987 --- /dev/null +++ b/examples/multi_neoforge_root/api/gradle.properties @@ -0,0 +1,3 @@ +net.neoforged.gradle.caching.cacheDirectory=/tmp/spock_multiple_projects_w_0_tempDir7138874024286350570/.ng-cache +org.gradle.console=rich +org.gradle.jvmargs=-Xmx8000m \ No newline at end of file diff --git a/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt b/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt new file mode 100644 index 000000000..c688e6ee5 --- /dev/null +++ b/examples/multi_neoforge_root/api/runs/junit/junit_jvm_args.txt @@ -0,0 +1,24 @@ +-p +/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/bootstraplauncher/2.0.2/1a2d076cbc33b0520cbacd591224427b2a20047d/bootstraplauncher-2.0.2.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/securejarhandler/3.0.8/c0ef95cecd8699a0449053ac7d9c160748d902cd/securejarhandler-3.0.8.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.7/e86dda4696d3c185fcc95d8d311904e7ce38a53f/asm-commons-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.7/c0655519f24d92af2202cb681cd7c1569df6ead6/asm-util-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.7/e4a258b7eb96107106c0599f0061cfc1832fe07a/asm-analysis-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.7/e446a17b175bfb733b87c5c2560ccb4e57d69f1a/asm-tree-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.7/73d7b3086e14beb604ced229c302feff6449723/asm-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/net.neoforged/JarJarFileSystems/0.4.1/78f59f89defcd032ed788b151ca6a0d40ace796a/JarJarFileSystems-0.4.1.jar +--add-modules +ALL-MODULE-PATH +--add-opens +java.base/java.util.jar=cpw.mods.securejarhandler +--add-opens +java.base/java.lang.invoke=cpw.mods.securejarhandler +--add-exports +java.base/sun.security.util=cpw.mods.securejarhandler +--add-exports +jdk.naming.dns/com.sun.jndi.dns=java.naming +-p +/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/bootstraplauncher/2.0.2/1a2d076cbc33b0520cbacd591224427b2a20047d/bootstraplauncher-2.0.2.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/cpw.mods/securejarhandler/3.0.8/c0ef95cecd8699a0449053ac7d9c160748d902cd/securejarhandler-3.0.8.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-commons/9.7/e86dda4696d3c185fcc95d8d311904e7ce38a53f/asm-commons-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-util/9.7/c0655519f24d92af2202cb681cd7c1569df6ead6/asm-util-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-analysis/9.7/e4a258b7eb96107106c0599f0061cfc1832fe07a/asm-analysis-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm-tree/9.7/e446a17b175bfb733b87c5c2560ccb4e57d69f1a/asm-tree-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/org.ow2.asm/asm/9.7/73d7b3086e14beb604ced229c302feff6449723/asm-9.7.jar:/var/home/marchermans/.gradle/caches/modules-2/files-2.1/net.neoforged/JarJarFileSystems/0.4.1/78f59f89defcd032ed788b151ca6a0d40ace796a/JarJarFileSystems-0.4.1.jar +--add-modules +ALL-MODULE-PATH +--add-opens +java.base/java.util.jar=cpw.mods.securejarhandler +--add-opens +java.base/java.lang.invoke=cpw.mods.securejarhandler +--add-exports +java.base/sun.security.util=cpw.mods.securejarhandler +--add-exports +jdk.naming.dns/com.sun.jndi.dns=java.naming diff --git a/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt b/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt new file mode 100644 index 000000000..b38c35c7b --- /dev/null +++ b/examples/multi_neoforge_root/api/runs/junit/junit_test_args.txt @@ -0,0 +1,36 @@ +--launchTarget +forgejunituserdev +--version +21.3.6-beta +--assetIndex +asset-index +--assetsDir +/var/home/marchermans/.gradle/caches/minecraft/assets +--gameDir +. +--fml.neoForgeVersion +21.3.6-beta +--fml.fmlVersion +4.0.31 +--fml.mcVersion +1.21.3 +--fml.neoFormVersion +20241023.131943 +--launchTarget +forgejunituserdev +--version +21.3.6-beta +--assetIndex +asset-index +--assetsDir +/var/home/marchermans/.gradle/caches/minecraft/assets +--gameDir +. +--fml.neoForgeVersion +21.3.6-beta +--fml.fmlVersion +4.0.31 +--fml.mcVersion +1.21.3 +--fml.neoFormVersion +20241023.131943 diff --git a/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java b/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java new file mode 100644 index 000000000..8a21ee205 --- /dev/null +++ b/examples/multi_neoforge_root/api/src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java @@ -0,0 +1,11 @@ + + package net.neoforged.gradle.apitest; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + } + } + \ No newline at end of file diff --git a/examples/multi_neoforge_root/build.gradle b/examples/multi_neoforge_root/build.gradle new file mode 100644 index 000000000..939da3986 --- /dev/null +++ b/examples/multi_neoforge_root/build.gradle @@ -0,0 +1,11 @@ +plugins { + id 'net.neoforged.gradle.userdev' +} + + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + \ No newline at end of file diff --git a/examples/multi_neoforge_root/gradle.properties b/examples/multi_neoforge_root/gradle.properties new file mode 100644 index 000000000..5adc99987 --- /dev/null +++ b/examples/multi_neoforge_root/gradle.properties @@ -0,0 +1,3 @@ +net.neoforged.gradle.caching.cacheDirectory=/tmp/spock_multiple_projects_w_0_tempDir7138874024286350570/.ng-cache +org.gradle.console=rich +org.gradle.jvmargs=-Xmx8000m \ No newline at end of file diff --git a/examples/multi_neoforge_root/gradle/libs.versions.toml b/examples/multi_neoforge_root/gradle/libs.versions.toml new file mode 100644 index 000000000..336928333 --- /dev/null +++ b/examples/multi_neoforge_root/gradle/libs.versions.toml @@ -0,0 +1,6 @@ +[versions] + # Neoforge Settings + neoforge = "+" + + [libraries] + neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" } \ No newline at end of file diff --git a/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar b/examples/multi_neoforge_root/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c 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 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 +216,7 @@ private static void buildArguments(final RuntimeArguments arguments, final NeoFo step.getValues().forEach((key, value) -> { if (value.startsWith("{") && value.endsWith("}")) { Optional> dependentTask; - if (!Objects.equals(key, "input") || !alternativeInputProvider.isPresent()) { + if (!Objects.equals(key, "input") || alternativeInputProvider.isEmpty()) { dependentTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, value, tasks); } else { dependentTask = alternativeInputProvider; @@ -354,24 +353,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { Optional> adaptedInput = Optional.empty(); if (spec.getPreTaskTypeAdapters().containsKey(step.getName())) { - final String inputArgumentMarker = step.getValue("input"); - if (inputArgumentMarker == null) { - throw new IllegalStateException("Can not change input chain on: " + step.getName() + " it has no input to transform!"); - } - - Optional> inputTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, inputArgumentMarker, taskOutputs); - - if (!spec.getPreTaskTypeAdapters().get(step.getName()).isEmpty() && inputTask.isPresent()) { - for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(step.getName())) { - final TaskProvider 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 +381,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { Optional> finalAdaptedInput = adaptedInput; neoFormRuntimeTaskProvider.configure((WithOutput neoFormRuntimeTask) -> { - if (neoFormRuntimeTask instanceof Runtime) { - final Runtime runtimeTask = (Runtime) neoFormRuntimeTask; + if (neoFormRuntimeTask instanceof Runtime runtimeTask) { configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, taskOutputs, step, runtimeTask, finalAdaptedInput); } }); @@ -438,22 +419,36 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { remapTask, symbolicDataSources, neoFormDirectory, - context.getLibrariesTask().flatMap(WithOutput::getOutput) + Objects.requireNonNull(context.getLibrariesTask()).flatMap(WithOutput::getOutput) ); - final FileCollection recompileDependencies = spec.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration())); + recompileInput = adaptPreTaskInput( + definition, + "recompile", + spec, + neoFormDirectory, + symbolicDataSources, + Optional.of(recompileInput), + Optional.of(recompileInput) + ).orElseThrow(() -> new IllegalStateException("No input for recompile task due to pre-task adapters")); + final FileCollection recompileDependencies = definition.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration())); + final TaskProvider extractionSource = recompileInput; final TaskProvider unpackSources = spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "unzipSources"), UnpackZip.class, task -> { task.getInput().from( - recompileInput.flatMap(WithOutput::getOutput) + extractionSource.flatMap(WithOutput::getOutput) .map(sourceJar -> task.getArchiveOperations().zipTree(sourceJar).matching(sp -> sp.include("**/*.java")).getAsFileTree()) ); + task.getInput().from( + definition.getAdditionalCompileSources() + ); }); unpackSources.configure(neoFormRuntimeTask -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, neoFormRuntimeTask)); final TaskProvider recompileTask = spec.getProject() .getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "recompile"), RecompileSourceJar.class, task -> { task.setSource(unpackSources.flatMap(UnpackZip::getUnpackingTarget)); + task.getAdditionalInputFiles().from(definition.getAdditionalCompileSources()); task.getCompileClasspath().setFrom(recompileDependencies); task.getStepName().set("recompile"); @@ -464,7 +459,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { ForkOptions forkOptions = task.getOptions().getForkOptions(); forkOptions.setMemoryMaximumSize(maxMemory); forkOptions.setJvmArgs(settings.getJvmArgs().get()); - task.getOptions().getCompilerArgumentProviders().add(settings.getArgs()::get); + task.getOptions().getCompilerArgumentProviders().add(new CustomCompilerArgsProvider(settings.getArgs())); task.getJavaVersion().set( definition.getVersionJson() @@ -478,7 +473,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { final TaskProvider packTask = spec.getProject() .getTasks().register(CommonRuntimeUtils.buildTaskName(spec, "packRecomp"), PackJar.class, task -> { - task.getInputFiles().from(recompileInput.flatMap(WithOutput::getOutput).map(task.getArchiveOperations()::zipTree).map(zipTree -> zipTree.matching(sp -> sp.exclude("**/*.java")))); + task.getInputFiles().from(extractionSource.flatMap(WithOutput::getOutput).map(task.getArchiveOperations()::zipTree).map(zipTree -> zipTree.matching(sp -> sp.exclude("**/*.java")))); task.getInputFiles().from(recompileTask.flatMap(AbstractCompile::getDestinationDirectory)); }); packTask.configure(neoFormRuntimeTask -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, neoFormRuntimeTask)); @@ -486,7 +481,7 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { taskOutputs.put(recompileTask.getName(), packTask); definition.getSourceJarTask().configure(task -> { - task.getInputFiles().from(recompileInput); + task.getInputFiles().from(extractionSource.flatMap(WithOutput::getOutput)); task.dependsOn(remapTask); }); definition.getRawJarTask().configure(task -> { @@ -495,6 +490,32 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { }); } + private static Optional> adaptPreTaskInput(NeoFormRuntimeDefinition definition, NeoFormConfigConfigurationSpecV1.Step step, NeoFormRuntimeSpecification spec, LinkedHashMap> taskOutputs, File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput) { + final String inputArgumentMarker = step.getValue("input"); + if (inputArgumentMarker == null) { + throw new IllegalStateException("Can not change input chain on: " + step.getName() + " it has no input to transform!"); + } + + Optional> inputTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, inputArgumentMarker, taskOutputs); + + return adaptPreTaskInput(definition, step.getName(), spec, neoFormDirectory, symbolicDataSources, adaptedInput, inputTask); + } + + private static Optional> adaptPreTaskInput(NeoFormRuntimeDefinition definition, String stepName, NeoFormRuntimeSpecification spec, File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput, Optional> inputTask) { + if (!spec.getPreTaskTypeAdapters().get(stepName).isEmpty() && inputTask.isPresent()) { + for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(stepName)) { + final TaskProvider 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 +528,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 +564,12 @@ private static TaskProvider maybeApplyParchment(NeoFormRun configureCommonRuntimeTaskParameters(task, symbolicDataSources, "applyParchment", runtimeDefinition.getSpecification(), neoFormDirectory); }); + } - return applyParchmentTask; + public record CustomCompilerArgsProvider(Provider> args) implements CommandLineArgumentProvider { + @Override + public Iterable asArguments() { + return args.get(); + } } } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java index 9b2a5a2df..c7b760ad7 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/RecompileSourceJar.java @@ -9,16 +9,14 @@ import net.neoforged.gradle.dsl.common.runtime.tasks.RuntimeMultiArguments; import org.gradle.api.GradleException; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileTree; import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.services.ServiceReference; -import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Nested; -import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.*; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.internal.jvm.Jvm; import org.gradle.jvm.toolchain.JavaLanguageVersion; @@ -133,6 +131,10 @@ public Property getJavaVersion() { @ServiceReference(CachedExecutionService.NAME) public abstract Property getCacheService(); + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getAdditionalInputFiles(); + @Override protected void compile(InputChanges inputs) { try { @@ -142,7 +144,7 @@ protected void compile(InputChanges inputs) { ICacheableJob.Default.directory( getDestinationDirectory(), () -> { - super.compile(inputs); + doCachedCompile(inputs); } ) ).execute(); @@ -150,4 +152,31 @@ protected void compile(InputChanges inputs) { throw new GradleException("Failed to recompile!", e); } } + + private void doCachedCompile(InputChanges inputs) { + super.compile(inputs); + + final FileTree outputTree = getDestinationDirectory().get().getAsFileTree(); + outputTree.matching(pattern -> pattern.include(fileTreeElement -> { + final String relativePath = fileTreeElement.getRelativePath().getPathString(); + if (!relativePath.endsWith(".class")) { + return false; + } + + final String sourceFilePath; + if (!relativePath.contains("$")) { + sourceFilePath = relativePath.substring(0, relativePath.length() - ".class".length()) + ".java"; + } else { + sourceFilePath = relativePath.substring(0, relativePath.indexOf('$')) + ".java"; + } + + return !getAdditionalInputFiles() + .getAsFileTree() + .matching(sp1 -> sp1.include(sourceFilePath)) + .getFiles().isEmpty(); + })).forEach(file -> { + getLogger().debug("Removing additional source file: {}", file); + file.delete(); + }); + } } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java new file mode 100644 index 000000000..bec0ae202 --- /dev/null +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormAccessTaskAdapterUtils.java @@ -0,0 +1,59 @@ +package net.neoforged.gradle.neoform.util; + +import net.neoforged.gradle.common.runtime.tasks.SourceAccessTransformer; +import net.neoforged.gradle.common.runtime.tasks.SourceInterfaceInjection; +import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils; +import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; +import net.neoforged.gradle.dsl.common.extensions.InterfaceInjections; +import net.neoforged.gradle.dsl.common.extensions.Minecraft; +import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter; +import net.neoforged.gradle.dsl.common.tasks.WithOutput; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskProvider; + +public class NeoFormAccessTaskAdapterUtils { + + private NeoFormAccessTaskAdapterUtils() { + throw new IllegalStateException("Can not instantiate an instance of: McpAccessTransformerUtils. This is a utility class"); + } + + public static TaskTreeAdapter createAccessTransformerAdapter(final Project project) { + final Minecraft minecraftExtension = project.getExtensions().getByType(Minecraft.class); + final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers(); + + return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> { + if (accessTransformerFiles.getFiles().isEmpty()) { + return null; + } + + final TaskProvider 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/FunctionalTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy index 8f33dc9cd..99342f1ef 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy @@ -407,4 +407,78 @@ class FunctionalTests extends BuilderBasedTestSpecification { run.task(':clean').outcome == TaskOutcome.SUCCESS run.task(':build').outcome == TaskOutcome.SUCCESS } + + def "a mod with userdev does not expose the userdev artifact to consumers"() { + given: + def project = create("gradle_multi_sourceset", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + api 'net.neoforged:neoforge:+' + } + + publishing { + repositories { + maven { + // change to point to your repo, e.g. http://my.org/repo + url = layout.buildDirectory.dir('repo') + } + } + + publications { + maven(MavenPublication) { + groupId = 'org.gradle.sample' + artifactId = 'library' + version = '1.1' + + from components.java + } + } + } + + """) + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.plugin("java-library") + it.plugin("maven-publish") + }) + + when: + def run = project.run { + it.tasks('clean', 'build', 'publish') + it.stacktrace() + } + + then: + run.task(':clean').outcome == TaskOutcome.SUCCESS + run.task(':build').outcome == TaskOutcome.SUCCESS + run.task(':publish').outcome == TaskOutcome.SUCCESS + + and: + def repo = run.file("build/repo/org/gradle/sample/library/1.1") + def moduleFile = new File(repo, "library-1.1.module") + def pomFile = new File(repo, "library-1.1.pom") + + then: + moduleFile.exists() + pomFile.exists() + !moduleFile.text.contains("neoforged") + !pomFile.text.contains("neoforged") + } } diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy new file mode 100644 index 000000000..4916ab65b --- /dev/null +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/InterfaceInjectionTests.groovy @@ -0,0 +1,231 @@ +package net.neoforged.gradle.userdev + + +import net.neoforged.trainingwheels.gradle.functional.BuilderBasedTestSpecification +import org.gradle.testkit.runner.TaskOutcome + +class InterfaceInjectionTests extends BuilderBasedTestSpecification { + + @Override + protected void configurePluginUnderTest() { + pluginUnderTest = "net.neoforged.gradle.userdev"; + injectIntoAllProject = true; + } + + def "the userdev runtime supports loading iis from a file"() { + given: + def project = create("userdev_supports_iis_from_file", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json') + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""") + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + Minecraft.getInstance().doSomething(); + } + } + """) + it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """ + package com.example.examplemod; + + public interface MyInjectedInterface { + default void doSomething() { }; + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def initialRun = project.run { + it.tasks('build') + it.debug() + } + + then: + initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS + initialRun.task(":build").outcome == TaskOutcome.SUCCESS + } + + def "the userdev runtime supports loading iis from a file after the dependencies block"() { + given: + def project = create("userdev_supports_iis_from_file", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + + minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json') + """) + it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""") + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + Minecraft.getInstance().doSomething(); + } + } + """) + it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """ + package com.example.examplemod; + + public interface MyInjectedInterface { + default void doSomething() { }; + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def initialRun = project.run { + it.tasks('build') + } + + then: + initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS + initialRun.task(":build").outcome == TaskOutcome.SUCCESS + } + + def "the userdev runtime supports loading iis from multiple files"() { + given: + def project = create("userdev_supports_iis_in_multiple_distinctly_named_files", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json') + minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis2.json') + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""") + it.file("src/main/resources/META-INF/iis2.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MySecondaryInterface"]}""") + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + Minecraft.getInstance().doSomething(); + Minecraft.getInstance().doSecondSomething(); + } + } + """) + it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """ + package com.example.examplemod; + + public interface MyInjectedInterface { + default void doSomething() { }; + } + """) + it.file("src/main/java/com/example/examplemod/MySecondaryInterface.java", """ + package com.example.examplemod; + + public interface MySecondaryInterface { + default void doSecondSomething() { }; + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def initialRun = project.run { + it.tasks('build') + } + + then: + initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS + initialRun.task(":build").outcome == TaskOutcome.SUCCESS + } + + def "the userdev runtime supports loading iis from multiple files named the same in different directories"() { + given: + def project = create("userdev_supports_iis_in_files_named_the_same", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + minecraft.interfaceInjections.file rootProject.file('src/main/resources/META-INF/iis.json') + minecraft.interfaceInjections.file rootProject.file('src/main/resources/iis.json') + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/resources/META-INF/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MyInjectedInterface"]}""") + it.file("src/main/resources/iis.json", """{"net/minecraft/client/Minecraft": ["com/example/examplemod/MySecondaryInterface"]}""") + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + Minecraft.getInstance().doSomething(); + Minecraft.getInstance().doSecondSomething(); + } + } + """) + it.file("src/main/java/com/example/examplemod/MyInjectedInterface.java", """ + package com.example.examplemod; + + public interface MyInjectedInterface { + default void doSomething() { }; + } + """) + it.file("src/main/java/com/example/examplemod/MySecondaryInterface.java", """ + package com.example.examplemod; + + public interface MySecondaryInterface { + default void doSecondSomething() { }; + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def initialRun = project.run { + it.tasks('build') + } + + then: + initialRun.task(":neoFormRecompile").outcome == TaskOutcome.SUCCESS + initialRun.task(":build").outcome == TaskOutcome.SUCCESS + } +} diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy index bcf06bc12..9f42ddb41 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy @@ -105,6 +105,102 @@ class MultiProjectTests extends BuilderBasedTestSpecification { run.output.contains("Caused by: net.neoforged.fml.ModLoadingException: Loading errors encountered:") } + def "multiple projects with neoforge dependencies from version catalogs should be able to build"() { + given: + def rootProject = create("multi_neoforge_root", { + it.file("gradle/libs.versions.toml", + """ + [versions] + # Neoforge Settings + neoforge = "+" + + [libraries] + neoforge = { group = "net.neoforged", name = "neoforge", version.ref = "neoforge" } + """.trim()) + + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + def apiProject = create(rootProject, "api", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + api(libs.neoforge) + } + """) + it.file("src/main/java/net/neoforged/gradle/apitest/FunctionalTests.java", """ + package net.neoforged.gradle.apitest; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.plugin(this.pluginUnderTest) + it.plugin("java-library") + }) + + def mainProject = create(rootProject,"main", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + implementation project(':api') + //We still need a neoforge dependency here, because neoforge is not exposed from the api project. Compile would faile. + implementation libs.neoforge + } + """) + it.file("src/main/java/net/neoforged/gradle/main/ApiTests.java", """ + package net.neoforged.gradle.main; + + import net.minecraft.client.Minecraft; + import net.neoforged.gradle.apitest.FunctionalTests; + + public class ApiTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + FunctionalTests.main(args); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.plugin(this.pluginUnderTest) + }) + + when: + def run = rootProject.run { + it.tasks(':main:build') + it.stacktrace() + it.debug() + } + + then: + run.task(':main:build').outcome == TaskOutcome.SUCCESS + } + def "multiple projects with neoforge dependencies should be able to run the game when renderDoc is enabled"() { given: def rootProject = create("multi_neoforge_root_renderdoc", { diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy index a774678c3..ab8c0709d 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy @@ -425,13 +425,14 @@ class RunTests extends BuilderBasedTestSpecification { import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; + import net.minecraft.core.registries.Registries; @ExtendWith(EphemeralTestServerProvider.class) public class TestTest { @Test public void testIngredient(MinecraftServer server) { // required to load tags Assertions.assertTrue( - Ingredient.of(ItemTags.AXES).test(Items.DIAMOND_AXE.getDefaultInstance()) + Ingredient.of(server.registryAccess().lookupOrThrow(Registries.ITEM).getOrThrow(ItemTags.AXES)).test(Items.DIAMOND_AXE.getDefaultInstance()) ); } } diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java b/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java index 2acf60c3c..aa3fe835f 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/dependency/UserDevDependencyManager.java @@ -85,8 +85,8 @@ private void registerReplacementHandler(Project project) { "NeoForgeUserDevAdditionalReplacementDependenciesFor" + runtimeDefinition.getSpecification().getIdentifier(), configuration -> { configuration.setDescription("Additional dependencies for the NeoForge UserDev replacement for " + runtimeDefinition.getSpecification().getIdentifier()); - configuration.extendsFrom(runtimeDefinition.getNeoFormRuntimeDefinition().getMinecraftDependenciesConfiguration()); - configuration.extendsFrom(runtimeDefinition.getAdditionalUserDevDependencies()); + ConfigurationUtils.extendsFrom(project, configuration, runtimeDefinition.getNeoFormRuntimeDefinition().getMinecraftDependenciesConfiguration()); + ConfigurationUtils.extendsFrom(project, configuration, runtimeDefinition.getAdditionalUserDevDependencies()); } ); diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java index d942398d7..629fa672e 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java @@ -18,6 +18,7 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; import org.gradle.api.file.RegularFile; import org.gradle.api.provider.MapProperty; @@ -102,12 +103,16 @@ public TaskProvider getListLibrariesTaskProvider() { return neoformRuntimeDefinition.getListLibrariesTaskProvider(); } + @Override + public @NotNull FileCollection getAdditionalRecompileDependencies() { + return neoformRuntimeDefinition.getAdditionalRecompileDependencies(); + } + @Override protected void buildRunInterpolationData(RunImpl run, @NotNull MapProperty interpolationData) { neoformRuntimeDefinition.buildRunInterpolationData(run, interpolationData); if (userdevConfiguration.getModules() != null && !userdevConfiguration.getModules().get().isEmpty()) { - final String name = String.format("moduleResolverForgeUserDev%s", getSpecification().getVersionedName()); final Configuration modulesCfg = ConfigurationUtils .temporaryUnhandledConfiguration( getSpecification().getProject().getConfigurations(), @@ -132,9 +137,9 @@ protected void buildRunInterpolationData(RunImpl run, @NotNull MapProperty { @@ -65,10 +64,12 @@ public UserDevRuntimeExtension(Project project) { .withDistributionType(DistributionType.JOINED) .withAdditionalDependencies(getProject().files(userDevAdditionalDependenciesConfiguration)); - final TaskTreeAdapter atAdapter = createAccessTransformerAdapter(userDevProfile.getAccessTransformerDirectory().get(), userDevJar) - .andThen(NeoFormAccessTransformerUtils.createAccessTransformerAdapter(getProject())); + final TaskTreeAdapter atAndIISAdapter = createAccessTransformerAdapter(userDevProfile.getAccessTransformerDirectory().get(), userDevJar) + .andThen(NeoFormAccessTaskAdapterUtils.createAccessTransformerAdapter(getProject())); - builder.withPostTaskAdapter("decompile", atAdapter); + builder.withPostTaskAdapter("decompile", atAndIISAdapter); + + builder.withPreTaskAdapter("recompile", NeoFormAccessTaskAdapterUtils.createInterfaceInjectionAdapter(getProject())); builder.withPostTaskAdapter("patch", createPatchAdapter(userDevJar, userDevProfile.getSourcePatchesDirectory().get())); @@ -137,7 +138,12 @@ private TaskTreeAdapter createAccessTransformerAdapter(final String accessTransf userDev.matching(filter -> filter.include(accessTransformerDirectory + "/**")); return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> { - final TaskProvider accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "Forges", runtimeWorkspace, dependentTaskConfigurationHandler, accessTransformerFiles, Collections.emptyList(), definition.getListLibrariesTaskProvider(), definition.getAllDependencies()); + if (accessTransformerFiles.isEmpty()) { + // No access transformers found, so we don't need to do anything + return null; + } + + final TaskProvider accessTransformerTask = CommonRuntimeTaskUtils.createSourceAccessTransformer(definition, "Forges", accessTransformerFiles, definition.getListLibrariesTaskProvider(), definition.getAllDependencies()); accessTransformerTask.configure(task -> task.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput))); accessTransformerTask.configure(task -> task.dependsOn(previousTasksOutput)); return accessTransformerTask; diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java index 2b962ad4e..ca760eced 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/specification/UserDevRuntimeSpecification.java @@ -51,7 +51,14 @@ public UserDevRuntimeSpecification(Project project, String forgeName, String forgeVersion, Artifact artifact) { - super(project, "neoForge", version, distribution, preTaskTypeAdapters, postTypeAdapters, taskCustomizers, UserDevRuntimeExtension.class); + super(project, + "neoForge", + version, + distribution, + preTaskTypeAdapters, + postTypeAdapters, + taskCustomizers, + UserDevRuntimeExtension.class); this.userDevArchive = userDevArchive; this.profile = profile; this.forgeGroup = forgeGroup; diff --git a/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy b/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy index df0c1106c..da7a72370 100644 --- a/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy +++ b/vanilla/src/functionalTest/groovy/net/neoforged/gradle/vanilla/AccessTransformerTests.groovy @@ -54,91 +54,6 @@ class AccessTransformerTests extends BuilderBasedTestSpecification { initialRun.task(":build").outcome == TaskOutcome.SUCCESS } - def "the vanilla runtime supports loading ats from the script"() { - given: - def project = create("vanilla_supports_ats_in_scripts", { - it.build(""" - java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } - } - - minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper' - - dependencies { - implementation 'net.minecraft:client:+' - } - """) - it.file("src/main/java/net/neoforged/gradle/vanilla/FunctionalTests.java", """ - package net.neoforged.gradle.vanilla; - - import net.minecraft.client.Minecraft; - - public class FunctionalTests { - public static void main(String[] args) { - System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString()); - } - } - """) - it.withToolchains() - it.withGlobalCacheDirectory(tempDir) - }) - - when: - def initialRun = project.run { - it.tasks('build') - } - - then: - initialRun.task(":clientApplyUserAccessTransformer").outcome == TaskOutcome.SUCCESS - initialRun.task(":build").outcome == TaskOutcome.SUCCESS - } - - def "the vanilla runtime supports loading ats from the script and the file"() { - given: - def project = create("vanilla_supports_ats_in_script_and_file", { - it.build(""" - java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } - } - minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') - minecraft.accessTransformers.entry 'public-f net.minecraft.client.Minecraft LOGGER # LOGGER' - - dependencies { - implementation 'net.minecraft:client:+' - } - """) - it.file("src/main/resources/META-INF/accesstransformer.cfg", """public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper""") - it.file("src/main/java/net/neoforged/gradle/vanilla/FunctionalTests.java", """ - package net.neoforged.gradle.vanilla; - - import net.minecraft.client.Minecraft; - - public class FunctionalTests { - public static void main(String[] args) { - System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString()); - System.out.println(Minecraft.LOGGER.getClass().toString()); - } - } - """) - it.withToolchains() - it.withGlobalCacheDirectory(tempDir) - }) - - when: - def initialRun = project.run { - it.tasks('build') - - } - - then: - initialRun.task(":clientApplyUserAccessTransformer").outcome == TaskOutcome.SUCCESS - initialRun.task(":build").outcome == TaskOutcome.SUCCESS - } - def "the vanilla runtime supports loading ats from multiple files"() { given: def project = create("vanilla_supports_ats_in_multiple_distinctly_named_files", { diff --git a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java index 2658acd6b..2f643a8b9 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/extensions/VanillaRuntimeExtension.java @@ -165,26 +165,28 @@ protected void bakeDefinition(VanillaRuntimeDefinition definition) { ) ); - task.configure((Runtime mcpRuntimeTask) -> configureCommonRuntimeTaskParameters(mcpRuntimeTask, step.getName(), spec, runtimeWorkingDirectory)); - - if (!spec.getPostTypeAdapters().containsKey(step.getName())) { - definition.getTasks().put(task.getName(), task); - } else { - int taskPostAdapterIndex = 0; - for (TaskTreeAdapter taskTreeAdapter : spec.getPostTypeAdapters().get(step.getName())) { - final AtomicInteger additionalPostAdapterTasks = new AtomicInteger(0); - final int currentPostAdapterIndex = taskPostAdapterIndex++; - final TaskProvider 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 1e5909933..32771d3eb 100644 --- a/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java +++ b/vanilla/src/main/java/net/neoforged/gradle/vanilla/runtime/steps/ApplyAccessTransformerStep.java @@ -3,7 +3,6 @@ import net.neoforged.gradle.common.runtime.tasks.BinaryAccessTransformer; import net.neoforged.gradle.dsl.common.util.GameArtifact; import net.neoforged.gradle.util.StringCapitalizationUtils; -import net.neoforged.gradle.common.runtime.tasks.NoopRuntime; import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils; import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; import net.neoforged.gradle.dsl.common.extensions.Minecraft; @@ -25,20 +24,14 @@ public TaskProvider buildTask(VanillaRuntimeDefinition defini final Minecraft minecraftExtension = definition.getSpecification().getProject().getExtensions().getByType(Minecraft.class); final AccessTransformers accessTransformerFiles = minecraftExtension.getAccessTransformers(); - if (accessTransformerFiles.getFiles().isEmpty() && (!accessTransformerFiles.getEntries().isPresent() || accessTransformerFiles.getEntries().get().isEmpty())) { - return definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), String.format("apply%sAccessTransformer", StringCapitalizationUtils.capitalize("user"))), NoopRuntime.class, task -> { - task.getInput().set(inputProvidingTask.flatMap(WithOutput::getOutput)); - task.dependsOn(inputProvidingTask); - }); + if (accessTransformerFiles.getFiles().isEmpty()) { + return null; } final TaskProvider task = CommonRuntimeTaskUtils.createBinaryAccessTransformer( definition, "user", - workingDirectory, - additionalTaskConfigurator, - accessTransformerFiles.getFiles().getAsFileTree(), - accessTransformerFiles.getEntries().get() + accessTransformerFiles.getFiles().getAsFileTree() ); task.configure(t -> {