From b9894f2039299a78e043b2cff6a012a1be99f2ce Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:34:19 +0200 Subject: [PATCH 01/39] Switch to custom neodev plugin based on moddev --- build.gradle | 19 +- buildSrc/build.gradle | 23 + buildSrc/gradle.properties | 3 + buildSrc/settings.gradle | 0 .../neodev/ApplyAccessTransformer.java | 60 ++ .../net/neoforged/neodev/ApplyPatches.java | 74 +++ .../neodev/CreateCleanArtifacts.java | 36 ++ .../neodev/GenerateBinaryPatches.java | 70 +++ .../neodev/GenerateSourcePatches.java | 55 ++ .../neoforged/neodev/NeoDevBasePlugin.java | 23 + .../net/neoforged/neodev/NeoDevExtension.java | 35 ++ .../neoforged/neodev/NeoDevExtraPlugin.java | 95 +++ .../net/neoforged/neodev/NeoDevPlugin.java | 550 ++++++++++++++++++ .../neoforged/neodev/NeoDevTestExtension.java | 30 + .../java/net/neoforged/neodev/RemapJar.java | 44 ++ .../main/java/net/neoforged/neodev/Tools.java | 33 ++ .../neoforged/neodev/WriteUserDevConfig.java | 194 ++++++ .../installer/ArtifactPathsCollector.java | 47 ++ .../neodev/installer/CreateArgsFile.java | 128 ++++ .../installer/CreateInstallerProfile.java | 204 +++++++ .../installer/CreateLauncherProfile.java | 128 ++++ .../neodev/installer/InstallerProcessor.java | 16 + .../neoforged/neodev/installer/Library.java | 5 + .../neodev/installer/LibraryArtifact.java | 7 + .../neodev/installer/LibraryCollector.java | 220 +++++++ .../neodev/installer/LibraryDownload.java | 4 + .../ModuleIdentificationVisitor.java | 51 ++ gradle.properties | 16 +- gradle/wrapper/gradle-wrapper.properties | 2 +- projects/base/build.gradle | 16 +- projects/neoforge/build.gradle | 287 ++++----- settings.gradle | 28 +- testframework/build.gradle | 27 +- tests/build.gradle | 150 +++-- 34 files changed, 2386 insertions(+), 294 deletions(-) create mode 100644 buildSrc/gradle.properties create mode 100644 buildSrc/settings.gradle create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/Tools.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java diff --git a/build.gradle b/build.gradle index 6c46fb5ba6..fbc90b7d61 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ import java.util.regex.Pattern plugins { id 'net.neoforged.gradleutils' version '3.0.0' id 'com.diffplug.spotless' version '6.22.0' apply false - id 'net.neoforged.licenser' version '0.7.2' + id 'net.neoforged.licenser' version '0.7.5' id 'neoforge.formatting-conventions' id 'neoforge.versioning' } @@ -23,19 +23,22 @@ System.out.println("NeoForge version ${project.version}") allprojects { version rootProject.version group 'net.neoforged' - repositories { - mavenLocal() - } -} -subprojects { apply plugin: 'java' java.toolchain.languageVersion.set(JavaLanguageVersion.of(project.java_version)) } -repositories { - mavenCentral() +// Remove src/ sources from the root project. They are used in the neoforge subproject. +sourceSets { + main { + java { + srcDirs = [] + } + resources { + srcDirs = [] + } + } } // Put licenser here otherwise it tries to license all source sets including decompiled MC sources diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 6784052459..e89269c0ed 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,3 +1,26 @@ plugins { + id 'java-gradle-plugin' id 'groovy-gradle-plugin' } + +repositories { + gradlePluginPortal() + mavenCentral() + maven { + name = "NeoForged" + url = "https://maven.neoforged.net/releases" + content { + includeGroup "codechicken" + includeGroup "net.neoforged" + } + } +} + +dependencies { + // buildSrc is an includedbuild of the parent directory (gradle.parent) + // ../settings.gradle sets the moddevgradle_plugin_version accordingly + implementation "net.neoforged:moddev-gradle:${gradle.parent.ext.moddevgradle_plugin_version}" + + implementation "com.google.code.gson:gson:${project.gson_version}" + implementation "io.codechicken:DiffPatch:${project.diffpatch_version}" +} diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties new file mode 100644 index 0000000000..6665e0d877 --- /dev/null +++ b/buildSrc/gradle.properties @@ -0,0 +1,3 @@ + +gson_version=2.11.0 +diffpatch_version=2.0.0.35 diff --git a/buildSrc/settings.gradle b/buildSrc/settings.gradle new file mode 100644 index 0000000000..e69de29bb2 diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java new file mode 100644 index 0000000000..b208523caf --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java @@ -0,0 +1,60 @@ +package net.neoforged.neodev; + +import net.neoforged.moddevgradle.internal.utils.FileUtils; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; + +abstract class ApplyAccessTransformer extends JavaExec { + @InputFile + public abstract RegularFileProperty getInputJar(); + + @InputFile + public abstract RegularFileProperty getAccessTransformer(); + + @OutputFile + public abstract RegularFileProperty getOutputJar(); + + // Used to give JST more information about the classes. + @Classpath + public abstract ConfigurableFileCollection getLibraries(); + + @Internal + public abstract RegularFileProperty getLibrariesFile(); + + @Inject + public ApplyAccessTransformer() {} + + @Override + @TaskAction + public void exec() { + try { + FileUtils.writeLinesSafe( + getLibrariesFile().getAsFile().get().toPath(), + getLibraries().getFiles().stream().map(File::getAbsolutePath).toList(), + StandardCharsets.UTF_8); + } catch (IOException exception) { + throw new UncheckedIOException("Failed to write libraries for JST.", exception); + } + + args( + "--enable-accesstransformers", + "--access-transformer", getAccessTransformer().getAsFile().get().getAbsolutePath(), + "--libraries-list", getLibrariesFile().getAsFile().get().getAbsolutePath(), + getInputJar().getAsFile().get().getAbsolutePath(), + getOutputJar().getAsFile().get().getAbsolutePath()); + + super.exec(); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java new file mode 100644 index 0000000000..58ed2fc31b --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java @@ -0,0 +1,74 @@ +package net.neoforged.neodev; + +import io.codechicken.diffpatch.cli.PatchOperation; +import io.codechicken.diffpatch.util.Input.MultiInput; +import io.codechicken.diffpatch.util.Output.MultiOutput; +import io.codechicken.diffpatch.util.PatchMode; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +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.InputDirectory; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.gradle.work.DisableCachingByDefault; + +import javax.inject.Inject; +import java.io.IOException; + +@DisableCachingByDefault(because = "Not worth caching") +abstract class ApplyPatches extends DefaultTask { + @InputFile + public abstract RegularFileProperty getOriginalJar(); + + @InputDirectory + @PathSensitive(PathSensitivity.NONE) + public abstract DirectoryProperty getPatchesFolder(); + + @OutputFile + public abstract RegularFileProperty getPatchedJar(); + + @OutputDirectory + public abstract DirectoryProperty getRejectsFolder(); + + @Input + protected abstract Property getIsUpdating(); + + @Inject + public ApplyPatches(Project project) { + getIsUpdating().set(project.getProviders().gradleProperty("updating").map(Boolean::parseBoolean).orElse(false)); + } + + @TaskAction + public void applyPatches() throws IOException { + var isUpdating = getIsUpdating().get(); + + var builder = PatchOperation.builder() + .logTo(getLogger()::lifecycle) + .baseInput(MultiInput.detectedArchive(getOriginalJar().get().getAsFile().toPath())) + .patchesInput(MultiInput.folder(getPatchesFolder().get().getAsFile().toPath())) + .patchedOutput(MultiOutput.detectedArchive(getPatchedJar().get().getAsFile().toPath())) + .rejectsOutput(MultiOutput.folder(getRejectsFolder().get().getAsFile().toPath())) + .mode(isUpdating ? PatchMode.FUZZY : PatchMode.ACCESS) + .aPrefix("a/") + .bPrefix("b/") + .level(isUpdating ? io.codechicken.diffpatch.util.LogLevel.ALL : io.codechicken.diffpatch.util.LogLevel.WARN) + .minFuzz(0.9f); // The 0.5 default in DiffPatch is too low. + + var result = builder.build().operate(); + + int exit = result.exit; + if (exit != 0 && exit != 1) { + throw new RuntimeException("DiffPatch failed with exit code: " + exit); + } + if (exit != 0 && !isUpdating) { + throw new RuntimeException("Patches failed to apply."); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java b/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java new file mode 100644 index 0000000000..2cb2e89ade --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/CreateCleanArtifacts.java @@ -0,0 +1,36 @@ +package net.neoforged.neodev; + +import net.neoforged.nfrtgradle.CreateMinecraftArtifacts; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.OutputFile; + +import javax.inject.Inject; + +abstract class CreateCleanArtifacts extends CreateMinecraftArtifacts { + @OutputFile + abstract RegularFileProperty getCleanClientJar(); + + @OutputFile + abstract RegularFileProperty getRawServerJar(); + + @OutputFile + abstract RegularFileProperty getCleanServerJar(); + + @OutputFile + abstract RegularFileProperty getCleanJoinedJar(); + + @OutputFile + abstract RegularFileProperty getMergedMappings(); + + @Inject + public CreateCleanArtifacts() { + getAdditionalResults().put("node.stripClient.output.output", getCleanClientJar().getAsFile()); + getAdditionalResults().put("node.downloadServer.output.output", getRawServerJar().getAsFile()); + getAdditionalResults().put("node.stripServer.output.output", getCleanServerJar().getAsFile()); + getAdditionalResults().put("node.rename.output.output", getCleanJoinedJar().getAsFile()); + getAdditionalResults().put("node.mergeMappings.output.output", getMergedMappings().getAsFile()); + + // TODO: does anyone care about this? they should be contained in the client mappings + //"--write-result", "node.downloadServerMappings.output.output:" + getServerMappings().get().getAsFile().getAbsolutePath() + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java b/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java new file mode 100644 index 0000000000..35a06a8c33 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java @@ -0,0 +1,70 @@ +package net.neoforged.neodev; + +import org.gradle.api.GradleException; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputFile; + +import javax.inject.Inject; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +abstract class GenerateBinaryPatches extends JavaExec { + @Inject + public GenerateBinaryPatches() {} + + /** + * The jar file containing classes in the base state. + */ + @InputFile + abstract RegularFileProperty getCleanJar(); + + /** + * The jar file containing classes in the desired target state. + */ + @InputFile + abstract RegularFileProperty getPatchedJar(); + + @InputFile + abstract RegularFileProperty getMappings(); + + /** + * This directory of patch files for the Java sources is used as a hint to only diff class files that + * supposedly have changed. If it is not set, the tool will diff every .class file instead. + */ + @InputDirectory + @Optional + abstract DirectoryProperty getSourcePatchesFolder(); + + /** + * The location where the LZMA compressed binary patches are written to. + */ + @OutputFile + abstract RegularFileProperty getOutputFile(); + + @Override + public void exec() { + args("--clean", getCleanJar().get().getAsFile().getAbsolutePath()); + args("--dirty", getPatchedJar().get().getAsFile().getAbsolutePath()); + args("--srg", getMappings().get().getAsFile().getAbsolutePath()); + if (getSourcePatchesFolder().isPresent()) { + args("--patches", getSourcePatchesFolder().get().getAsFile().getAbsolutePath()); + } + args("--output", getOutputFile().get().getAsFile().getAbsolutePath()); + + var logFile = new File(getTemporaryDir(), "console.log"); + try (var out = new BufferedOutputStream(new FileOutputStream(logFile))) { + getLogger().info("Logging binpatcher console output to {}", logFile.getAbsolutePath()); + setStandardOutput(out); + super.exec(); + } catch (IOException e) { + throw new GradleException("Failed to create binary patches.", e); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java b/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java new file mode 100644 index 0000000000..c6e1c7e730 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/GenerateSourcePatches.java @@ -0,0 +1,55 @@ +package net.neoforged.neodev; + +import io.codechicken.diffpatch.cli.CliOperation; +import io.codechicken.diffpatch.cli.DiffOperation; +import io.codechicken.diffpatch.util.Input.MultiInput; +import io.codechicken.diffpatch.util.Output.MultiOutput; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.IOException; + +abstract class GenerateSourcePatches extends DefaultTask { + @InputFile + public abstract RegularFileProperty getOriginalJar(); + + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getModifiedSources(); + + @OutputFile + public abstract RegularFileProperty getPatchesJar(); + + @Inject + public GenerateSourcePatches() {} + + @TaskAction + public void generateSourcePatches() throws IOException { + var builder = DiffOperation.builder() + .logTo(getLogger()::lifecycle) + .baseInput(MultiInput.detectedArchive(getOriginalJar().get().getAsFile().toPath())) + .changedInput(MultiInput.folder(getModifiedSources().get().getAsFile().toPath())) + .patchesOutput(MultiOutput.detectedArchive(getPatchesJar().get().getAsFile().toPath())) + .autoHeader(true) + .level(io.codechicken.diffpatch.util.LogLevel.WARN) + .summary(false) + .aPrefix("a/") + .bPrefix("b/") + .lineEnding("\n"); + + CliOperation.Result result = builder.build().operate(); + + int exit = result.exit; + if (exit != 0 && exit != 1) { + throw new RuntimeException("DiffPatch failed with exit code: " + exit); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java new file mode 100644 index 0000000000..9a5c382b4b --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java @@ -0,0 +1,23 @@ +package net.neoforged.neodev; + +import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; +import net.neoforged.nfrtgradle.CreateMinecraftArtifacts; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.Sync; + +public class NeoDevBasePlugin implements Plugin { + @Override + public void apply(Project project) { + // These plugins allow us to declare dependencies on Minecraft libraries needed to compile the official sources + project.getPlugins().apply(MinecraftDependenciesPlugin.class); + + var createSources = NeoDevPlugin.configureMinecraftDecompilation(project); + + project.getTasks().register("setup", Sync.class, task -> { + task.setDescription("Replaces the contents of the base project sources with the unpatched, decompiled Minecraft source code."); + task.from(project.zipTree(createSources.flatMap(CreateMinecraftArtifacts::getSourcesArtifact))); + task.into(project.file("src/main/java/")); + }); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java new file mode 100644 index 0000000000..224c7ab7e9 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtension.java @@ -0,0 +1,35 @@ +package net.neoforged.neodev; + +import net.neoforged.moddevgradle.dsl.ModModel; +import net.neoforged.moddevgradle.dsl.RunModel; +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Project; + +public class NeoDevExtension { + public static final String NAME = "neoDev"; + + private final NamedDomainObjectContainer mods; + private final NamedDomainObjectContainer runs; + + public NeoDevExtension(Project project) { + mods = project.container(ModModel.class); + runs = project.container(RunModel.class, name -> project.getObjects().newInstance(RunModel.class, name, project, mods)); + } + + public NamedDomainObjectContainer getMods() { + return mods; + } + + public void mods(Action> action) { + action.execute(mods); + } + + public NamedDomainObjectContainer getRuns() { + return runs; + } + + public void runs(Action> action) { + action.execute(runs); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java new file mode 100644 index 0000000000..fabb8f75ac --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -0,0 +1,95 @@ +package net.neoforged.neodev; + +import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; +import net.neoforged.moddevgradle.internal.NeoDevFacade; +import net.neoforged.nfrtgradle.CreateMinecraftArtifacts; +import net.neoforged.nfrtgradle.DownloadAssets; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ProjectDependency; +import org.gradle.api.artifacts.dsl.DependencyFactory; +import org.gradle.api.tasks.testing.Test; + +import java.util.function.Consumer; + +// TODO: the only point of this is to configure runs that depend on neoforge. Maybe this could be done with less code duplication... +// TODO: Gradle says "thou shalt not referenceth otherth projects" yet here we are +// TODO: depend on neoforge configurations that the moddev plugin also uses +public class NeoDevExtraPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(MinecraftDependenciesPlugin.class); + + var neoForgeProject = project.getRootProject().getChildProjects().get("neoforge"); + + var dependencyFactory = project.getDependencyFactory(); + var tasks = project.getTasks(); + var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev"); + + var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); + + var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version"); + var minecraftVersion = project.getProviders().gradleProperty("minecraft_version"); + var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); + + // TODO: this is temporary + var modulesConfiguration = project.getConfigurations().create("moduleOnly", spec -> { + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "moduleOnly")); + }); + + var downloadAssets = neoForgeProject.getTasks().named("downloadAssets", DownloadAssets.class); + var createArtifacts = neoForgeProject.getTasks().named("createSourceArtifacts", CreateMinecraftArtifacts.class); + var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", WriteUserDevConfig.class); + + Consumer configureLegacyClasspath = spec -> { + spec.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> { + caps.requireCapability("net.neoforged:neoform-dependencies"); + }))); + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "installer")); + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "moduleOnly")); + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "userdevCompileOnly")); + // TODO: Convert into a cross-project dependency too + spec.getDependencies().add( + dependencyFactory.create( + project.files(createArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) + ) + ); + }; + + extension.getRuns().configureEach(run -> { + configureLegacyClasspath.accept(run.getAdditionalRuntimeClasspathConfiguration()); + }); + NeoDevFacade.setupRuns( + project, + neoDevBuildDir, + extension.getRuns(), + writeNeoDevConfig, + modulePath -> modulePath.extendsFrom(modulesConfiguration), + configureLegacyClasspath, + downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) + ); + + var testExtension = project.getExtensions().create(NeoDevTestExtension.NAME, NeoDevTestExtension.class); + var testTask = tasks.register("junitTest", Test.class, test -> test.setGroup("verification")); + tasks.named("check").configure(task -> task.dependsOn(testTask)); + + NeoDevFacade.setupTestTask( + project, + neoDevBuildDir, + testTask, + writeNeoDevConfig, + testExtension.getLoadedMods(), + testExtension.getTestedMod(), + modulePath -> modulePath.extendsFrom(modulesConfiguration), + configureLegacyClasspath, + downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) + ); + } + + private static ProjectDependency projectDep(DependencyFactory dependencyFactory, Project project, String configurationName) { + var dep = dependencyFactory.create(project); + dep.setTargetConfiguration(configurationName); + return dep; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java new file mode 100644 index 0000000000..36f720a2d3 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -0,0 +1,550 @@ +package net.neoforged.neodev; + +import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; +import net.neoforged.moddevgradle.internal.NeoDevFacade; +import net.neoforged.moddevgradle.internal.utils.DependencyUtils; +import net.neoforged.moddevgradle.tasks.JarJar; +import net.neoforged.neodev.installer.CreateArgsFile; +import net.neoforged.neodev.installer.CreateInstallerProfile; +import net.neoforged.neodev.installer.CreateLauncherProfile; +import net.neoforged.neodev.installer.InstallerProcessor; +import net.neoforged.nfrtgradle.CreateMinecraftArtifacts; +import net.neoforged.nfrtgradle.DownloadAssets; +import net.neoforged.nfrtgradle.NeoFormRuntimePlugin; +import net.neoforged.nfrtgradle.NeoFormRuntimeTask; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.repositories.MavenArtifactRepository; +import org.gradle.api.file.Directory; +import org.gradle.api.file.RegularFile; +import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Sync; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.api.tasks.bundling.Zip; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class NeoDevPlugin implements Plugin { + @Override + public void apply(Project project) { + project.getPlugins().apply(MinecraftDependenciesPlugin.class); + + var createSourceArtifacts = configureMinecraftDecompilation(project); + + var configurations = project.getConfigurations(); + var runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + var dependencyFactory = project.getDependencyFactory(); + var tasks = project.getTasks(); + var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev"); + + var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version"); + var fmlVersion = project.getProviders().gradleProperty("fancy_mod_loader_version"); + var minecraftVersion = project.getProviders().gradleProperty("minecraft_version"); + var neoForgeVersion = project.provider(() -> (String) project.getVersion()); // TODO: is this correct? + var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); + + var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); + + var neoFormDependencies = configurations.create("neoFormDependencies", spec -> { + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.getDependencies().addLater(mcAndNeoFormVersion.map(version -> { + var dep = dependencyFactory.create("net.neoforged:neoform:" + version).capabilities(caps -> { + caps.requireCapability("net.neoforged:neoform-dependencies"); + }); + dep.endorseStrictVersions(); + return dep; + })); + }); + + var jstConfiguration = configurations.create("javaSourceTransformer", files -> { + files.setCanBeConsumed(false); + files.setCanBeResolved(true); + files.defaultDependencies(spec -> { + spec.add(Tools.JST.asDependency(project)); + }); + }); + + var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); + var applyAt = tasks.register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { + task.classpath(jstConfiguration); + task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); + task.getAccessTransformer().set(atFile); + task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar"))); + task.getLibraries().from(neoFormDependencies); + task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt"))); + }); + + var patchesFolder = project.getRootProject().file("patches"); + var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> { + task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); + task.getPatchesFolder().set(patchesFolder); + task.getPatchedJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/patched-sources.jar"))); + task.getRejectsFolder().set(project.getRootProject().file("rejects")); + }); + + var mcSourcesPath = project.file("src/main/java"); + tasks.register("setup", Sync.class, task -> { + task.from(project.zipTree(applyPatches.flatMap(ApplyPatches::getPatchedJar))); + task.into(mcSourcesPath); + }); + + var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> { + task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(v -> "net.neoforged:neoform:" + v + "@zip")); + task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); + }); + + var installerConfiguration = project.getConfigurations().create("installer"); + var installerLibrariesConfiguration = configurations.create("installerLibraries"); + var modulesConfiguration = configurations.create("moduleOnly"); + var userDevCompileOnlyConfiguration = configurations.create("userdevCompileOnly"); + var userDevTestImplementationConfiguration = configurations.create("userdevTestImplementation"); + userDevTestImplementationConfiguration.shouldResolveConsistentlyWith(runtimeClasspath); + + var devLibrariesConfiguration = configurations.create("devLibraries", files -> { + files.setCanBeConsumed(false); + files.setCanBeResolved(true); + }); + devLibrariesConfiguration.shouldResolveConsistentlyWith(runtimeClasspath); + devLibrariesConfiguration.extendsFrom(installerLibrariesConfiguration, modulesConfiguration, userDevCompileOnlyConfiguration); + + var writeNeoDevConfig = tasks.register("writeNeoDevConfig", WriteUserDevConfig.class, task -> { + task.getForNeoDev().set(true); + task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("neodev-config.json"))); + }); + var writeUserDevConfig = tasks.register("writeUserDevConfig", WriteUserDevConfig.class, task -> { + task.getForNeoDev().set(false); + task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("userdev-config.json"))); + }); + for (var taskProvider : List.of(writeNeoDevConfig, writeUserDevConfig)) { + taskProvider.configure(task -> { + task.getFmlVersion().set(fmlVersion); + task.getMinecraftVersion().set(minecraftVersion); + task.getNeoForgeVersion().set(neoForgeVersion); + task.getRawNeoFormVersion().set(rawNeoFormVersion); + task.getLibraries().addAll(configurationToGavList(devLibrariesConfiguration)); + task.getModules().addAll(configurationToGavList(modulesConfiguration)); + task.getTestLibraries().addAll(configurationToGavList(userDevTestImplementationConfiguration)); + task.getTestLibraries().add(neoForgeVersion.map(v -> "net.neoforged:testframework:" + v)); + task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); + })); + task.getIgnoreList().addAll(userDevCompileOnlyConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); + })); + task.getIgnoreList().addAll("client-extra", "neoforge-"); + task.getBinpatcherGav().set(Tools.BINPATCHER.asGav(project)); + }); + } + + NeoDevFacade.setupRuns( + project, + neoDevBuildDir, + extension.getRuns(), + writeNeoDevConfig, + modulePath -> { + modulePath.extendsFrom(modulesConfiguration); + }, + legacyClassPath -> { + legacyClassPath.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> { + caps.requireCapability("net.neoforged:neoform-dependencies"); + }))); + legacyClassPath.getDependencies().add( + dependencyFactory.create( + project.files(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) + ) + ); + legacyClassPath.extendsFrom(installerLibrariesConfiguration, modulesConfiguration, userDevCompileOnlyConfiguration); + }, + downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) + ); + + var genSourcePatches = tasks.register("generateSourcePatches", GenerateSourcePatches.class, task -> { + task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); + task.getModifiedSources().set(project.file("src/main/java")); + task.getPatchesJar().set(neoDevBuildDir.map(dir -> dir.file("source-patches.zip"))); + }); + + var genPatches = tasks.register("genPatches", Sync.class, task -> { + task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar))); + task.into(project.getRootProject().file("patches")); + }); + + // TODO: signing? + var universalJar = tasks.register("universalJar", Jar.class, task -> { + task.getArchiveClassifier().set("universal"); + + task.from(project.zipTree( + tasks.named("jar", Jar.class).flatMap(AbstractArchiveTask::getArchiveFile))); + task.exclude("net/minecraft/**"); + task.exclude("com/**"); + task.exclude("mcp/**"); + + task.manifest(manifest -> { + manifest.attributes(Map.of("FML-System-Mods", "neoforge")); + manifest.attributes( + Map.of( + "Specification-Title", "NeoForge", + "Specification-Vendor", "NeoForge", + "Specification-Version", project.getVersion().toString().substring(0, project.getVersion().toString().lastIndexOf(".")), + "Implementation-Title", project.getGroup(), + "Implementation-Version", project.getVersion(), + "Implementation-Vendor", "NeoForged"), + "net/neoforged/neoforge/internal/versions/neoforge/"); + manifest.attributes( + Map.of( + "Specification-Title", "Minecraft", + "Specification-Vendor", "Mojang", + "Specification-Version", minecraftVersion, + "Implementation-Title", "MCP", + "Implementation-Version", mcAndNeoFormVersion, + "Implementation-Vendor", "NeoForged"), + "net/neoforged/neoforge/versions/neoform/"); + }); + }); + + var jarJarTask = JarJar.registerWithConfiguration(project, "jarJar"); + universalJar.configure(task -> task.from(jarJarTask)); + + var createCleanArtifacts = tasks.register("createCleanArtifacts", CreateCleanArtifacts.class, task -> { + var cleanArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts/clean")); + task.getCleanClientJar().set(cleanArtifactsDir.map(dir -> dir.file("client.jar"))); + task.getRawServerJar().set(cleanArtifactsDir.map(dir -> dir.file("raw-server.jar"))); + task.getCleanServerJar().set(cleanArtifactsDir.map(dir -> dir.file("server.jar"))); + task.getCleanJoinedJar().set(cleanArtifactsDir.map(dir -> dir.file("joined.jar"))); + task.getMergedMappings().set(cleanArtifactsDir.map(dir -> dir.file("merged-mappings.txt"))); + task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip")); + }); + + var binaryPatchOutputs = configureBinaryPatchCreation( + project, + createCleanArtifacts, + neoDevBuildDir, + patchesFolder + ); + + var createLauncherProfile = tasks.register("createLauncherProfile", CreateLauncherProfile.class, task -> { + task.getFmlVersion().set(fmlVersion); + task.getMinecraftVersion().set(minecraftVersion); + task.getNeoForgeVersion().set(neoForgeVersion); + task.getRawNeoFormVersion().set(rawNeoFormVersion); + task.getLibraries().from(installerConfiguration, modulesConfiguration); + task.getRepositoryURLs().set(project.provider(() -> { + List repos = new ArrayList<>(); + for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { + var uri = repo.getUrl(); + if (!uri.toString().endsWith("/")) { + uri = URI.create(uri + "/"); + } + repos.add(uri); + } + return repos; + })); + task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); + })); + task.getIgnoreList().addAll("client-extra", "neoforge-"); + task.getModulePath().from(modulesConfiguration); + task.getLauncherProfile().set(neoDevBuildDir.map(dir -> dir.file("launcher-profile.json"))); + }); + + var installerProfileLibraries = configurations.create("installerProfileLibraries", spec -> { + spec.setCanBeResolved(true); + spec.setCanBeConsumed(false); + }); + installerProfileLibraries.extendsFrom(installerLibrariesConfiguration); + installerProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); + var createInstallerProfile = tasks.register("createInstallerProfile", CreateInstallerProfile.class, task -> { + task.getMinecraftVersion().set(minecraftVersion); + task.getNeoForgeVersion().set(neoForgeVersion); + task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion); + task.getIcon().set(project.getRootProject().file("docs/assets/neoforged.ico")); + task.getLibraries().from(installerProfileLibraries); + task.getRepositoryURLs().set(project.provider(() -> { + List repos = new ArrayList<>(); + for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { + var uri = repo.getUrl(); + if (!uri.toString().endsWith("/")) { + uri = URI.create(uri + "/"); + } + repos.add(uri); + } + return repos; + })); + task.getUniversalJar().set(universalJar.flatMap(AbstractArchiveTask::getArchiveFile)); + task.getInstallerProfile().set(neoDevBuildDir.map(dir -> dir.file("installer-profile.json"))); + }); + + for (var installerProcessor : InstallerProcessor.values()) { + var configuration = configurations.create("installerProcessor" + installerProcessor.toString(), files -> { + files.setCanBeConsumed(false); + files.setCanBeResolved(true); + files.getDependencies().add(installerProcessor.tool.asDependency(project)); + }); + installerProfileLibraries.extendsFrom(configuration); + // Each tool should resolve consistently with the full set of installed libraries. + configuration.shouldResolveConsistentlyWith(installerProfileLibraries); + createInstallerProfile.configure(task -> { + task.getProcessorClasspaths().put(installerProcessor, configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + // Using .toList() fails with the configuration cache - looks like Gradle can't deserialize the resulting list? + return results.stream().map(DependencyUtils::guessMavenGav).collect(Collectors.toCollection(ArrayList::new)); + })); + task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project)); + }); + } + + var createWindowsServerArgsFile = tasks.register("createWindowsServerArgsFile", CreateArgsFile.class, task -> { + task.getPathSeparator().set(";"); + task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("windows-server-args.txt"))); + }); + var createUnixServerArgsFile = tasks.register("createUnixServerArgsFile", CreateArgsFile.class, task -> { + task.getPathSeparator().set(":"); + task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("unix-server-args.txt"))); + }); + + for (var taskProvider : List.of(createWindowsServerArgsFile, createUnixServerArgsFile)) { + taskProvider.configure(task -> { + task.getTemplate().set(project.getRootProject().file("server_files/args.txt")); + task.getFmlVersion().set(fmlVersion); + task.getMinecraftVersion().set(minecraftVersion); + task.getNeoForgeVersion().set(neoForgeVersion); + task.getRawNeoFormVersion().set(rawNeoFormVersion); + task.getModules().from(modulesConfiguration); + task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); + })); + task.getClasspath().from(installerConfiguration); + task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar)); + }); + } + + var installerConfig = configurations.create("legacyInstaller", files -> { + files.setCanBeConsumed(false); + files.setCanBeResolved(true); + files.setTransitive(false); + files.getDependencies().add(Tools.LEGACYINSTALLER.asDependency(project)); + }); + // TODO: signing? + // We want to use the manifest from LegacyInstaller. + // - Jar tasks have special manifest handling, so use Zip. + // - The manifest must be the first entry in the jar so LegacyInstaller has to be the first input. + var installerJar = tasks.register("installerJar", Zip.class, task -> { + task.getArchiveClassifier().set("installer"); + task.getArchiveExtension().set("jar"); + task.setMetadataCharset("UTF-8"); + task.getDestinationDirectory().convention(project.getExtensions().getByType(BasePluginExtension.class).getLibsDirectory()); + + // TODO: is this correct? + task.from(project.zipTree(project.provider(installerConfig::getSingleFile)), spec -> { + spec.exclude("big_logo.png"); + }); + task.from(createLauncherProfile.flatMap(CreateLauncherProfile::getLauncherProfile), spec -> { + spec.rename(s -> "version.json"); + }); + task.from(createInstallerProfile.flatMap(CreateInstallerProfile::getInstallerProfile), spec -> { + spec.rename(s -> "install_profile.json"); + }); + task.from(project.getRootProject().file("src/main/resources/url.png")); + task.from(project.getRootProject().file("src/main/resources/neoforged_logo.png"), spec -> { + spec.rename(s -> "big_logo.png"); + }); + task.from(createUnixServerArgsFile.flatMap(CreateArgsFile::getArgsFile), spec -> { + spec.into("data"); + spec.rename(s -> "unix_args.txt"); + }); + task.from(createWindowsServerArgsFile.flatMap(CreateArgsFile::getArgsFile), spec -> { + spec.into("data"); + spec.rename(s -> "win_args.txt"); + }); + task.from(binaryPatchOutputs.binaryPatchesForClient(), spec -> { + spec.into("data"); + spec.rename(s -> "client.lzma"); + }); + task.from(binaryPatchOutputs.binaryPatchesForServer(), spec -> { + spec.into("data"); + spec.rename(s -> "server.lzma"); + }); + var mavenPath = neoForgeVersion.map(v -> "net/neoforged/neoforge/" + v); + task.getInputs().property("mavenPath", mavenPath); + task.from(project.getRootProject().files("server_files"), spec -> { + spec.into("data"); + spec.exclude("args.txt"); + spec.filter(s -> { + return s.replaceAll("@MAVEN_PATH@", mavenPath.get()); + }); + }); + + // This is true by default (see gradle.properties), and needs to be disabled explicitly when building (see release.yml). + if (project.getProperties().containsKey("neogradle.runtime.platform.installer.debug") && Boolean.parseBoolean(project.getProperties().get("neogradle.runtime.platform.installer.debug").toString())) { + task.from(universalJar.flatMap(AbstractArchiveTask::getArchiveFile), spec -> { + spec.into(String.format("/maven/net/neoforged/neoforge/%s/", neoForgeVersion.get())); + spec.rename(name -> String.format("neoforge-%s-universal.jar", neoForgeVersion.get())); + }); + } + }); + + var userdevJar = tasks.register("userdevJar", Jar.class, task -> { + task.getArchiveClassifier().set("userdev"); + + task.from(writeUserDevConfig.flatMap(WriteUserDevConfig::getUserDevConfig), spec -> { + spec.rename(s -> "config.json"); + }); + task.from(atFile, spec -> { + spec.into("ats/"); + }); + task.from(binaryPatchOutputs.binaryPatchesForMerged(), spec -> { + spec.rename(s -> "joined.lzma"); + }); + task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar)), spec -> { + spec.into("patches/"); + }); + }); + + project.getExtensions().getByType(JavaPluginExtension.class).withSourcesJar(); + final TaskProvider sourcesJarProvider = project.getTasks().named("sourcesJar", Jar.class); + sourcesJarProvider.configure(task -> { + task.exclude("net/minecraft/**"); + task.exclude("com/**"); + task.exclude("mcp/**"); + }); + + tasks.named("assemble", task -> { + task.dependsOn(installerJar); + task.dependsOn(universalJar); + task.dependsOn(userdevJar); + task.dependsOn(sourcesJarProvider); + }); + } + + private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, + TaskProvider createCleanArtifacts, + Provider neoDevBuildDir, + File sourcesPatchesFolder) { + var configurations = project.getConfigurations(); + var tasks = project.getTasks(); + + var artConfig = configurations.create("art", spec -> { + spec.setDescription("Used to resolve the jar remapping tool"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.getDependencies().add(Tools.AUTO_RENAMING_TOOL.asDependency(project)); + }); + var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> { + task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client."); + task.classpath(artConfig); + task.getMainClass().set("net.neoforged.art.Main"); + task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanClientJar)); + task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar"))); + }); + var remapServerJar = tasks.register("remapServerJar", RemapJar.class, task -> { + task.setDescription("Creates a Minecraft dedicated server jar with the official mappings applied. Used as the base for generating binary patches for the client."); + task.classpath(artConfig); + task.getMainClass().set("net.neoforged.art.Main"); + task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanServerJar)); + task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar"))); + }); + + var binpatcherConfig = configurations.create("binpatcher", spec -> { + spec.setDescription("Used to resolve the tool for creating binary patches"); + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.setTransitive(false); + spec.getDependencies().add(Tools.BINPATCHER.asDependency(project)); + }); + var generateMergedBinPatches = tasks.register("generateMergedBinPatches", GenerateBinaryPatches.class, task -> { + task.setDescription("Creates binary patch files by diffing a merged client/server jar-file and the compiled Minecraft classes in this project."); + task.classpath(binpatcherConfig); + task.getCleanJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanJoinedJar)); + task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); + task.getSourcePatchesFolder().set(sourcesPatchesFolder); + task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("merged-binpatches.lzma"))); + }); + var generateClientBinPatches = tasks.register("generateClientBinPatches", GenerateBinaryPatches.class, task -> { + task.setDescription("Creates binary patch files by diffing a merged client jar-file and the compiled Minecraft classes in this project."); + task.classpath(binpatcherConfig); + task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getMojmapJar)); + task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); + task.getSourcePatchesFolder().set(sourcesPatchesFolder); + task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("client-binpatches.lzma"))); + }); + var generateServerBinPatches = tasks.register("generateServerBinPatches", GenerateBinaryPatches.class, task -> { + task.setDescription("Creates binary patch files by diffing a merged server jar-file and the compiled Minecraft classes in this project."); + task.classpath(binpatcherConfig); + task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getMojmapJar)); + task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); + task.getSourcePatchesFolder().set(sourcesPatchesFolder); + task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("server-binpatches.lzma"))); + }); + + return new BinaryPatchOutputs( + generateMergedBinPatches.flatMap(GenerateBinaryPatches::getOutputFile), + generateClientBinPatches.flatMap(GenerateBinaryPatches::getOutputFile), + generateServerBinPatches.flatMap(GenerateBinaryPatches::getOutputFile) + ); + } + + private record BinaryPatchOutputs( + Provider binaryPatchesForMerged, + Provider binaryPatchesForClient, + Provider binaryPatchesForServer + ) { + } + + /** + * Sets up NFRT, and creates the sources and resources artifacts. + */ + static TaskProvider configureMinecraftDecompilation(Project project) { + project.getPlugins().apply(NeoFormRuntimePlugin.class); + + var configurations = project.getConfigurations(); + var dependencyFactory = project.getDependencyFactory(); + var tasks = project.getTasks(); + var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev"); + + var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version"); + var minecraftVersion = project.getProviders().gradleProperty("minecraft_version"); + var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); + + // Configuration for all artifacts that should be passed to NFRT to prevent repeated downloads + var neoFormRuntimeArtifactManifestNeoForm = configurations.create("neoFormRuntimeArtifactManifestNeoForm", spec -> { + spec.setCanBeConsumed(false); + spec.setCanBeResolved(true); + spec.getDependencies().addLater(mcAndNeoFormVersion.map(version -> { + return dependencyFactory.create("net.neoforged:neoform:" + version); + })); + }); + + tasks.withType(NeoFormRuntimeTask.class, task -> { + task.addArtifactsToManifest(neoFormRuntimeArtifactManifestNeoForm); + }); + + return tasks.register("createSourceArtifacts", CreateMinecraftArtifacts.class, task -> { + var minecraftArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts")); + task.getSourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("base-sources.jar"))); + task.getResourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("minecraft-local-resources-aka-client-extra.jar"))); + task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip")); + }); + } + + private Provider> configurationToGavList(Configuration configuration) { + return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(DependencyUtils::guessMavenGav).toList(); + }); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java new file mode 100644 index 0000000000..57d3f7be33 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevTestExtension.java @@ -0,0 +1,30 @@ +package net.neoforged.neodev; + +import net.neoforged.moddevgradle.dsl.ModModel; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.SetProperty; + +import javax.inject.Inject; + +public abstract class NeoDevTestExtension { + public static final String NAME = "neoDevTest"; + + @Inject + public NeoDevTestExtension() { + } + + /** + * The mod that will be loaded in JUnit tests. + * The compiled classes from {@code src/test/java} and the resources from {@code src/test/resources} + * will be added to that mod at runtime. + */ + public abstract Property getTestedMod(); + + /** + * The mods to load when running unit tests. Defaults to all mods registered in the project. + * This must contain {@link #getTestedMod()}. + * + * @see ModModel + */ + public abstract SetProperty getLoadedMods(); +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java new file mode 100644 index 0000000000..d9f3d943be --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java @@ -0,0 +1,44 @@ +package net.neoforged.neodev; + +import org.gradle.api.GradleException; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.JavaExec; +import org.gradle.api.tasks.OutputFile; + +import javax.inject.Inject; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +abstract class RemapJar extends JavaExec { + @Inject + public RemapJar() {} + + @InputFile + abstract RegularFileProperty getObfSlimJar(); + + @InputFile + abstract RegularFileProperty getMergedMappings(); + + @OutputFile + abstract RegularFileProperty getMojmapJar(); + + @Override + public void exec() { + args("--input", getObfSlimJar().get().getAsFile().getAbsolutePath()); + args("--output", getMojmapJar().get().getAsFile().getAbsolutePath()); + args("--names", getMergedMappings().get().getAsFile().getAbsolutePath()); + args("--ann-fix", "--ids-fix", "--src-fix", "--record-fix"); + + var logFile = new File(getTemporaryDir(), "console.log"); + try (var out = new BufferedOutputStream(new FileOutputStream(logFile))) { + getLogger().info("Logging ART console output to {}", logFile.getAbsolutePath()); + setStandardOutput(out); + super.exec(); + } catch (IOException e) { + throw new GradleException("Failed to create binary patches.", e); + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java new file mode 100644 index 0000000000..8a4ad2d2e7 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -0,0 +1,33 @@ +package net.neoforged.neodev; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; + +public enum Tools { + JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version"), + LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version"), + AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version"), + INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version"), + JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version"), + BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version"); + + private final String gavPattern; + private final String versionProperty; + + Tools(String gavPattern, String versionProperty) { + this.gavPattern = gavPattern; + this.versionProperty = versionProperty; + } + + public String asGav(Project project) { + var version = project.property(versionProperty); + if (version == null) { + throw new IllegalStateException("Could not find property " + versionProperty); + } + return gavPattern.formatted(version); + } + + public Dependency asDependency(Project project) { + return project.getDependencyFactory().create(asGav(project)); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java b/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java new file mode 100644 index 0000000000..25bc50b283 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java @@ -0,0 +1,194 @@ +package net.neoforged.neodev; + +import com.google.gson.GsonBuilder; +import net.neoforged.moddevgradle.internal.utils.FileUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +abstract class WriteUserDevConfig extends DefaultTask { + @Inject + public WriteUserDevConfig() {} + + @Input + abstract Property getForNeoDev(); + + @Input + abstract Property getFmlVersion(); + + @Input + abstract Property getMinecraftVersion(); + + @Input + abstract Property getNeoForgeVersion(); + + @Input + abstract Property getRawNeoFormVersion(); + + @Input + abstract ListProperty getLibraries(); + + @Input + abstract ListProperty getModules(); + + @Input + abstract ListProperty getTestLibraries(); + + @Input + abstract ListProperty getIgnoreList(); + + @Input + abstract Property getBinpatcherGav(); + + @OutputFile + abstract RegularFileProperty getUserDevConfig(); + + @TaskAction + public void writeUserDevConfig() throws IOException { + var config = new UserDevConfig( + 2, + "net.neoforged:neoform:%s-%s@zip".formatted(getMinecraftVersion().get(), getRawNeoFormVersion().get()), + "ats/", + "joined.lzma", + new BinpatcherConfig( + getBinpatcherGav().get(), + List.of("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}")), + "patches/", + "net.neoforged:neoforge:%s:sources".formatted(getNeoForgeVersion().get()), + "net.neoforged:neoforge:%s:universal".formatted(getNeoForgeVersion().get()), + getLibraries().get(), + getTestLibraries().get(), + new LinkedHashMap<>(), + getModules().get()); + + for (var runType : RunType.values()) { + var launchTarget = switch (runType) { + case CLIENT -> "forgeclient"; + case DATA -> "forgedata"; + case GAME_TEST_SERVER, SERVER -> "forgeserver"; + case JUNIT -> "forgejunit"; + } + (getForNeoDev().get() ? "dev" : "userdev"); + + List args = new ArrayList<>(); + Collections.addAll(args, + "--launchTarget", launchTarget); + + if (runType == RunType.CLIENT || runType == RunType.JUNIT) { + // TODO: this is copied from NG but shouldn't it be the MC version? + Collections.addAll(args, + "--version", getNeoForgeVersion().get()); + } + + if (runType == RunType.CLIENT || runType == RunType.DATA || runType == RunType.JUNIT) { + Collections.addAll(args, + "--assetIndex", "{asset_index}", + "--assetsDir", "{assets_root}"); + } + + Collections.addAll(args, + "--gameDir", ".", + "--fml.fmlVersion", getFmlVersion().get(), + "--fml.mcVersion", getMinecraftVersion().get(), + "--fml.neoForgeVersion", getNeoForgeVersion().get(), + "--fml.neoFormVersion", getRawNeoFormVersion().get()); + + Map systemProperties = new LinkedHashMap<>(); + systemProperties.put("java.net.preferIPv6Addresses", "system"); + systemProperties.put("ignoreList", String.join(",", getIgnoreList().get())); + systemProperties.put("legacyClassPath.file", "{minecraft_classpath_file}"); + + if (runType == RunType.CLIENT || runType == RunType.GAME_TEST_SERVER) { + systemProperties.put("neoforge.enableGameTest", "true"); + + if (runType == RunType.GAME_TEST_SERVER) { + systemProperties.put("neoforge.gameTestServer", "true"); + } + } + + config.runs().put(runType.jsonName, new UserDevRunType( + runType != RunType.JUNIT, + "cpw.mods.bootstraplauncher.BootstrapLauncher", + args, + List.of( + "-p", "{modules}", + "--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"), + runType == RunType.CLIENT || runType == RunType.JUNIT, + runType == RunType.GAME_TEST_SERVER || runType == RunType.SERVER, + runType == RunType.DATA, + runType == RunType.CLIENT || runType == RunType.GAME_TEST_SERVER, + runType == RunType.JUNIT, + Map.of( + "MOD_CLASSES", "{source_roots}"), + systemProperties + )); + } + + FileUtils.writeStringSafe( + getUserDevConfig().getAsFile().get().toPath(), + new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(config), + // TODO: Not sure what this should be? Most likely the file is ASCII. + StandardCharsets.UTF_8); + } + + private enum RunType { + CLIENT("client"), + DATA("data"), + GAME_TEST_SERVER("gameTestServer"), + SERVER("server"), + JUNIT("junit"); + + private final String jsonName; + + RunType(String jsonName) { + this.jsonName = jsonName; + } + } +} + +record UserDevConfig( + int spec, + String mcp, + String ats, + String binpatches, + BinpatcherConfig binpatcher, + String patches, + String sources, + String universal, + List libraries, + List testLibraries, + Map runs, + List modules) {} + +record BinpatcherConfig( + String version, + List args) {} + +record UserDevRunType( + boolean singleInstance, + String main, + List args, + List jvmArgs, + boolean client, + boolean server, + boolean dataGenerator, + boolean gameTest, + boolean unitTest, + Map env, + Map props) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java new file mode 100644 index 0000000000..59aa0b69a8 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java @@ -0,0 +1,47 @@ +package net.neoforged.neodev.installer; + +import java.io.File; + +class ArtifactPathsCollector extends ModuleIdentificationVisitor { + + private final StringBuilder builder = new StringBuilder(); + private final String separator; + private final String prefix; + + public ArtifactPathsCollector(String separator, String prefix) { + this.separator = separator; + this.prefix = prefix; + } + + @Override + protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) throws Exception { + builder.append(prefix); + builder.append(group.replace(".", "/")); + builder.append("/"); + builder.append(module); + builder.append("/"); + builder.append(version); + builder.append("/"); + builder.append(module); + builder.append("-"); + builder.append(version); + + if (classifier != null && !classifier.isEmpty()) { + builder.append("-"); + builder.append(classifier); + } + + builder.append(".").append(extension); + builder.append(separator); + } + + @Override + public String toString() { + String result = builder.toString(); + if (result.endsWith(separator)) { + return result.substring(0, result.length() - separator.length()); + } else { + return result; + } + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java new file mode 100644 index 0000000000..159484286c --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java @@ -0,0 +1,128 @@ +package net.neoforged.neodev.installer; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ArchiveOperations; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class CreateArgsFile extends DefaultTask { + @Inject + public CreateArgsFile() {} + + @InputFile + public abstract RegularFileProperty getTemplate(); + + @Input + public abstract Property getFmlVersion(); + + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getRawNeoFormVersion(); + + @Input + public abstract Property getPathSeparator(); + + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getModules(); + + @Input + public abstract ListProperty getIgnoreList(); + + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getClasspath(); + + @InputFile + public abstract RegularFileProperty getRawServerJar(); + + @OutputFile + public abstract RegularFileProperty getArgsFile(); + + @Inject + protected abstract ArchiveOperations getArchiveOperations(); + + private String resolveClasspath() throws IOException { + ArtifactPathsCollector classpathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); + getClasspath().getAsFileTree().visit(classpathCollector); + + var ourClasspath = classpathCollector + getPathSeparator().get() + + "libraries/net/minecraft/server/%s/server-%s-extra.jar".formatted( + getRawNeoFormVersion().get(), getRawNeoFormVersion().get()); + + // The raw server jar also contains its own classpath. + // We want to make sure that our versions of the libraries are used when there is a conflict. + var ourClasspathEntries = Stream.of(ourClasspath.split(getPathSeparator().get())) + .map(CreateArgsFile::stripVersionSuffix) + .collect(Collectors.toSet()); + + var serverClasspath = getArchiveOperations().zipTree(getRawServerJar()) + .filter(spec -> spec.getPath().endsWith("META-INF" + File.separator + "classpath-joined")) + .getSingleFile(); + + var filteredServerClasspath = Stream.of(Files.readString(serverClasspath.toPath()).split(";")) + .filter(path -> !ourClasspathEntries.contains(stripVersionSuffix(path))) + // Exclude the actual MC server jar, which is under versions/ + .filter(path -> path.startsWith("libraries/")) + .collect(Collectors.joining(getPathSeparator().get())); + + return ourClasspath + getPathSeparator().get() + filteredServerClasspath; + } + + // Example: + // Convert "libraries/com/github/oshi/oshi-core/6.4.10/oshi-core-6.4.10.jar" + // to "libraries/com/github/oshi/oshi-core". + private static String stripVersionSuffix(String classpathEntry) { + var parts = classpathEntry.split("/"); + return String.join("/", List.of(parts).subList(0, parts.length - 2)); + } + + @TaskAction + public void createArgsFile() throws IOException { + ArtifactPathsCollector modulePathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); + + getModules().getAsFileTree().visit(modulePathCollector); + + var replacements = new HashMap(); + replacements.put("@MODULE_PATH@", modulePathCollector.toString()); + replacements.put("@MODULES@", "ALL-MODULE-PATH"); + replacements.put("@IGNORE_LIST@", String.join(",", getIgnoreList().get())); + replacements.put("@PLUGIN_LAYER_LIBRARIES@", ""); + replacements.put("@GAME_LAYER_LIBRARIES@", ""); + replacements.put("@CLASS_PATH@", resolveClasspath()); + replacements.put("@TASK@", "forgeserver"); + replacements.put("@FORGE_VERSION@", getNeoForgeVersion().get()); + replacements.put("@FML_VERSION@", getFmlVersion().get()); + replacements.put("@MC_VERSION@", getMinecraftVersion().get()); + replacements.put("@MCP_VERSION@", getRawNeoFormVersion().get()); + + var contents = Files.readString(getTemplate().get().getAsFile().toPath()); + for (var entry : replacements.entrySet()) { + contents = contents.replaceAll(entry.getKey(), entry.getValue()); + } + Files.writeString(getArgsFile().get().getAsFile().toPath(), contents); + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java new file mode 100644 index 0000000000..c72884654b --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -0,0 +1,204 @@ +package net.neoforged.neodev.installer; + +import com.google.gson.GsonBuilder; +import net.neoforged.moddevgradle.internal.utils.FileUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.jetbrains.annotations.Nullable; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Base64; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +public abstract class CreateInstallerProfile extends DefaultTask { + @Inject + public CreateInstallerProfile() {} + + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getMcAndNeoFormVersion(); + + @InputFile + public abstract RegularFileProperty getIcon(); + + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getLibraries(); + + @Input + public abstract ListProperty getRepositoryURLs(); + + @Input + public abstract MapProperty> getProcessorClasspaths(); + + @Input + public abstract MapProperty getProcessorGavs(); + + @InputFile + public abstract RegularFileProperty getUniversalJar(); + + @OutputFile + public abstract RegularFileProperty getInstallerProfile(); + + private void addProcessor(List processors, @Nullable List sides, InstallerProcessor processor, List args) { + processors.add(new ProcessorEntry(sides, getProcessorGavs().get().get(processor), getProcessorClasspaths().get().get(processor), args)); + } + + @TaskAction + public void createInstallerProfile() throws IOException { + var icon = "data:image/png;base64," + Base64.getEncoder().encodeToString(Files.readAllBytes(getIcon().getAsFile().get().toPath())); + + var data = new LinkedHashMap(); + var neoFormVersion = getMcAndNeoFormVersion().get(); + data.put("MAPPINGS", new LauncherDataEntry(String.format("[net.neoforged:neoform:%s:mappings@txt]", neoFormVersion), String.format("[net.neoforged:neoform:%s:mappings@txt]", neoFormVersion))); + data.put("MOJMAPS", new LauncherDataEntry(String.format("[net.minecraft:client:%s:mappings@txt]", neoFormVersion), String.format("[net.minecraft:server:%s:mappings@txt]", neoFormVersion))); + data.put("MERGED_MAPPINGS", new LauncherDataEntry(String.format("[net.neoforged:neoform:%s:mappings-merged@txt]", neoFormVersion), String.format("[net.neoforged:neoform:%s:mappings-merged@txt]", neoFormVersion))); + data.put("BINPATCH", new LauncherDataEntry("/data/client.lzma", "/data/server.lzma")); + data.put("MC_UNPACKED", new LauncherDataEntry(String.format("[net.minecraft:client:%s:unpacked]", neoFormVersion), String.format("[net.minecraft:server:%s:unpacked]", neoFormVersion))); + data.put("MC_SLIM", new LauncherDataEntry(String.format("[net.minecraft:client:%s:slim]", neoFormVersion), String.format("[net.minecraft:server:%s:slim]", neoFormVersion))); + data.put("MC_EXTRA", new LauncherDataEntry(String.format("[net.minecraft:client:%s:extra]", neoFormVersion), String.format("[net.minecraft:server:%s:extra]", neoFormVersion))); + data.put("MC_SRG", new LauncherDataEntry(String.format("[net.minecraft:client:%s:srg]", neoFormVersion), String.format("[net.minecraft:server:%s:srg]", neoFormVersion))); + data.put("PATCHED", new LauncherDataEntry(String.format("[%s:%s:%s:client]", "net.neoforged", "neoforge", getNeoForgeVersion().get()), String.format("[%s:%s:%s:server]", "net.neoforged", "neoforge", getNeoForgeVersion().get()))); + data.put("MCP_VERSION", new LauncherDataEntry(String.format("'%s'", neoFormVersion), String.format("'%s'", neoFormVersion))); + + var processors = new ArrayList(); + BiConsumer> commonProcessor = (processor, args) -> addProcessor(processors, null, processor, args); + BiConsumer> clientProcessor = (processor, args) -> addProcessor(processors, List.of("client"), processor, args); + BiConsumer> serverProcessor = (processor, args) -> addProcessor(processors, List.of("server"), processor, args); + + serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "EXTRACT_FILES", "--archive", "{INSTALLER}", + + "--from", "data/run.sh", "--to", "{ROOT}/run.sh", "--exec", "{ROOT}/run.sh", + + "--from", "data/run.bat", "--to", "{ROOT}/run.bat", + + "--from", "data/user_jvm_args.txt", "--to", "{ROOT}/user_jvm_args.txt", "--optional", "{ROOT}/user_jvm_args.txt", + + "--from", "data/win_args.txt", "--to", "{ROOT}/libraries/net/neoforged/neoforge/%s/win_args.txt".formatted(getNeoForgeVersion().get()), + + "--from", "data/unix_args.txt", "--to", "{ROOT}/libraries/net/neoforged/neoforge/%s/unix_args.txt".formatted(getNeoForgeVersion().get())) + ); + serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{ROOT}/libraries/", "--libraries") + ); + serverProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "BUNDLER_EXTRACT", "--input", "{MINECRAFT_JAR}", "--output", "{MC_UNPACKED}", "--jar-only") + ); + var neoformDependency = "net.neoforged:neoform:" + getMcAndNeoFormVersion().get() + "@zip";; + commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "MCP_DATA", "--input", String.format("[%s]", neoformDependency), "--output", "{MAPPINGS}", "--key", "mappings") + ); + commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "DOWNLOAD_MOJMAPS", "--version", getMinecraftVersion().get(), "--side", "{SIDE}", "--output", "{MOJMAPS}") + ); + commonProcessor.accept(InstallerProcessor.INSTALLERTOOLS, + List.of("--task", "MERGE_MAPPING", "--left", "{MAPPINGS}", "--right", "{MOJMAPS}", "--output", "{MERGED_MAPPINGS}", "--classes", "--fields", "--methods", "--reverse-right") + ); + clientProcessor.accept(InstallerProcessor.JARSPLITTER, + List.of("--input", "{MINECRAFT_JAR}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}") + ); + serverProcessor.accept(InstallerProcessor.JARSPLITTER, + List.of("--input", "{MC_UNPACKED}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}") + ); + commonProcessor.accept(InstallerProcessor.FART, + List.of("--input", "{MC_SLIM}", "--output", "{MC_SRG}", "--names", "{MERGED_MAPPINGS}", "--ann-fix", "--ids-fix", "--src-fix", "--record-fix") + ); + commonProcessor.accept(InstallerProcessor.BINPATCHER, + List.of("--clean", "{MC_SRG}", "--output", "{PATCHED}", "--apply", "{BINPATCH}") + ); + + getLogger().info("Collecting libraries for Installer Profile"); + var profileFiller = new LibraryCollector(getRepositoryURLs().get()); + getLibraries().getAsFileTree().visit(profileFiller); + var libraries = new ArrayList<>(profileFiller.getLibraries()); + + var universalJar = getUniversalJar().getAsFile().get().toPath(); + libraries.add(new Library( + "net.neoforged:neoforge:%s:universal".formatted(getNeoForgeVersion().get()), + new LibraryDownload(new LibraryArtifact( + LibraryCollector.sha1Hash(universalJar), + Files.size(universalJar), + "https://maven.neoforged.net/releases/net/neoforged/neoforge/%s/neoforge-%s-universal.jar".formatted( + getNeoForgeVersion().get(), + getNeoForgeVersion().get()), + "net/neoforged/neoforge/%s/neoforge-%s-universal.jar".formatted( + getNeoForgeVersion().get(), + getNeoForgeVersion().get()) + )))); + + var profile = new InstallerProfile( + "1", + "NeoForge", + "neoforge-%s".formatted(getNeoForgeVersion().get()), + icon, + getMinecraftVersion().get(), + "/version.json", + "/big_logo.png", + "Welcome to the simple NeoForge installer", + "https://mirrors.neoforged.net", + true, + data, + processors, + libraries, + "{LIBRARY_DIR}/net/minecraft/server/{MINECRAFT_VERSION}/server-{MINECRAFT_VERSION}.jar" + ); + + FileUtils.writeStringSafe( + getInstallerProfile().getAsFile().get().toPath(), + new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile), + // TODO: Not sure what this should be? Most likely the file is ASCII. + StandardCharsets.UTF_8); + } +} + +record InstallerProfile( + String spec, + String profile, + String version, + String icon, + String minecraft, + String json, + String logo, + String welcome, + String mirrorList, + boolean hideExtract, + Map data, + List processors, + List libraries, + String serverJarPath) {} + +record LauncherDataEntry( + String client, + String server) {} + +record ProcessorEntry( + @Nullable + List sides, + String jar, + List classpath, + List args) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java new file mode 100644 index 0000000000..c2389af6a7 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -0,0 +1,128 @@ +package net.neoforged.neodev.installer; + +import com.google.gson.GsonBuilder; +import net.neoforged.moddevgradle.internal.utils.FileUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.IOException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public abstract class CreateLauncherProfile extends DefaultTask { + @Inject + public CreateLauncherProfile() {} + + @Input + public abstract Property getFmlVersion(); + + @Input + public abstract Property getMinecraftVersion(); + + @Input + public abstract Property getNeoForgeVersion(); + + @Input + public abstract Property getRawNeoFormVersion(); + + @InputFiles + @PathSensitive(PathSensitivity.NONE) + public abstract ConfigurableFileCollection getLibraries(); + + @Input + public abstract ListProperty getRepositoryURLs(); + + @Input + public abstract ListProperty getIgnoreList(); + + @InputFiles + @PathSensitive(PathSensitivity.NAME_ONLY) + public abstract ConfigurableFileCollection getModulePath(); + + @OutputFile + public abstract RegularFileProperty getLauncherProfile(); + + @TaskAction + public void createLauncherProfile() throws IOException { + var time = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME); + + getLogger().info("Collecting libraries for Launcher Profile"); + var profileFiller = new LibraryCollector(getRepositoryURLs().get()); + getLibraries().getAsFileTree().visit(profileFiller); + var libraries = profileFiller.getLibraries(); + + var gameArguments = new ArrayList<>(List.of( + "--fml.neoForgeVersion", getNeoForgeVersion().get(), + "--fml.fmlVersion", getFmlVersion().get(), + "--fml.mcVersion", getMinecraftVersion().get(), + "--fml.neoFormVersion", getRawNeoFormVersion().get(), + "--launchTarget", "forgeclient")); + + var jvmArguments = new ArrayList<>(List.of( + "-Djava.net.preferIPv6Addresses=system", + "-DignoreList=" + String.join(",", getIgnoreList().get()), + // TODO: is this still relevant in any way? + "-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar", + "-DlibraryDirectory=${library_directory}")); + + var modulePathCollector = new ArtifactPathsCollector("${classpath_separator}", "${library_directory}/"); + getModulePath().getAsFileTree().visit(modulePathCollector); + jvmArguments.add("-p"); + jvmArguments.add(modulePathCollector.toString()); + + jvmArguments.addAll(List.of( + "--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")); + + var arguments = new LinkedHashMap>(); + arguments.put("game", gameArguments); + arguments.put("jvm", jvmArguments); + + var profile = new LauncherProfile( + "neoforge-%s".formatted(getNeoForgeVersion().get()), + time, + time, + "release", + "cpw.mods.bootstraplauncher.BootstrapLauncher", + getMinecraftVersion().get(), + arguments, + libraries + ); + + FileUtils.writeStringSafe( + getLauncherProfile().getAsFile().get().toPath(), + new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile), + // TODO: Not sure what this should be? Most likely the file is ASCII. + StandardCharsets.UTF_8); + } +} + +record LauncherProfile( + String id, + String time, + String releaseTime, + String type, + String mainClass, + String inheritsFrom, + Map> arguments, + List libraries) {} + diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java new file mode 100644 index 0000000000..e5c5b60b45 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java @@ -0,0 +1,16 @@ +package net.neoforged.neodev.installer; + +import net.neoforged.neodev.Tools; + +public enum InstallerProcessor { + BINPATCHER(Tools.BINPATCHER), + FART(Tools.AUTO_RENAMING_TOOL), + INSTALLERTOOLS(Tools.INSTALLERTOOLS), + JARSPLITTER(Tools.JARSPLITTER); + + public final Tools tool; + + InstallerProcessor(Tools tool) { + this.tool = tool; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java new file mode 100644 index 0000000000..d064dfa4da --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java @@ -0,0 +1,5 @@ +package net.neoforged.neodev.installer; + +record Library( + String name, + LibraryDownload downloads) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java new file mode 100644 index 0000000000..1e345980e9 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryArtifact.java @@ -0,0 +1,7 @@ +package net.neoforged.neodev.installer; + +record LibraryArtifact( + String sha1, + long size, + String url, + String path) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java new file mode 100644 index 0000000000..e9a086c6cb --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java @@ -0,0 +1,220 @@ +package net.neoforged.neodev.installer; + +import org.gradle.api.artifacts.component.ModuleComponentIdentifier; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HexFormat; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.function.Function; + +class LibraryCollector extends ModuleIdentificationVisitor { + private static final Logger LOGGER = Logging.getLogger(LibraryCollector.class); + /** + * Hosts from which we allow the installer to download. + * We whitelist here to avoid redirecting player download traffic to anyone not affiliated with Mojang or us. + */ + private static final List HOST_WHITELIST = List.of( + "minecraft.net", + "neoforged.net", + "mojang.com" + ); + + private static final URI MOJANG_MAVEN = URI.create("https://libraries.minecraft.net"); + private static final URI NEOFORGED_MAVEN = URI.create("https://maven.neoforged.net/releases"); + + private final List repositoryUrls; + + private final List> libraries = new ArrayList<>(); + + private final HttpClient httpClient = HttpClient.newBuilder().build(); + + LibraryCollector(List repoUrl) { + this.repositoryUrls = new ArrayList<>(repoUrl); + + // Only remote repositories make sense (no maven local) + repositoryUrls.removeIf(it -> { + var lowercaseScheme = it.getScheme().toLowerCase(Locale.ROOT); + return !lowercaseScheme.equals("https") && !lowercaseScheme.equals("http"); + }); + // Allow only URLs from whitelisted hosts + repositoryUrls.removeIf(uri -> { + var lowercaseHost = uri.getHost().toLowerCase(Locale.ROOT); + return HOST_WHITELIST.stream().noneMatch(it -> lowercaseHost.equals(it) || lowercaseHost.endsWith("." + it)); + }); + // Always try Mojang Maven first, then our installer Maven + repositoryUrls.removeIf(it -> it.getHost().equals(MOJANG_MAVEN.getHost())); + repositoryUrls.removeIf(it -> it.getHost().equals(NEOFORGED_MAVEN.getHost()) && it.getPath().startsWith(NEOFORGED_MAVEN.getPath())); + repositoryUrls.add(0, NEOFORGED_MAVEN); + repositoryUrls.add(0, MOJANG_MAVEN); + + LOGGER.info("Collecting libraries from:"); + for (var repo : repositoryUrls) { + LOGGER.info(" - " + repo); + } + } + + void visit(ResolvedArtifactResult artifactResult) throws IOException { + var componentId = artifactResult.getId().getComponentIdentifier(); + if (componentId instanceof ModuleComponentIdentifier moduleComponentId) { + visitModule( + artifactResult.getFile(), + moduleComponentId.getGroup(), + moduleComponentId.getModule(), + moduleComponentId.getVersion(), + guessMavenClassifier(artifactResult.getFile(), moduleComponentId), + getExtension(artifactResult.getFile().getName()) + ); + } else { + LOGGER.warn("Cannot handle component: " + componentId); + } + } + + private static String guessMavenClassifier(File file, ModuleComponentIdentifier id) { + var artifact = id.getModule(); + var version = id.getVersion(); + var expectedBasename = artifact + "-" + version; + var filename = file.getName(); + var startOfExt = filename.lastIndexOf('.'); + if (startOfExt != -1) { + filename = filename.substring(0, startOfExt); + } + + if (filename.startsWith(expectedBasename + "-")) { + return filename.substring((expectedBasename + "-").length()); + } + return ""; + } + + /** + * The filename includes the period. + */ + private static String getExtension(String path) { + var lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); + + var potentialExtension = path.lastIndexOf('.'); + if (potentialExtension > lastSep) { + // Check for a double extension like .tar.gz heuristically + var doubleExtensionStart = path.lastIndexOf('.', potentialExtension - 1); + // We only allow 3 chars maximum for the double extension + if (doubleExtensionStart > lastSep && potentialExtension - doubleExtensionStart <= 4) { + return path.substring(doubleExtensionStart); + } + + return path.substring(potentialExtension); + } else { + return ""; + } + } + + @Override + protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) throws IOException { + final String name = group + ":" + module + ":" + version + (classifier.isEmpty() ? "" : ":" + classifier) + "@" + extension; + final String path = group.replace(".", "/") + "/" + module + "/" + version + "/" + module + "-" + version + (classifier.isEmpty() ? "" : "-" + classifier) + "." + extension; + + var sha1 = sha1Hash(file.toPath()); + var fileSize = Files.size(file.toPath()); + + // Try each configured repository in-order to find the file + CompletableFuture libraryFuture = null; + for (var repositoryUrl : repositoryUrls) { + var artifactUri = joinUris(repositoryUrl, path); + var request = HttpRequest.newBuilder(artifactUri) + .method("HEAD", HttpRequest.BodyPublishers.noBody()) + .build(); + + Function> makeRequest = (String previousError) -> { + return httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()) + .thenApply(response -> { + if (response.statusCode() != 200) { + LOGGER.info(" Got %d for %s".formatted(response.statusCode(), artifactUri)); + String message = "Could not find %s: %d".formatted(artifactUri, response.statusCode()); + // Prepend error message from previous repo if they all fail + if (previousError != null) { + message = previousError + "\n" + message; + } + throw new RuntimeException(message); + } + LOGGER.info(" Found $name -> $artifactUri"); + return new Library( + name, + new LibraryDownload(new LibraryArtifact( + sha1, + fileSize, + artifactUri.toString(), + path))); + }); + }; + + if (libraryFuture == null) { + libraryFuture = makeRequest.apply(null); + } else { + libraryFuture = libraryFuture.exceptionallyCompose(error -> { + return makeRequest.apply(error.getMessage()); + }); + } + } + + libraries.add(libraryFuture); + } + + static String sha1Hash(Path path) throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + + try (var in = Files.newInputStream(path); + var din = new DigestInputStream(in, digest)) { + byte[] buffer = new byte[8192]; + while (din.read(buffer) != -1) { + } + } + + return HexFormat.of().formatHex(digest.digest()); + } + + private static URI joinUris(URI repositoryUrl, String path) { + var baseUrl = repositoryUrl.toString(); + if (baseUrl.endsWith("/") && path.startsWith("/")) { + while (path.startsWith("/")) { + path = path.substring(1); + } + return URI.create(baseUrl + path); + } else if (!baseUrl.endsWith("/") && !path.startsWith("/")) { + return URI.create(baseUrl + "/" + path); + } else { + return URI.create(baseUrl + path); + } + } + + List getLibraries() { + var result = libraries.stream().map(future -> { + try { + return future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).toList(); + LOGGER.info("Collected %d libraries".formatted(result.size())); + return result; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java new file mode 100644 index 0000000000..38afe5d057 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryDownload.java @@ -0,0 +1,4 @@ +package net.neoforged.neodev.installer; + +record LibraryDownload( + LibraryArtifact artifact) {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java new file mode 100644 index 0000000000..9682244dab --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java @@ -0,0 +1,51 @@ +package net.neoforged.neodev.installer; + +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.FileVisitor; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// TODO: simplify this class and subclasses if possible +abstract class ModuleIdentificationVisitor implements FileVisitor { + + // The following regex detects file path patterns, in gradle cache format. Like: /net.neoforged.fancymodloader/earlydisplay/47.1.47/46509b19504a71e25b115383e900aade5088598a/earlydisplay-47.1.47.jar + private static final Pattern GRADLE_CACHE_PATTERN = Pattern.compile("/(?[^/]+)/(?[^/]+)/(?[^/]+)/(?[a-z0-9]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); + + // The following regex detects file path patterns, in maven local cache format. Like: /.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar + private static final Pattern MAVEN_LOCAL_PATTERN = Pattern.compile("/.m2/repository/(?.+)/(?[^/]+)/(?[^/]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); + + @Override + public void visitDir(FileVisitDetails dirDetails) { + //Noop + } + + @Override + public void visitFile(FileVisitDetails fileDetails) { + File file = fileDetails.getFile(); + String absolutePath = file.getAbsolutePath().replace("\\", "/"); + + Matcher matcher = GRADLE_CACHE_PATTERN.matcher(absolutePath); + if (!matcher.find()) { + matcher = MAVEN_LOCAL_PATTERN.matcher(absolutePath); + if (!matcher.find()) { + throw new IllegalStateException("Cannot determine the GAV of " + file + ", since it is neither a remote nor a Maven local dependency!"); + } + } + + String group = matcher.group("group").replace("/", "."); //In case we match the maven way. + String module = matcher.group("module"); + String version = matcher.group("version"); + String classifier = matcher.group("classifier") == null ? "" : matcher.group("classifier"); + String extension = matcher.group("extension"); + + try { + visitModule(file, group, module, version, classifier, extension); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected abstract void visitModule(File file, String group, String module, String version, String classifier, String extension) throws Exception; +} diff --git a/gradle.properties b/gradle.properties index ee9ba5e243..56f42ae36e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,11 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=true org.gradle.parallel=true org.gradle.caching=true -org.gradle.configuration-cache=false +org.gradle.configuration-cache=true org.gradle.debug=false +org.gradle.warning.mode=fail + +moddevgradle_plugin_version=2.0.40-beta java_version=21 @@ -14,6 +17,12 @@ neoform_version=20241023.131943 # on snapshot versions, used to prefix the version neoforge_snapshot_next_stable=21.4 +jst_version=1.0.45 +legacyinstaller_version=3.0.27 +art_version=2.0.3 +installertools_version=2.1.2 +devlaunch_version=1.0.1 + mergetool_version=2.0.0 accesstransformers_version=11.0.1 coremods_version=6.0.4 @@ -22,7 +31,6 @@ modlauncher_version=11.0.4 securejarhandler_version=3.0.8 bootstraplauncher_version=2.0.2 asm_version=9.7 -installer_version=2.1.+ mixin_version=0.15.2+mixin.0.8.7 terminalconsoleappender_version=1.3.0 nightconfig_version=3.8.0 @@ -48,7 +56,3 @@ vintage_engine_version=5.+ assertj_core=3.25.1 neogradle.runtime.platform.installer.debug=true -# We want to be able to have a junit run disconnected from the test and main sourcesets -neogradle.subsystems.conventions.sourcesets.automatic-inclusion=false -neogradle.subsystems.conventions.enabled=false -neogradle.subsystems.tools.jst=net.neoforged.jst:jst-cli-bundle:1.0.45 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9355b41557..0aaefbcaf0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/projects/base/build.gradle b/projects/base/build.gradle index 7a78b9aa79..1a1f878f9a 100644 --- a/projects/base/build.gradle +++ b/projects/base/build.gradle @@ -1,3 +1,15 @@ -dynamicProject { - neoform("${project.minecraft_version}-${project.neoform_version}") + +plugins { + id 'java-library' +} + +apply plugin: net.neoforged.neodev.NeoDevBasePlugin + +dependencies { + implementation("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { + capabilities { + requireCapability 'net.neoforged:neoform-dependencies' + } + endorseStrictVersions() + } } diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index bc642bd0c9..ea85103b01 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -1,3 +1,4 @@ + import net.neoforged.jarcompatibilitychecker.gradle.JCCPlugin import net.neoforged.jarcompatibilitychecker.gradle.CompatibilityTask import net.neoforged.jarcompatibilitychecker.gradle.ProvideNeoForgeJarTask @@ -10,6 +11,8 @@ plugins { id 'neoforge.versioning' } +apply plugin : net.neoforged.neodev.NeoDevPlugin + gradleutils.setupSigning(project: project, signAllPublications: true) changelog { @@ -17,67 +20,81 @@ changelog { disableAutomaticPublicationRegistration() } -dynamicProject { - runtime("${project.minecraft_version}-${project.neoform_version}", - rootProject.layout.projectDirectory.dir('patches'), - rootProject.layout.projectDirectory.dir('rejects')) +sourceSets { + main { + java { + srcDirs rootProject.file('src/main/java') + } + resources { + srcDirs rootProject.file('src/main/resources'), rootProject.file('src/generated/resources') + } + } } +// TODO re-enable final checkVersion = JCCPlugin.providePreviousVersion( project.providers, project.providers.provider({['https://maven.neoforged.net/releases']}), project.providers.provider({'net.neoforged:neoforge'}), project.provider { project.version }.map { ver -> CompatibilityTask.VersionComponentTest.MINOR.predicate(ver) } ) -final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { - // Use the same jar that the patches were generated against - cleanJar.set(tasks.generateClientBinaryPatches.clean) - maven.set('https://maven.neoforged.net/releases') - artifact.set('net.neoforged:neoforge') - version.set(checkVersion) - javaLauncher = javaToolchains.launcherFor { - languageVersion = JavaLanguageVersion.of(java_version) +//final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { +// // Use the same jar that the patches were generated against +// cleanJar.set(tasks.generateClientBinaryPatches.clean) +// maven.set('https://maven.neoforged.net/releases') +// artifact.set('net.neoforged:neoforge') +// version.set(checkVersion) +// javaLauncher = javaToolchains.launcherFor { +// languageVersion = JavaLanguageVersion.of(java_version) +// } +//} +//checkJarCompatibility { +// isAPI = true +// baseJar = createCompatJar.flatMap { it.output } +//} +// +//tasks.configureEach { tsk -> +// if (tsk.name == 'neoFormApplyUserAccessTransformer' && project.hasProperty('validateAccessTransformers')) { +// tsk.inputs.property('validation', 'error') +// tsk.logLevel('ERROR') +// tsk.doFirst { +// tsk.getRuntimeProgramArguments().addAll(tsk.getRuntimeProgramArguments().get()) +// tsk.getRuntimeProgramArguments().add('--access-transformer-validation=error') +// } +// } +//} + +neoDev { + mods { + minecraft { + sourceSet sourceSets.main + } } } -checkJarCompatibility { - isAPI = true - baseJar = createCompatJar.flatMap { it.output } -} - -installerProfile { - profile = 'NeoForge' -} - -minecraft { - // FML looks for this mod id to find the minecraft classes - modIdentifier 'minecraft' - accessTransformers { - file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') +configurations { + installer { + // TODO: shouldn't have both on + canBeResolved = true + canBeConsumed = true } -} - -tasks.configureEach { tsk -> - if (tsk.name == 'neoFormApplyUserAccessTransformer' && project.hasProperty('validateAccessTransformers')) { - tsk.inputs.property('validation', 'error') - tsk.logLevel('ERROR') - tsk.doFirst { - tsk.getRuntimeProgramArguments().addAll(tsk.getRuntimeProgramArguments().get()) - tsk.getRuntimeProgramArguments().add('--access-transformer-validation=error') - } + installerLibraries { + extendsFrom installer } + moduleOnly { + // TODO: shouldn't have both on + canBeResolved = true + canBeConsumed = true + } + implementation.extendsFrom installer } -sourceSets { - main { - java { - srcDirs rootProject.file('src/main/java') - } - resources { - srcDirs rootProject.file('src/main/resources'), rootProject.file('src/generated/resources') +dependencies { + implementation("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { + capabilities { + requireCapability 'net.neoforged:neoform-dependencies' } + endorseStrictVersions() } -} -dependencies { runtimeOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" moduleOnly "cpw.mods:securejarhandler:${project.securejarhandler_version}" @@ -132,124 +149,41 @@ dependencies { installer "org.apache.logging.log4j:log4j-api:${project.log4j_version}" installer "org.apache.logging.log4j:log4j-core:${project.log4j_version}" + installerLibraries "net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}@zip" + compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}" - userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}"), { - jarJar.ranged(it, "[${project.mixin_extras_version},)") - } + userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}") + // TODO: duplicated with the text fixtures userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") compileOnly(jarJar(project(":neoforge-coremods"))) } -runTypes { - client { - singleInstance false - client true - - arguments.addAll '--fml.neoForgeVersion', project.version - arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version - arguments.addAll '--fml.mcVersion', project.minecraft_version - arguments.addAll '--fml.neoFormVersion', project.neoform_version - } - - server { - server true - - arguments.addAll '--fml.neoForgeVersion', project.version - arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version - arguments.addAll '--fml.mcVersion', project.minecraft_version - arguments.addAll '--fml.neoFormVersion', project.neoform_version - } - - gameTestServer { - from project.runTypes.server - - gameTest true - } - - gameTestClient { - from project.runTypes.client - - gameTest true - } - - data { - dataGenerator true - - // Don't set modid here so we can reuse this runType for test datagen - arguments.addAll '--fml.neoForgeVersion', project.version - arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version - arguments.addAll '--fml.mcVersion', project.minecraft_version - arguments.addAll '--fml.neoFormVersion', project.neoform_version - } - - junit { - junit true - arguments.addAll '--fml.neoForgeVersion', project.version - arguments.addAll '--fml.fmlVersion', project.fancy_mod_loader_version - arguments.addAll '--fml.mcVersion', project.minecraft_version - arguments.addAll '--fml.neoFormVersion', project.neoform_version - } -} - -runs { - client { } - server { } - gameTestServer { } - gameTestClient { } - data { - arguments.addAll '--mod', 'neoforge' - - modSources.add project.sourceSets.main - - idea { - primarySourceSet project.sourceSets.main +neoDev { + runs { + configureEach { + gameDirectory.set project.file("run/${it.name}") as File + modSources.add project(":neoforge-coremods").sourceSets.main + } + client { + client() + } + server { + server() + } + gameTestServer { + type = "gameTestServer" + } + data { + data() + programArguments.addAll '--mod', 'neoforge', '--flat', '--all', '--validate', + '--existing', rootProject.file("src/main/resources").absolutePath, + '--output', rootProject.file("src/generated/resources").absolutePath } } } -runs.configureEach { it -> - modSources.add project(":neoforge-coremods").sourceSets.main - - final File gameDir = project.file("run/${it.name}") as File - gameDir.mkdirs(); - - it.workingDirectory.set gameDir - it.arguments.addAll '--gameDir', gameDir.absolutePath -} - -tasks.register("genPatches") { - dependsOn tasks.unpackSourcePatches -} - -launcherProfile { - arguments { - game '--fml.neoForgeVersion' - game project.version - game '--fml.fmlVersion' - game project.fancy_mod_loader_version - game '--fml.mcVersion' - game project.minecraft_version - game '--fml.neoFormVersion' - game project.neoform_version - } -} - -userdevProfile { - runTypes.configureEach { - argument '--fml.neoForgeVersion' - argument project.version - argument '--fml.fmlVersion' - argument project.fancy_mod_loader_version - argument '--fml.mcVersion' - argument project.minecraft_version - argument '--fml.neoFormVersion' - argument project.neoform_version - } - additionalTestDependencyArtifactCoordinate "net.neoforged:testframework:${project.version}" -} - tasks.withType(Javadoc.class).configureEach { options.tags = [ 'apiNote:a:API Note:', @@ -359,9 +293,7 @@ configurations { modDevApiElements { canBeResolved = false canBeConsumed = true - afterEvaluate { - extendsFrom userdevCompileOnly, installerLibraries, moduleOnly - } + extendsFrom userdevCompileOnly, installerLibraries, moduleOnly attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -444,38 +376,35 @@ dependencies { processResources { inputs.property("version", project.version) + final version = project.version filesMatching("META-INF/neoforge.mods.toml") { expand([ "global": [ - "neoForgeVersion": project.version + "neoForgeVersion": version ] ]) } } -afterEvaluate { - artifacts { - modDevBundle(userdevJar) { - setClassifier("userdev") // Legacy - } - modDevConfig(createUserdevJson.output) { - builtBy(createUserdevJson) - setClassifier("moddev-config") - } - universalJar(signUniversalJar.output) { - builtBy(signUniversalJar) - setClassifier("universal") - } - installerJar(signInstallerJar.output) { - builtBy(signInstallerJar) - setClassifier("installer") - } - changelog(createChangelog.outputFile) { - builtBy(createChangelog) - setClassifier("changelog") - setExtension("txt") - } - } +artifacts { + modDevBundle(userdevJar) { + setClassifier("userdev") // Legacy + } + modDevConfig(writeUserDevConfig.userDevConfig) { + setClassifier("moddev-config") + } + universalJar(universalJar) { + setClassifier("universal") + } +// installerJar(signInstallerJar.output) { +// builtBy(signInstallerJar) +// setClassifier("installer") +// } +// changelog(createChangelog.outputFile) { +// builtBy(createChangelog) +// setClassifier("changelog") +// setExtension("txt") +// } } publishing { diff --git a/settings.gradle b/settings.gradle index 2e8f7b6541..e8b5e56ed4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,13 +1,26 @@ pluginManagement { repositories { gradlePluginPortal() - mavenLocal() maven { url = 'https://maven.neoforged.net/releases' } + mavenLocal() } } plugins { - id 'net.neoforged.gradle.platform' version '7.0.171' + id 'net.neoforged.moddev.repositories' version "${moddevgradle_plugin_version}" + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' +} + +// This makes the version available to buildSrc +gradle.ext.moddevgradle_plugin_version = moddevgradle_plugin_version + +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + rulesMode = RulesMode.FAIL_ON_PROJECT_RULES + repositories { + mavenCentral() + mavenLocal() + } } if (rootProject.name.toLowerCase() == "neoforge") { @@ -15,13 +28,10 @@ if (rootProject.name.toLowerCase() == "neoforge") { rootProject.name = "NeoForge-Root" } -dynamicProjects { - include ':base' - include ':neoforge' - - project(":base").projectDir = file("projects/base") - project(":neoforge").projectDir = file("projects/neoforge") -} +include ':base' +project(':base').projectDir = file('projects/base') +include ':neoforge' +project(':neoforge').projectDir = file('projects/neoforge') include ':tests' project(":tests").projectDir = file("tests") diff --git a/testframework/build.gradle b/testframework/build.gradle index 91a01c1c76..d5d98b4059 100644 --- a/testframework/build.gradle +++ b/testframework/build.gradle @@ -3,25 +3,19 @@ plugins { id 'maven-publish' id 'com.diffplug.spotless' id 'net.neoforged.licenser' - id 'net.neoforged.gradle.platform' id 'neoforge.formatting-conventions' } java.withSourcesJar() -repositories { - maven { - name 'Mojang' - url 'https://libraries.minecraft.net' - } - maven { - name 'NeoForged' - url 'https://maven.neoforged.net/releases' - } -} +apply plugin : net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin dependencies { - implementation project(path: ':neoforge', configuration: 'runtimeElements') + // TODO: is this leaking in the POM? (most likely yes) + // TODO: does this need to be changed back to runtimeDependencies? + // TODO: should use attributes to resolve the right variant instead of hardcoding + compileOnly project(path: ':neoforge', configuration: 'apiElements') + runtimeOnly project(path: ':neoforge', configuration: 'runtimeElements') compileOnly(platform("org.junit:junit-bom:${project.jupiter_api_version}")) compileOnly "org.junit.jupiter:junit-jupiter-params" @@ -30,6 +24,14 @@ dependencies { compileOnly "com.google.code.findbugs:jsr305:3.0.2" } +sourceSets { + main { + // TODO: cursed + compileClasspath += project(':neoforge').sourceSets.main.compileClasspath + runtimeClasspath += project(':neoforge').sourceSets.main.runtimeClasspath + } +} + license { header = rootProject.file('codeformat/HEADER.txt') include '**/*.java' @@ -39,6 +41,7 @@ tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } +def version = project.version tasks.withType(ProcessResources).configureEach { inputs.properties version: version diff --git a/tests/build.gradle b/tests/build.gradle index 2da20a4e40..9551414af0 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -1,38 +1,27 @@ + plugins { id 'java' - id 'net.neoforged.gradle.platform' id 'com.diffplug.spotless' id 'net.neoforged.licenser' id 'neoforge.formatting-conventions' } +apply plugin : net.neoforged.neodev.NeoDevExtraPlugin + +evaluationDependsOn(":neoforge") + def neoforgeProject = project(':neoforge') def testframeworkProject = project(':testframework') def coremodsProject = project(':neoforge-coremods') -repositories { - mavenLocal() - maven { - name 'Mojang' - url 'https://libraries.minecraft.net' - } - maven { - name 'NeoForged' - url 'https://maven.neoforged.net/releases' - } -} - sourceSets { main { resources { srcDir file('src/generated/resources') } } - junit {} -} - -configurations { - junitImplementation.extendsFrom(implementation) + junit { + } } dependencies { @@ -45,74 +34,81 @@ dependencies { junitImplementation("org.assertj:assertj-core:${project.assertj_core}") junitImplementation "net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}" + junitImplementation project(path: ':neoforge', configuration: 'runtimeElements') + junitImplementation(testframeworkProject) compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}" } +// Required to fix compile classpath using an older junit version. Alternatively we could also just bump junit. +configurations.junitCompileClasspath.shouldResolveConsistentlyWith configurations.junitRuntimeClasspath -runs { - client { - configure neoforgeProject.runTypes.client - } - junit { - configure neoforgeProject.runTypes.junit - unitTestSource sourceSets.junit - } - server { - configure neoforgeProject.runTypes.server - } - gameTestServer { - configure neoforgeProject.runTypes.gameTestServer - } - gameTestClient { - configure neoforgeProject.runTypes.gameTestClient +junitTest { + useJUnitPlatform() + classpath = sourceSets.junit.output + sourceSets.junit.runtimeClasspath + testClassesDirs = sourceSets.junit.output.classesDirs + outputs.upToDateWhen { false } +} + +neoDev { + mods { + neotests { + sourceSet sourceSets.main + } + testframework { + sourceSet project(":testframework").sourceSets.main + } + junit { + sourceSet sourceSets.junit + } + coremods { + sourceSet coremodsProject.sourceSets.main + } } - data { - configure neoforgeProject.runTypes.data - - arguments.addAll '--flat', '--all', '--validate', - '--mod', 'data_gen_test', - '--mod', 'global_loot_test', - '--mod', 'scaffolding_test', - '--mod', 'custom_tag_types_test', - '--mod', 'new_model_loader_test', - '--mod', 'remove_tag_datagen_test', - '--mod', 'tag_based_tool_types', - '--mod', 'custom_transformtype_test', - '--mod', 'data_pack_registries_test', - '--mod', 'biome_modifiers_test', - '--mod', 'structure_modifiers_test', - '--mod', 'custom_preset_editor_test', - '--mod', 'custom_predicate_test', - '--mod', 'neotests', - '--existing-mod', 'testframework', - '--existing', sourceSets.main.resources.srcDirs[0].absolutePath - - final File gameDir = project.file("runs/${name}") as File - gameDir.mkdirs(); - - workingDirectory.set gameDir - arguments.addAll '--gameDir', gameDir.absolutePath + + runs { + client { + client() + } + server { + server() + } + gameTestServer { + type = "gameTestServer" + } + data { + data() + + programArguments.addAll '--flat', '--all', '--validate', + '--mod', 'data_gen_test', + '--mod', 'global_loot_test', + '--mod', 'scaffolding_test', + '--mod', 'custom_tag_types_test', + '--mod', 'new_model_loader_test', + '--mod', 'remove_tag_datagen_test', + '--mod', 'tag_based_tool_types', + '--mod', 'custom_transformtype_test', + '--mod', 'data_pack_registries_test', + '--mod', 'biome_modifiers_test', + '--mod', 'structure_modifiers_test', + '--mod', 'custom_preset_editor_test', + '--mod', 'custom_predicate_test', + '--mod', 'neotests', + '--existing-mod', 'testframework', + '--existing', project.file("src/main/resources").absolutePath, + '--output', project.file("src/generated/resources").absolutePath + } } -} -//We need the assets and natives tasks from the forge project. -runs.configureEach { - dependsOn.add(neoforgeProject.runtime.assets) - dependsOn.add(neoforgeProject.runtime.natives) - modSource neoforgeProject.sourceSets.main - modSource coremodsProject.sourceSets.main - modSource testframeworkProject.sourceSets.main -} + runs.configureEach { + // Add NeoForge and Minecraft (both under the "minecraft" mod), and exclude junit. + loadedMods = [neoforgeProject.neoDev.mods.minecraft, mods.neotests, mods.testframework, mods.coremods] -afterEvaluate { - runs.data { - // Override --output that forge already has - def args = new ArrayList(arguments.get()); - def outputIndex = args.indexOf('--output'); - args.set(outputIndex+1, file('src/generated/resources/').absolutePath); - arguments.set(args); + gameDirectory.set project.file("runs/${it.name}") as File } - runs.junit.modSources.all().get().values().remove(sourceSets.main) +} + +neoDevTest { + loadedMods = [ project(":neoforge").neoDev.mods.minecraft, neoDev.mods.testframework, neoDev.mods.junit ] } license { From 08d5a5f7bbf06fc14f136eed1903797210d538bd Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:30:40 +0200 Subject: [PATCH 02/39] Directly add dependency --- buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 36f720a2d3..3116abd643 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -71,9 +71,7 @@ public void apply(Project project) { var jstConfiguration = configurations.create("javaSourceTransformer", files -> { files.setCanBeConsumed(false); files.setCanBeResolved(true); - files.defaultDependencies(spec -> { - spec.add(Tools.JST.asDependency(project)); - }); + files.getDependencies().add(Tools.JST.asDependency(project)); }); var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); From 962296ea0233660ec93846615c3f7ca0f1fd38a3 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 21 Oct 2024 00:46:29 +0200 Subject: [PATCH 03/39] Simplify ModuleIdentificationVisitor a little --- .../installer/ArtifactPathsCollector.java | 5 +- .../neodev/installer/CreateArgsFile.java | 4 +- .../installer/CreateInstallerProfile.java | 2 +- .../installer/CreateLauncherProfile.java | 4 +- .../neodev/installer/LibraryCollector.java | 58 +------------------ .../ModuleIdentificationVisitor.java | 43 ++++++-------- 6 files changed, 29 insertions(+), 87 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java index 59aa0b69a8..d3cb6fafc3 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java @@ -2,6 +2,9 @@ import java.io.File; +/** + * Turns a collection of files into a classpath description, with libraries stored under a {@code prefix} folder following Maven structure. + */ class ArtifactPathsCollector extends ModuleIdentificationVisitor { private final StringBuilder builder = new StringBuilder(); @@ -14,7 +17,7 @@ public ArtifactPathsCollector(String separator, String prefix) { } @Override - protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) throws Exception { + protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) { builder.append(prefix); builder.append(group.replace(".", "/")); builder.append("/"); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java index 159484286c..977328da6f 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java @@ -67,7 +67,7 @@ public CreateArgsFile() {} private String resolveClasspath() throws IOException { ArtifactPathsCollector classpathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); - getClasspath().getAsFileTree().visit(classpathCollector); + classpathCollector.visitFiles(getClasspath()); var ourClasspath = classpathCollector + getPathSeparator().get() + "libraries/net/minecraft/server/%s/server-%s-extra.jar".formatted( @@ -104,7 +104,7 @@ private static String stripVersionSuffix(String classpathEntry) { public void createArgsFile() throws IOException { ArtifactPathsCollector modulePathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); - getModules().getAsFileTree().visit(modulePathCollector); + modulePathCollector.visitFiles(getModules()); var replacements = new HashMap(); replacements.put("@MODULE_PATH@", modulePathCollector.toString()); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index c72884654b..75b2ff1eb5 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -134,7 +134,7 @@ public void createInstallerProfile() throws IOException { getLogger().info("Collecting libraries for Installer Profile"); var profileFiller = new LibraryCollector(getRepositoryURLs().get()); - getLibraries().getAsFileTree().visit(profileFiller); + profileFiller.visitFiles(getLibraries()); var libraries = new ArrayList<>(profileFiller.getLibraries()); var universalJar = getUniversalJar().getAsFile().get().toPath(); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index c2389af6a7..dc03cf5612 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -64,7 +64,7 @@ public void createLauncherProfile() throws IOException { getLogger().info("Collecting libraries for Launcher Profile"); var profileFiller = new LibraryCollector(getRepositoryURLs().get()); - getLibraries().getAsFileTree().visit(profileFiller); + profileFiller.visitFiles(getLibraries()); var libraries = profileFiller.getLibraries(); var gameArguments = new ArrayList<>(List.of( @@ -82,7 +82,7 @@ public void createLauncherProfile() throws IOException { "-DlibraryDirectory=${library_directory}")); var modulePathCollector = new ArtifactPathsCollector("${classpath_separator}", "${library_directory}/"); - getModulePath().getAsFileTree().visit(modulePathCollector); + modulePathCollector.visitFiles(getModulePath()); jvmArguments.add("-p"); jvmArguments.add(modulePathCollector.toString()); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java index e9a086c6cb..74ad54c574 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java @@ -1,7 +1,5 @@ package net.neoforged.neodev.installer; -import org.gradle.api.artifacts.component.ModuleComponentIdentifier; -import org.gradle.api.artifacts.result.ResolvedArtifactResult; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; @@ -24,6 +22,9 @@ import java.util.concurrent.Future; import java.util.function.Function; +/** + * For each file in a collection, finds the repository that the file came from. + */ class LibraryCollector extends ModuleIdentificationVisitor { private static final Logger LOGGER = Logging.getLogger(LibraryCollector.class); /** @@ -70,59 +71,6 @@ class LibraryCollector extends ModuleIdentificationVisitor { } } - void visit(ResolvedArtifactResult artifactResult) throws IOException { - var componentId = artifactResult.getId().getComponentIdentifier(); - if (componentId instanceof ModuleComponentIdentifier moduleComponentId) { - visitModule( - artifactResult.getFile(), - moduleComponentId.getGroup(), - moduleComponentId.getModule(), - moduleComponentId.getVersion(), - guessMavenClassifier(artifactResult.getFile(), moduleComponentId), - getExtension(artifactResult.getFile().getName()) - ); - } else { - LOGGER.warn("Cannot handle component: " + componentId); - } - } - - private static String guessMavenClassifier(File file, ModuleComponentIdentifier id) { - var artifact = id.getModule(); - var version = id.getVersion(); - var expectedBasename = artifact + "-" + version; - var filename = file.getName(); - var startOfExt = filename.lastIndexOf('.'); - if (startOfExt != -1) { - filename = filename.substring(0, startOfExt); - } - - if (filename.startsWith(expectedBasename + "-")) { - return filename.substring((expectedBasename + "-").length()); - } - return ""; - } - - /** - * The filename includes the period. - */ - private static String getExtension(String path) { - var lastSep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); - - var potentialExtension = path.lastIndexOf('.'); - if (potentialExtension > lastSep) { - // Check for a double extension like .tar.gz heuristically - var doubleExtensionStart = path.lastIndexOf('.', potentialExtension - 1); - // We only allow 3 chars maximum for the double extension - if (doubleExtensionStart > lastSep && potentialExtension - doubleExtensionStart <= 4) { - return path.substring(doubleExtensionStart); - } - - return path.substring(potentialExtension); - } else { - return ""; - } - } - @Override protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) throws IOException { final String name = group + ":" + module + ":" + version + (classifier.isEmpty() ? "" : ":" + classifier) + "@" + extension; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java index 9682244dab..9d7cf79678 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java @@ -1,14 +1,14 @@ package net.neoforged.neodev.installer; -import org.gradle.api.file.FileVisitDetails; -import org.gradle.api.file.FileVisitor; +import org.gradle.api.file.FileCollection; import java.io.File; +import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; // TODO: simplify this class and subclasses if possible -abstract class ModuleIdentificationVisitor implements FileVisitor { +abstract class ModuleIdentificationVisitor { // The following regex detects file path patterns, in gradle cache format. Like: /net.neoforged.fancymodloader/earlydisplay/47.1.47/46509b19504a71e25b115383e900aade5088598a/earlydisplay-47.1.47.jar private static final Pattern GRADLE_CACHE_PATTERN = Pattern.compile("/(?[^/]+)/(?[^/]+)/(?[^/]+)/(?[a-z0-9]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); @@ -16,36 +16,27 @@ abstract class ModuleIdentificationVisitor implements FileVisitor { // The following regex detects file path patterns, in maven local cache format. Like: /.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar private static final Pattern MAVEN_LOCAL_PATTERN = Pattern.compile("/.m2/repository/(?.+)/(?[^/]+)/(?[^/]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); - @Override - public void visitDir(FileVisitDetails dirDetails) { - //Noop - } - - @Override - public void visitFile(FileVisitDetails fileDetails) { - File file = fileDetails.getFile(); - String absolutePath = file.getAbsolutePath().replace("\\", "/"); + final void visitFiles(FileCollection files) throws IOException { + for (var file : files) { + String absolutePath = file.getAbsolutePath().replace("\\", "/"); - Matcher matcher = GRADLE_CACHE_PATTERN.matcher(absolutePath); - if (!matcher.find()) { - matcher = MAVEN_LOCAL_PATTERN.matcher(absolutePath); + Matcher matcher = GRADLE_CACHE_PATTERN.matcher(absolutePath); if (!matcher.find()) { - throw new IllegalStateException("Cannot determine the GAV of " + file + ", since it is neither a remote nor a Maven local dependency!"); + matcher = MAVEN_LOCAL_PATTERN.matcher(absolutePath); + if (!matcher.find()) { + throw new IllegalStateException("Cannot determine the GAV of " + file + ", since it is neither a remote nor a Maven local dependency!"); + } } - } - String group = matcher.group("group").replace("/", "."); //In case we match the maven way. - String module = matcher.group("module"); - String version = matcher.group("version"); - String classifier = matcher.group("classifier") == null ? "" : matcher.group("classifier"); - String extension = matcher.group("extension"); + String group = matcher.group("group").replace("/", "."); //In case we match the maven way. + String module = matcher.group("module"); + String version = matcher.group("version"); + String classifier = matcher.group("classifier") == null ? "" : matcher.group("classifier"); + String extension = matcher.group("extension"); - try { visitModule(file, group, module, version, classifier, extension); - } catch (Exception e) { - throw new RuntimeException(e); } } - protected abstract void visitModule(File file, String group, String module, String version, String classifier, String extension) throws Exception; + protected abstract void visitModule(File file, String group, String module, String version, String classifier, String extension) throws IOException; } From a9dfc941286d0b53eb36155d840e1131cd676a0b Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:55:06 +0200 Subject: [PATCH 04/39] Fix gradle sync --- .../net/neoforged/neodev/NeoDevExtraPlugin.java | 6 +----- coremods/build.gradle | 9 --------- coremods/settings.gradle | 8 ++++++++ projects/neoforge/build.gradle | 16 ++++++++++++---- 4 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 coremods/settings.gradle diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java index fabb8f75ac..883d0b29d7 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -43,11 +43,7 @@ public void apply(Project project) { var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", WriteUserDevConfig.class); Consumer configureLegacyClasspath = spec -> { - spec.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> { - caps.requireCapability("net.neoforged:neoform-dependencies"); - }))); - spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "installer")); - spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "moduleOnly")); + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "modDevRuntimeElements")); spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "userdevCompileOnly")); // TODO: Convert into a cross-project dependency too spec.getDependencies().add( diff --git a/coremods/build.gradle b/coremods/build.gradle index 3d942a4701..127e7ec9df 100644 --- a/coremods/build.gradle +++ b/coremods/build.gradle @@ -5,15 +5,6 @@ plugins { id 'neoforge.formatting-conventions' } -repositories { - maven { url = 'https://maven.neoforged.net/releases' } - maven { - name 'Mojang' - url 'https://libraries.minecraft.net' - } - mavenCentral() -} - jar { manifest { attributes( diff --git a/coremods/settings.gradle b/coremods/settings.gradle new file mode 100644 index 0000000000..06c2cf68e4 --- /dev/null +++ b/coremods/settings.gradle @@ -0,0 +1,8 @@ +repositories { + maven { url = 'https://maven.neoforged.net/releases' } + maven { + name 'Mojang' + url 'https://libraries.minecraft.net' + } + mavenCentral() +} diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index ea85103b01..5b1791385e 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -13,6 +13,9 @@ plugins { apply plugin : net.neoforged.neodev.NeoDevPlugin +// Because of the source set reference. +evaluationDependsOn(":neoforge-coremods") + gradleutils.setupSigning(project: project, signAllPublications: true) changelog { @@ -67,6 +70,9 @@ neoDev { minecraft { sourceSet sourceSets.main } + "neoforge-coremods" { + sourceSet project(":neoforge-coremods").sourceSets.main + } } } @@ -106,6 +112,12 @@ dependencies { strictly project.asm_version } } + // Also override ASM in implementation + implementation(asmModule) { + version { + strictly project.asm_version + } + } } moduleOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" moduleOnly "net.neoforged:JarJarFileSystems:${project.jarjar_version}" @@ -162,10 +174,6 @@ dependencies { neoDev { runs { - configureEach { - gameDirectory.set project.file("run/${it.name}") as File - modSources.add project(":neoforge-coremods").sourceSets.main - } client { client() } From ed633d70aeddc8ab6634913506989750a4dd43ee Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:14:45 +0200 Subject: [PATCH 05/39] Make generatePackageInfos config cache compatible --- .../groovy/neoforge.formatting-conventions.gradle | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle index 0ba7b48852..287bff73c1 100644 --- a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle +++ b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle @@ -2,9 +2,14 @@ import java.util.regex.Matcher project.plugins.apply('com.diffplug.spotless') -final generatePackageInfos = tasks.register('generatePackageInfos', Task) { - doLast { - fileTree('src/main/java').each { javaFile -> +abstract class GeneratePackageInfos extends DefaultTask { + @InputFiles + @PathSensitive(PathSensitivity.NONE) + abstract ConfigurableFileCollection getFiles(); + + @TaskAction + void generatePackageInfos() { + getFiles().each { javaFile -> def packageInfoFile = new File(javaFile.parent, 'package-info.java') if (!packageInfoFile.exists()) { def pkgName = javaFile.toString().replaceAll(Matcher.quoteReplacement(File.separator), '/') @@ -27,6 +32,9 @@ final generatePackageInfos = tasks.register('generatePackageInfos', Task) { } } } +final generatePackageInfos = tasks.register('generatePackageInfos', GeneratePackageInfos) { + it.files.from fileTree("src/main/java") +} spotless { java { From cb30f1f62974001ed072dd918a6830e7113803db Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:40:07 +0200 Subject: [PATCH 06/39] Back to genPatches --- .github/workflows/check-local-changes.yml | 2 +- buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-local-changes.yml b/.github/workflows/check-local-changes.yml index ef101ceacd..0609d67e89 100644 --- a/.github/workflows/check-local-changes.yml +++ b/.github/workflows/check-local-changes.yml @@ -41,7 +41,7 @@ jobs: run: ./gradlew generatePackageInfos - name: Gen patches - run: ./gradlew :neoforge:unpackSourcePatches + run: ./gradlew :neoforge:genPatches - name: Run datagen with Gradle run: ./gradlew :neoforge:runData :tests:runData diff --git a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle index 287bff73c1..e0f554b820 100644 --- a/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle +++ b/buildSrc/src/main/groovy/neoforge.formatting-conventions.gradle @@ -4,7 +4,7 @@ project.plugins.apply('com.diffplug.spotless') abstract class GeneratePackageInfos extends DefaultTask { @InputFiles - @PathSensitive(PathSensitivity.NONE) + @PathSensitive(PathSensitivity.RELATIVE) abstract ConfigurableFileCollection getFiles(); @TaskAction From c0a5816ceb12c72927bd3b24c2cbf8f48a582762 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 25 Oct 2024 14:45:30 +0200 Subject: [PATCH 07/39] Copy utils from MDG --- .../neodev/ApplyAccessTransformer.java | 2 +- .../net/neoforged/neodev/NeoDevPlugin.java | 2 +- .../neoforged/neodev/WriteUserDevConfig.java | 2 +- .../installer/CreateInstallerProfile.java | 2 +- .../installer/CreateLauncherProfile.java | 2 +- .../neodev/utils/DependencyUtils.java | 56 +++++++++++++++ .../net/neoforged/neodev/utils/FileUtils.java | 72 +++++++++++++++++++ 7 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java index b208523caf..a0a5433b3e 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java @@ -1,6 +1,6 @@ package net.neoforged.neodev; -import net.neoforged.moddevgradle.internal.utils.FileUtils; +import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.tasks.Classpath; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 3116abd643..fd04f05a1e 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -2,12 +2,12 @@ import net.neoforged.minecraftdependencies.MinecraftDependenciesPlugin; import net.neoforged.moddevgradle.internal.NeoDevFacade; -import net.neoforged.moddevgradle.internal.utils.DependencyUtils; import net.neoforged.moddevgradle.tasks.JarJar; import net.neoforged.neodev.installer.CreateArgsFile; import net.neoforged.neodev.installer.CreateInstallerProfile; import net.neoforged.neodev.installer.CreateLauncherProfile; import net.neoforged.neodev.installer.InstallerProcessor; +import net.neoforged.neodev.utils.DependencyUtils; import net.neoforged.nfrtgradle.CreateMinecraftArtifacts; import net.neoforged.nfrtgradle.DownloadAssets; import net.neoforged.nfrtgradle.NeoFormRuntimePlugin; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java b/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java index 25bc50b283..51edf284aa 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java @@ -1,7 +1,7 @@ package net.neoforged.neodev; import com.google.gson.GsonBuilder; -import net.neoforged.moddevgradle.internal.utils.FileUtils; +import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index 75b2ff1eb5..eebe4af116 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -1,7 +1,7 @@ package net.neoforged.neodev.installer; import com.google.gson.GsonBuilder; -import net.neoforged.moddevgradle.internal.utils.FileUtils; +import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index dc03cf5612..ef5d4b05ae 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -1,7 +1,7 @@ package net.neoforged.neodev.installer; import com.google.gson.GsonBuilder; -import net.neoforged.moddevgradle.internal.utils.FileUtils; +import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java new file mode 100644 index 0000000000..35ef65fd77 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java @@ -0,0 +1,56 @@ +package net.neoforged.neodev.utils; + +import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier; + +public final class DependencyUtils { + private DependencyUtils() { + } + + /** + * Given a resolved artifact, try to guess which Maven GAV it was resolved from. + */ + public static String guessMavenGav(ResolvedArtifactResult result) { + String artifactId; + String ext = ""; + String classifier = null; + + var filename = result.getFile().getName(); + var startOfExt = filename.lastIndexOf('.'); + if (startOfExt != -1) { + ext = filename.substring(startOfExt + 1); + filename = filename.substring(0, startOfExt); + } + + if (result.getId() instanceof ModuleComponentArtifactIdentifier moduleId) { + var artifact = moduleId.getComponentIdentifier().getModule(); + var version = moduleId.getComponentIdentifier().getVersion(); + var expectedBasename = artifact + "-" + version; + + if (filename.startsWith(expectedBasename + "-")) { + classifier = filename.substring((expectedBasename + "-").length()); + } + artifactId = moduleId.getComponentIdentifier().getGroup() + ":" + artifact + ":" + version; + } else { + // When we encounter a project reference, the component identifier does not expose the group or module name. + // But we can access the list of capabilities associated with the published variant the artifact originates from. + // If the capability was not overridden, this will be the project GAV. If it is *not* the project GAV, + // it will be at least in valid GAV format, not crashing NFRT when it parses the manifest. It will just be ignored. + var capabilities = result.getVariant().getCapabilities(); + if (capabilities.size() == 1) { + var capability = capabilities.get(0); + artifactId = capability.getGroup() + ":" + capability.getName() + ":" + capability.getVersion(); + } else { + artifactId = result.getId().getComponentIdentifier().toString(); + } + } + String gav = artifactId; + if (classifier != null) { + gav += ":" + classifier; + } + if (!"jar".equals(ext)) { + gav += "@" + ext; + } + return gav; + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java new file mode 100644 index 0000000000..d91996d32d --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/FileUtils.java @@ -0,0 +1,72 @@ +package net.neoforged.neodev.utils; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.file.AtomicMoveNotSupportedException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.List; + +public final class FileUtils { + private FileUtils() { + } + + public static void writeStringSafe(Path destination, String content, Charset charset) throws IOException { + if (!charset.newEncoder().canEncode(content)) { + throw new IllegalArgumentException("The given character set " + charset + + " cannot represent this string: " + content); + } + + try (var out = newSafeFileOutputStream(destination)) { + var encodedContent = content.getBytes(charset); + out.write(encodedContent); + } + } + + public static void writeLinesSafe(Path destination, List lines, Charset charset) throws IOException { + writeStringSafe(destination, String.join("\n", lines), charset); + } + + public static OutputStream newSafeFileOutputStream(Path destination) throws IOException { + var uniqueId = ProcessHandle.current().pid() + "." + Thread.currentThread().getId(); + + var tempFile = destination.resolveSibling(destination.getFileName().toString() + "." + uniqueId + ".tmp"); + var closed = new boolean[1]; + return new FilterOutputStream(Files.newOutputStream(tempFile)) { + @Override + public void close() throws IOException { + try { + super.close(); + if (!closed[0]) { + atomicMoveIfPossible(tempFile, destination); + } + } finally { + try { + Files.deleteIfExists(tempFile); + } catch (IOException ignored) { + } + closed[0] = true; + } + } + }; + } + + /** + * Atomically moves the given source file to the given destination file. + * If the atomic move is not supported, the file will be moved normally. + * + * @param source The source file + * @param destination The destination file + * @throws IOException If an I/O error occurs + */ + private static void atomicMoveIfPossible(final Path source, final Path destination) throws IOException { + try { + Files.move(source, destination, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING); + } catch (AtomicMoveNotSupportedException ex) { + Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); + } + } +} From fb38822810ae56459eabb2269077828c712a142a Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sat, 26 Oct 2024 11:35:52 +0200 Subject: [PATCH 08/39] Try to fix compat JAR --- projects/neoforge/build.gradle | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 5b1791385e..781fbf1576 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -34,26 +34,26 @@ sourceSets { } } -// TODO re-enable final checkVersion = JCCPlugin.providePreviousVersion( project.providers, project.providers.provider({['https://maven.neoforged.net/releases']}), project.providers.provider({'net.neoforged:neoforge'}), project.provider { project.version }.map { ver -> CompatibilityTask.VersionComponentTest.MINOR.predicate(ver) } ) -//final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { -// // Use the same jar that the patches were generated against -// cleanJar.set(tasks.generateClientBinaryPatches.clean) -// maven.set('https://maven.neoforged.net/releases') -// artifact.set('net.neoforged:neoforge') -// version.set(checkVersion) -// javaLauncher = javaToolchains.launcherFor { -// languageVersion = JavaLanguageVersion.of(java_version) -// } -//} -//checkJarCompatibility { -// isAPI = true -// baseJar = createCompatJar.flatMap { it.output } -//} -// +final createCompatJar = tasks.register('createCompatibilityCheckJar', ProvideNeoForgeJarTask) { + // Use the same jar that the patches were generated against + cleanJar.set(tasks.generateClientBinPatches.cleanJar) + maven.set('https://maven.neoforged.net/releases') + artifact.set('net.neoforged:neoforge') + version.set(checkVersion) + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(java_version) + } +} +checkJarCompatibility { + isAPI = true + baseJar = createCompatJar.flatMap { it.output } +} + +// TODO: re-enable AT validation //tasks.configureEach { tsk -> // if (tsk.name == 'neoFormApplyUserAccessTransformer' && project.hasProperty('validateAccessTransformers')) { // tsk.inputs.property('validation', 'error') From 120caef8dc5d4abba54db08e7312924f17174bcd Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sat, 26 Oct 2024 17:18:19 +0200 Subject: [PATCH 09/39] Re-enable changelog and installer publication --- projects/neoforge/build.gradle | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 781fbf1576..f93beb1409 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -4,6 +4,7 @@ import net.neoforged.jarcompatibilitychecker.gradle.CompatibilityTask import net.neoforged.jarcompatibilitychecker.gradle.ProvideNeoForgeJarTask plugins { + id 'java-library' id 'maven-publish' id 'net.neoforged.jarcompatibilitychecker' version '0.1.12' @@ -404,15 +405,14 @@ artifacts { universalJar(universalJar) { setClassifier("universal") } -// installerJar(signInstallerJar.output) { -// builtBy(signInstallerJar) -// setClassifier("installer") -// } -// changelog(createChangelog.outputFile) { -// builtBy(createChangelog) -// setClassifier("changelog") -// setExtension("txt") -// } + installerJar(installerJar) { + setClassifier("installer") + } + changelog(createChangelog.outputFile) { + builtBy(createChangelog) + setClassifier("changelog") + setExtension("txt") + } } publishing { From 14e29d55205145dcd5f8e72ebef6a6b9d979b0a3 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sun, 10 Nov 2024 11:51:53 +0100 Subject: [PATCH 10/39] Replace ModuleIdentificationVisitor by ResolvedArtifactResult manipulation --- .../net/neoforged/neodev/NeoDevPlugin.java | 36 +++++------ .../installer/ArtifactPathsCollector.java | 50 ---------------- .../neodev/installer/CreateArgsFile.java | 35 +++++------ .../installer/CreateInstallerProfile.java | 20 +++---- .../installer/CreateLauncherProfile.java | 33 +++++----- .../neodev/installer/IdentifiedFile.java | 48 +++++++++++++++ .../neodev/installer/LibraryCollector.java | 44 ++++++++------ .../ModuleIdentificationVisitor.java | 42 ------------- .../neodev/utils/DependencyUtils.java | 60 ++++++++++++++----- .../neodev/utils/MavenIdentifier.java | 13 ++++ 10 files changed, 187 insertions(+), 194 deletions(-) delete mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java delete mode 100644 buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index fd04f05a1e..bc1b97a4e5 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -14,7 +14,6 @@ import net.neoforged.nfrtgradle.NeoFormRuntimeTask; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; @@ -131,9 +130,9 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getLibraries().addAll(configurationToGavList(devLibrariesConfiguration)); - task.getModules().addAll(configurationToGavList(modulesConfiguration)); - task.getTestLibraries().addAll(configurationToGavList(userDevTestImplementationConfiguration)); + task.getLibraries().addAll(DependencyUtils.configurationToGavList(devLibrariesConfiguration)); + task.getModules().addAll(DependencyUtils.configurationToGavList(modulesConfiguration)); + task.getTestLibraries().addAll(DependencyUtils.configurationToGavList(userDevTestImplementationConfiguration)); task.getTestLibraries().add(neoForgeVersion.map(v -> "net.neoforged:testframework:" + v)); task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { return results.stream().map(r -> r.getFile().getName()).toList(); @@ -232,12 +231,18 @@ public void apply(Project project) { patchesFolder ); + var launcherProfileLibraries = configurations.create("launcherProfileLibraries", spec -> { + spec.setCanBeResolved(true); + spec.setCanBeConsumed(false); + }); + launcherProfileLibraries.extendsFrom(installerConfiguration, modulesConfiguration); + launcherProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); var createLauncherProfile = tasks.register("createLauncherProfile", CreateLauncherProfile.class, task -> { task.getFmlVersion().set(fmlVersion); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getLibraries().from(installerConfiguration, modulesConfiguration); + task.setLibraries(launcherProfileLibraries); task.getRepositoryURLs().set(project.provider(() -> { List repos = new ArrayList<>(); for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { @@ -253,7 +258,7 @@ public void apply(Project project) { return results.stream().map(r -> r.getFile().getName()).toList(); })); task.getIgnoreList().addAll("client-extra", "neoforge-"); - task.getModulePath().from(modulesConfiguration); + task.setModules(modulesConfiguration); task.getLauncherProfile().set(neoDevBuildDir.map(dir -> dir.file("launcher-profile.json"))); }); @@ -268,7 +273,7 @@ public void apply(Project project) { task.getNeoForgeVersion().set(neoForgeVersion); task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion); task.getIcon().set(project.getRootProject().file("docs/assets/neoforged.ico")); - task.getLibraries().from(installerProfileLibraries); + task.setLibraries(installerProfileLibraries); task.getRepositoryURLs().set(project.provider(() -> { List repos = new ArrayList<>(); for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { @@ -294,20 +299,17 @@ public void apply(Project project) { // Each tool should resolve consistently with the full set of installed libraries. configuration.shouldResolveConsistentlyWith(installerProfileLibraries); createInstallerProfile.configure(task -> { - task.getProcessorClasspaths().put(installerProcessor, configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - // Using .toList() fails with the configuration cache - looks like Gradle can't deserialize the resulting list? - return results.stream().map(DependencyUtils::guessMavenGav).collect(Collectors.toCollection(ArrayList::new)); - })); + task.getProcessorClasspaths().put(installerProcessor, DependencyUtils.configurationToGavList(configuration)); task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project)); }); } var createWindowsServerArgsFile = tasks.register("createWindowsServerArgsFile", CreateArgsFile.class, task -> { - task.getPathSeparator().set(";"); + task.setLibraries(";", installerConfiguration, modulesConfiguration); task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("windows-server-args.txt"))); }); var createUnixServerArgsFile = tasks.register("createUnixServerArgsFile", CreateArgsFile.class, task -> { - task.getPathSeparator().set(":"); + task.setLibraries(":", installerConfiguration, modulesConfiguration); task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("unix-server-args.txt"))); }); @@ -318,11 +320,9 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getModules().from(modulesConfiguration); task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { return results.stream().map(r -> r.getFile().getName()).toList(); })); - task.getClasspath().from(installerConfiguration); task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar)); }); } @@ -539,10 +539,4 @@ static TaskProvider configureMinecraftDecompilation(Pr task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip")); }); } - - private Provider> configurationToGavList(Configuration configuration) { - return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(DependencyUtils::guessMavenGav).toList(); - }); - } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java deleted file mode 100644 index d3cb6fafc3..0000000000 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/ArtifactPathsCollector.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.neoforged.neodev.installer; - -import java.io.File; - -/** - * Turns a collection of files into a classpath description, with libraries stored under a {@code prefix} folder following Maven structure. - */ -class ArtifactPathsCollector extends ModuleIdentificationVisitor { - - private final StringBuilder builder = new StringBuilder(); - private final String separator; - private final String prefix; - - public ArtifactPathsCollector(String separator, String prefix) { - this.separator = separator; - this.prefix = prefix; - } - - @Override - protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) { - builder.append(prefix); - builder.append(group.replace(".", "/")); - builder.append("/"); - builder.append(module); - builder.append("/"); - builder.append(version); - builder.append("/"); - builder.append(module); - builder.append("-"); - builder.append(version); - - if (classifier != null && !classifier.isEmpty()) { - builder.append("-"); - builder.append(classifier); - } - - builder.append(".").append(extension); - builder.append(separator); - } - - @Override - public String toString() { - String result = builder.toString(); - if (result.endsWith(separator)) { - return result.substring(0, result.length() - separator.length()); - } else { - return result; - } - } -} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java index 977328da6f..1739aae1e8 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java @@ -1,17 +1,15 @@ package net.neoforged.neodev.installer; +import net.neoforged.neodev.utils.DependencyUtils; import org.gradle.api.DefaultTask; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ArchiveOperations; -import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import javax.inject.Inject; @@ -43,18 +41,22 @@ public CreateArgsFile() {} public abstract Property getRawNeoFormVersion(); @Input - public abstract Property getPathSeparator(); + protected abstract Property getPathSeparator(); - @InputFiles - @PathSensitive(PathSensitivity.NONE) - public abstract ConfigurableFileCollection getModules(); + @Input + protected abstract Property getModules(); @Input public abstract ListProperty getIgnoreList(); - @InputFiles - @PathSensitive(PathSensitivity.NONE) - public abstract ConfigurableFileCollection getClasspath(); + @Input + protected abstract Property getClasspath(); + + public void setLibraries(String separator, Configuration classpath, Configuration modulePath) { + getPathSeparator().set(separator); + getClasspath().set(DependencyUtils.configurationToClasspath(classpath, "libraries/", separator)); + getModules().set(DependencyUtils.configurationToClasspath(modulePath, "libraries/", separator)); + } @InputFile public abstract RegularFileProperty getRawServerJar(); @@ -66,10 +68,7 @@ public CreateArgsFile() {} protected abstract ArchiveOperations getArchiveOperations(); private String resolveClasspath() throws IOException { - ArtifactPathsCollector classpathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); - classpathCollector.visitFiles(getClasspath()); - - var ourClasspath = classpathCollector + getPathSeparator().get() + var ourClasspath = getClasspath().get() + getPathSeparator().get() + "libraries/net/minecraft/server/%s/server-%s-extra.jar".formatted( getRawNeoFormVersion().get(), getRawNeoFormVersion().get()); @@ -102,12 +101,8 @@ private static String stripVersionSuffix(String classpathEntry) { @TaskAction public void createArgsFile() throws IOException { - ArtifactPathsCollector modulePathCollector = new ArtifactPathsCollector(getPathSeparator().get(), "libraries/"); - - modulePathCollector.visitFiles(getModules()); - var replacements = new HashMap(); - replacements.put("@MODULE_PATH@", modulePathCollector.toString()); + replacements.put("@MODULE_PATH@", getModules().get()); replacements.put("@MODULES@", "ALL-MODULE-PATH"); replacements.put("@IGNORE_LIST@", String.join(",", getIgnoreList().get())); replacements.put("@PLUGIN_LAYER_LIBRARIES@", ""); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index eebe4af116..5170acf524 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -3,17 +3,15 @@ import com.google.gson.GsonBuilder; import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.DefaultTask; -import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import org.jetbrains.annotations.Nullable; @@ -45,9 +43,12 @@ public CreateInstallerProfile() {} @InputFile public abstract RegularFileProperty getIcon(); - @InputFiles - @PathSensitive(PathSensitivity.NONE) - public abstract ConfigurableFileCollection getLibraries(); + @Nested + protected abstract ListProperty getLibraryFiles(); + + public void setLibraries(Configuration libraries) { + getLibraryFiles().set(IdentifiedFile.listFromConfiguration(getProject(), libraries)); + } @Input public abstract ListProperty getRepositoryURLs(); @@ -133,9 +134,8 @@ public void createInstallerProfile() throws IOException { ); getLogger().info("Collecting libraries for Installer Profile"); - var profileFiller = new LibraryCollector(getRepositoryURLs().get()); - profileFiller.visitFiles(getLibraries()); - var libraries = new ArrayList<>(profileFiller.getLibraries()); + var libraries = new ArrayList<>( + LibraryCollector.resolveLibraries(getRepositoryURLs().get(), getLibraryFiles().get())); var universalJar = getUniversalJar().getAsFile().get().toPath(); libraries.add(new Library( diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index ef5d4b05ae..a72d078c22 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -1,17 +1,16 @@ package net.neoforged.neodev.installer; import com.google.gson.GsonBuilder; +import net.neoforged.neodev.utils.DependencyUtils; import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.DefaultTask; -import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import javax.inject.Inject; @@ -41,9 +40,12 @@ public CreateLauncherProfile() {} @Input public abstract Property getRawNeoFormVersion(); - @InputFiles - @PathSensitive(PathSensitivity.NONE) - public abstract ConfigurableFileCollection getLibraries(); + @Nested + protected abstract ListProperty getLibraryFiles(); + + public void setLibraries(Configuration libraries) { + getLibraryFiles().set(IdentifiedFile.listFromConfiguration(getProject(), libraries)); + } @Input public abstract ListProperty getRepositoryURLs(); @@ -51,9 +53,12 @@ public CreateLauncherProfile() {} @Input public abstract ListProperty getIgnoreList(); - @InputFiles - @PathSensitive(PathSensitivity.NAME_ONLY) - public abstract ConfigurableFileCollection getModulePath(); + @Input + protected abstract Property getModulePath(); + + public void setModules(Configuration modules) { + getModulePath().set(DependencyUtils.configurationToClasspath(modules, "${library_directory}/", "${classpath_separator}")); + } @OutputFile public abstract RegularFileProperty getLauncherProfile(); @@ -63,9 +68,7 @@ public void createLauncherProfile() throws IOException { var time = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME); getLogger().info("Collecting libraries for Launcher Profile"); - var profileFiller = new LibraryCollector(getRepositoryURLs().get()); - profileFiller.visitFiles(getLibraries()); - var libraries = profileFiller.getLibraries(); + var libraries = LibraryCollector.resolveLibraries(getRepositoryURLs().get(), getLibraryFiles().get()); var gameArguments = new ArrayList<>(List.of( "--fml.neoForgeVersion", getNeoForgeVersion().get(), @@ -81,10 +84,8 @@ public void createLauncherProfile() throws IOException { "-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar", "-DlibraryDirectory=${library_directory}")); - var modulePathCollector = new ArtifactPathsCollector("${classpath_separator}", "${library_directory}/"); - modulePathCollector.visitFiles(getModulePath()); jvmArguments.add("-p"); - jvmArguments.add(modulePathCollector.toString()); + jvmArguments.add(getModulePath().get()); jvmArguments.addAll(List.of( "--add-modules", "ALL-MODULE-PATH", diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java new file mode 100644 index 0000000000..a7f685236a --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/IdentifiedFile.java @@ -0,0 +1,48 @@ +package net.neoforged.neodev.installer; + +import net.neoforged.neodev.utils.DependencyUtils; +import net.neoforged.neodev.utils.MavenIdentifier; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; + +import javax.inject.Inject; +import java.io.File; +import java.util.List; + +/** + * Combines a {@link File} and its {@link MavenIdentifier maven identifier}, + * for usage as task inputs that will be passed to {@link LibraryCollector}. + */ +abstract class IdentifiedFile { + static Provider> listFromConfiguration(Project project, Configuration configuration) { + return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map( + artifacts -> artifacts.stream() + .map(artifact -> IdentifiedFile.of(project, artifact)) + .toList()); + } + + private static IdentifiedFile of(Project project, ResolvedArtifactResult resolvedArtifact) { + var identifiedFile = project.getObjects().newInstance(IdentifiedFile.class); + identifiedFile.getFile().set(resolvedArtifact.getFile()); + identifiedFile.getIdentifier().set(DependencyUtils.guessMavenIdentifier(resolvedArtifact)); + return identifiedFile; + } + + @Inject + public IdentifiedFile() {} + + @InputFile + @PathSensitive(PathSensitivity.NONE) + protected abstract RegularFileProperty getFile(); + + @Input + protected abstract Property getIdentifier(); +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java index 74ad54c574..7417ac14c3 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/LibraryCollector.java @@ -1,5 +1,6 @@ package net.neoforged.neodev.installer; +import net.neoforged.neodev.utils.MavenIdentifier; import org.gradle.api.logging.Logger; import org.gradle.api.logging.Logging; @@ -15,6 +16,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Collection; import java.util.HexFormat; import java.util.List; import java.util.Locale; @@ -25,7 +27,24 @@ /** * For each file in a collection, finds the repository that the file came from. */ -class LibraryCollector extends ModuleIdentificationVisitor { +class LibraryCollector { + public static List resolveLibraries(List repositoryUrls, Collection libraries) throws IOException { + var collector = new LibraryCollector(repositoryUrls); + for (var library : libraries) { + collector.addLibrary(library.getFile().getAsFile().get(), library.getIdentifier().get()); + } + + var result = collector.libraries.stream().map(future -> { + try { + return future.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + }).toList(); + LOGGER.info("Collected %d libraries".formatted(result.size())); + return result; + } + private static final Logger LOGGER = Logging.getLogger(LibraryCollector.class); /** * Hosts from which we allow the installer to download. @@ -46,7 +65,7 @@ class LibraryCollector extends ModuleIdentificationVisitor { private final HttpClient httpClient = HttpClient.newBuilder().build(); - LibraryCollector(List repoUrl) { + private LibraryCollector(List repoUrl) { this.repositoryUrls = new ArrayList<>(repoUrl); // Only remote repositories make sense (no maven local) @@ -71,10 +90,9 @@ class LibraryCollector extends ModuleIdentificationVisitor { } } - @Override - protected void visitModule(File file, String group, String module, String version, String classifier, final String extension) throws IOException { - final String name = group + ":" + module + ":" + version + (classifier.isEmpty() ? "" : ":" + classifier) + "@" + extension; - final String path = group.replace(".", "/") + "/" + module + "/" + version + "/" + module + "-" + version + (classifier.isEmpty() ? "" : "-" + classifier) + "." + extension; + private void addLibrary(File file, MavenIdentifier identifier) throws IOException { + final String name = identifier.artifactNotation(); + final String path = identifier.repositoryPath(); var sha1 = sha1Hash(file.toPath()); var fileSize = Files.size(file.toPath()); @@ -99,7 +117,7 @@ protected void visitModule(File file, String group, String module, String versio } throw new RuntimeException(message); } - LOGGER.info(" Found $name -> $artifactUri"); + LOGGER.info(" Found %s -> %s".formatted(name, artifactUri)); return new Library( name, new LibraryDownload(new LibraryArtifact( @@ -153,16 +171,4 @@ private static URI joinUris(URI repositoryUrl, String path) { return URI.create(baseUrl + path); } } - - List getLibraries() { - var result = libraries.stream().map(future -> { - try { - return future.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }).toList(); - LOGGER.info("Collected %d libraries".formatted(result.size())); - return result; - } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java deleted file mode 100644 index 9d7cf79678..0000000000 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/ModuleIdentificationVisitor.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.neoforged.neodev.installer; - -import org.gradle.api.file.FileCollection; - -import java.io.File; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -// TODO: simplify this class and subclasses if possible -abstract class ModuleIdentificationVisitor { - - // The following regex detects file path patterns, in gradle cache format. Like: /net.neoforged.fancymodloader/earlydisplay/47.1.47/46509b19504a71e25b115383e900aade5088598a/earlydisplay-47.1.47.jar - private static final Pattern GRADLE_CACHE_PATTERN = Pattern.compile("/(?[^/]+)/(?[^/]+)/(?[^/]+)/(?[a-z0-9]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); - - // The following regex detects file path patterns, in maven local cache format. Like: /.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar - private static final Pattern MAVEN_LOCAL_PATTERN = Pattern.compile("/.m2/repository/(?.+)/(?[^/]+)/(?[^/]+)/\\k-\\k(-(?[^/]+))?\\.(?(jar)|(zip))$"); - - final void visitFiles(FileCollection files) throws IOException { - for (var file : files) { - String absolutePath = file.getAbsolutePath().replace("\\", "/"); - - Matcher matcher = GRADLE_CACHE_PATTERN.matcher(absolutePath); - if (!matcher.find()) { - matcher = MAVEN_LOCAL_PATTERN.matcher(absolutePath); - if (!matcher.find()) { - throw new IllegalStateException("Cannot determine the GAV of " + file + ", since it is neither a remote nor a Maven local dependency!"); - } - } - - String group = matcher.group("group").replace("/", "."); //In case we match the maven way. - String module = matcher.group("module"); - String version = matcher.group("version"); - String classifier = matcher.group("classifier") == null ? "" : matcher.group("classifier"); - String extension = matcher.group("extension"); - - visitModule(file, group, module, version, classifier, extension); - } - } - - protected abstract void visitModule(File file, String group, String module, String version, String classifier, String extension) throws IOException; -} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java index 35ef65fd77..ee1bb65175 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/DependencyUtils.java @@ -1,8 +1,14 @@ package net.neoforged.neodev.utils; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.result.ResolvedArtifactResult; +import org.gradle.api.provider.Provider; import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + public final class DependencyUtils { private DependencyUtils() { } @@ -10,10 +16,12 @@ private DependencyUtils() { /** * Given a resolved artifact, try to guess which Maven GAV it was resolved from. */ - public static String guessMavenGav(ResolvedArtifactResult result) { - String artifactId; + public static MavenIdentifier guessMavenIdentifier(ResolvedArtifactResult result) { + String group; + String artifact; + String version; String ext = ""; - String classifier = null; + String classifier = ""; var filename = result.getFile().getName(); var startOfExt = filename.lastIndexOf('.'); @@ -23,14 +31,14 @@ public static String guessMavenGav(ResolvedArtifactResult result) { } if (result.getId() instanceof ModuleComponentArtifactIdentifier moduleId) { - var artifact = moduleId.getComponentIdentifier().getModule(); - var version = moduleId.getComponentIdentifier().getVersion(); + group = moduleId.getComponentIdentifier().getGroup(); + artifact = moduleId.getComponentIdentifier().getModule(); + version = moduleId.getComponentIdentifier().getVersion(); var expectedBasename = artifact + "-" + version; if (filename.startsWith(expectedBasename + "-")) { classifier = filename.substring((expectedBasename + "-").length()); } - artifactId = moduleId.getComponentIdentifier().getGroup() + ":" + artifact + ":" + version; } else { // When we encounter a project reference, the component identifier does not expose the group or module name. // But we can access the list of capabilities associated with the published variant the artifact originates from. @@ -39,18 +47,38 @@ public static String guessMavenGav(ResolvedArtifactResult result) { var capabilities = result.getVariant().getCapabilities(); if (capabilities.size() == 1) { var capability = capabilities.get(0); - artifactId = capability.getGroup() + ":" + capability.getName() + ":" + capability.getVersion(); + group = capability.getGroup(); + artifact = capability.getName(); + version = capability.getVersion(); } else { - artifactId = result.getId().getComponentIdentifier().toString(); + throw new IllegalArgumentException("Don't know how to break " + result.getId().getComponentIdentifier() + " into Maven components."); } } - String gav = artifactId; - if (classifier != null) { - gav += ":" + classifier; - } - if (!"jar".equals(ext)) { - gav += "@" + ext; - } - return gav; + return new MavenIdentifier(group, artifact, version, classifier, ext); + } + + /** + * Turns a configuration into a list of GAV entries. + */ + public static Provider> configurationToGavList(Configuration configuration) { + return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + // Using .toList() fails with the configuration cache - looks like Gradle can't deserialize the resulting list? + return results.stream().map(artifact -> guessMavenIdentifier(artifact).artifactNotation()).collect(Collectors.toCollection(ArrayList::new)); + }); + } + + /** + * Turns a configuration into a classpath string, + * assuming that the contents of the configuration are installed following the Maven directory layout. + * + * @param prefix string to add in front of each classpath entry + * @param separator separator to add between each classpath entry + */ + public static Provider configurationToClasspath(Configuration configuration, String prefix, String separator) { + return configuration.getIncoming().getArtifacts().getResolvedArtifacts().map( + results -> results.stream() + .map(artifact -> prefix + guessMavenIdentifier(artifact).repositoryPath()) + .collect(Collectors.joining(separator)) + ); } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java b/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java new file mode 100644 index 0000000000..6a22312fd0 --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/utils/MavenIdentifier.java @@ -0,0 +1,13 @@ +package net.neoforged.neodev.utils; + +import java.io.Serializable; + +public record MavenIdentifier(String group, String artifact, String version, String classifier, String extension) implements Serializable { + public String artifactNotation() { + return group + ":" + artifact + ":" + version + (classifier.isEmpty() ? "" : ":" + classifier) + ("jar".equals(extension) ? "" : "@" + extension); + } + + public String repositoryPath() { + return group.replace(".", "/") + "/" + artifact + "/" + version + "/" + artifact + "-" + version + (classifier.isEmpty() ? "" : "-" + classifier) + "." + extension; + } +} From a762a7505d35798c26e86dfb1be2651a5d956b49 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sun, 10 Nov 2024 12:59:58 +0100 Subject: [PATCH 11/39] Bump MDG, move gson and diffpatch versions to main gradle.properties --- buildSrc/build.gradle | 6 +++--- buildSrc/gradle.properties | 3 --- gradle.properties | 3 ++- settings.gradle | 2 ++ 4 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 buildSrc/gradle.properties diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index e89269c0ed..6d0ce4c5af 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -18,9 +18,9 @@ repositories { dependencies { // buildSrc is an includedbuild of the parent directory (gradle.parent) - // ../settings.gradle sets the moddevgradle_plugin_version accordingly + // ../settings.gradle sets these version properties accordingly implementation "net.neoforged:moddev-gradle:${gradle.parent.ext.moddevgradle_plugin_version}" - implementation "com.google.code.gson:gson:${project.gson_version}" - implementation "io.codechicken:DiffPatch:${project.diffpatch_version}" + implementation "com.google.code.gson:gson:${gradle.parent.ext.gson_version}" + implementation "io.codechicken:DiffPatch:${gradle.parent.ext.diffpatch_version}" } diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties deleted file mode 100644 index 6665e0d877..0000000000 --- a/buildSrc/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ - -gson_version=2.11.0 -diffpatch_version=2.0.0.35 diff --git a/gradle.properties b/gradle.properties index 56f42ae36e..cdb66f081b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,8 @@ org.gradle.configuration-cache=true org.gradle.debug=false org.gradle.warning.mode=fail -moddevgradle_plugin_version=2.0.40-beta +moddevgradle_plugin_version=2.0.43-beta +diffpatch_version=2.0.0.35 java_version=21 diff --git a/settings.gradle b/settings.gradle index e8b5e56ed4..aff93fdd4c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,8 @@ plugins { // This makes the version available to buildSrc gradle.ext.moddevgradle_plugin_version = moddevgradle_plugin_version +gradle.ext.gson_version = gson_version +gradle.ext.diffpatch_version = diffpatch_version dependencyResolutionManagement { repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS From 6531711c3bbe0a3b33075a4b33b4517c31240069 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sun, 10 Nov 2024 14:03:07 +0100 Subject: [PATCH 12/39] Disable fail on warnings --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index cdb66f081b..3d624b021e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.parallel=true org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.debug=false -org.gradle.warning.mode=fail +#org.gradle.warning.mode=fail moddevgradle_plugin_version=2.0.43-beta diffpatch_version=2.0.0.35 From cd997b9cccd4743d35d0d6afae2338871e07a354 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:37:28 +0100 Subject: [PATCH 13/39] Fix tests not compiling coremods in nondelegated builds --- projects/neoforge/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index f93beb1409..92c92a5f40 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -170,7 +170,9 @@ dependencies { // TODO: duplicated with the text fixtures userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") - compileOnly(jarJar(project(":neoforge-coremods"))) + // Must be implementation instead of compileOnly so that running dependent projects such as tests will trigger (re)compilation of coremods. + // (Only needed when compiling through IntelliJ non-delegated builds - otherwise `compileOnly` would work). + implementation(jarJar(project(":neoforge-coremods"))) } neoDev { From 14510bac97abc0e237ac2fa5b60fb9108ef58768 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Tue, 12 Nov 2024 22:18:04 +0100 Subject: [PATCH 14/39] Started on documentation. --- buildSrc/README.md | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 buildSrc/README.md diff --git a/buildSrc/README.md b/buildSrc/README.md new file mode 100644 index 0000000000..c1fe25b726 --- /dev/null +++ b/buildSrc/README.md @@ -0,0 +1,59 @@ +# NeoForge Development Gradle Plugin + +## NeoForge Project Structure + +Before understanding the `buildSrc` plugin, one should understand the structure of the NeoForge Gradle project it is +applied to. + +The project consists of a project tree with the following structure: + +| Folder Path | Gradle Project Path | Applied Plugins | Description | +|------------------------------------------------------------------------|----------------------|:------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`/build.gradle`](../build.gradle) | `:` | — | The root project. Since this project is reused for Kits, the root project name is based on the checkout folder, which actually can lead to issues if it is called `NeoForge`. | +| [`/projects/neoforge/build.gradle`](../projects/neoforge/build.gradle) | `:neoforge` | [NeoDevPlugin](#neodevplugin) | The core NeoForge project, which produces the artifacts that will be published. | +| [`/projects/base/build.gradle`](../projects/base/build.gradle) | `:base` | [NeoDevBasePlugin](#neodevbaseplugin) | A utility project that contains the Minecraft sources without any NeoForge additions. Can be used to quickly compare what NeoForge has changed. | +| [`/tests/build.gradle`](../tests/build.gradle) | `:tests` | [NeoDevExtraPlugin](#neodevextraplugin) | Contains the game and unit tests for NeoForge. | +| [`/testframework/build.gradle`](../testframework/build.gradle) | `:testframework` | [MinecraftDependenciesPlugin](#minecraftdependenciesplugin) | A library providing support classes around writing game tests. | +| [`/coremods/build.gradle`](../coremods/build.gradle) | `:neoforge-coremods` | — | Java Bytecode transformers that are embedded into NeoForge as a nested Jar file. | +| + +## Plugins + +### NeoDevBasePlugin + +Sources: [NeoDevBasePlugin.java](src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java) + +Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin). + +Sets up a `setup` task that reuses code from [NeoDevPlugin](#neodevplugin) to decompile Minecraft and place the +decompiled sources in `projects/base/src/main/java`. + +### NeoDevPlugin + +Sources: [NeoDevPlugin.java](src/main/java/net/neoforged/neodev/NeoDevPlugin.java) + +Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin). + +The primary plugin of this repository sets up the `neoforge` project. + +It creates a `setup` task that performs the following actions via various subtasks: + +- Decompile Minecraft using the [NeoForm Runtime](https://github.com/neoforged/neoformruntime) and Minecraft version specific [NeoForm data](https://github.com/neoforged/NeoForm). +- Apply the [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently. +- + +### NeoDevExtraPlugin + +Sources: [NeoDevExtraPlugin.java](src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java) + +This plugin can be applied to obtain a dependency on the `neoforge` project to depend on NeoForge including Minecraft +itself. Besides wiring up the dependency, it also creates run configurations based on the run-types defined in the +`neoforge` project. + +### MinecraftDependenciesPlugin + +This plugin is reused from [ModDevGradle](https://github.com/neoforged/ModDevGradle/). + +It sets up repositories and attributes such that +the [libraries that Minecraft itself depends upon](https://github.com/neoforged/GradleMinecraftDependencies) can be +used. From c762e3aed891a6fe177f58da62ab19e5a82855f3 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:36:31 +0100 Subject: [PATCH 15/39] Remove unused forValidation configuration --- projects/neoforge/build.gradle | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 92c92a5f40..3e39ba84bf 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -204,27 +204,6 @@ tasks.withType(Javadoc.class).configureEach { options.addStringOption('Xdoclint:all,-missing', '-public') } -configurations { - forValidation { - canBeConsumed = true - canBeResolved = false - attributes { - attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) - attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) - attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) - attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, LibraryElements.JAR)) - } - - extendsFrom api, runtimeOnly - } -} - -artifacts { - forValidation(jar.archiveFile) { - builtBy(jar) - } -} - AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java") // Ensure the two default variants are not published, since they // contain Minecraft classes From 8a23e4be1969e904e6d40dde3a79c64707b8ec53 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:50:07 +0100 Subject: [PATCH 16/39] Refactor configuration handling (prod not quite working yet) --- .../neodev/NeoDevConfigurations.java | 155 +++++++++++++++++ .../neoforged/neodev/NeoDevExtraPlugin.java | 21 +-- .../net/neoforged/neodev/NeoDevPlugin.java | 160 +++++++++--------- projects/neoforge/build.gradle | 150 ++++++---------- 4 files changed, 289 insertions(+), 197 deletions(-) create mode 100644 buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java new file mode 100644 index 0000000000..602265f4fe --- /dev/null +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -0,0 +1,155 @@ +package net.neoforged.neodev; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.plugins.JavaPlugin; + +/** + * Helper class to keep track of many {@link Configuration}s used for the {@code neoforge} project. + * + *

Self-contained configurations (e.g. used to resolve tools) are not included here. + */ +class NeoDevConfigurations { + static NeoDevConfigurations createAndSetup(Project project) { + return new NeoDevConfigurations(project.getConfigurations()); + } + + // + // Configurations against which dependencies should be declared ("dependency scopes"). + // + + /** + * Only the NeoForm data zip and the dependencies to run NeoForm. + * Does not contain the dependencies to run vanilla Minecraft. + */ + final Configuration neoFormData; + /** + * Only the NeoForm dependencies. + * These are the dependencies required to run NeoForm-decompiled Minecraft. + * Does not contain the dependencies to run the NeoForm process itself. + */ + final Configuration neoFormDependencies; + /** + * Libraries used by NeoForge at compilation and runtime. + * These will end up on the MC-BOOTSTRAP module layer. + */ + final Configuration libraries; + /** + * Libraries used by NeoForge at compilation and runtime that need to be placed on the jvm's module path to end up in the boot layer. + * Currently, this only contains the few dependencies that are needed to create the MC-BOOTSTRAP module layer. + * (i.e. BootstrapLauncher and its dependencies). + */ + final Configuration moduleLibraries; + /** + * Libraries that should be accessible in mod development environments at compilation time only. + * Currently, this is only used for MixinExtras, which is already available at runtime via JiJ in the NeoForge universal jar. + */ + final Configuration userdevCompileOnly; + /** + * Libraries that should be accessible at runtime in unit tests. + * Currently, this only contains the fml-junit test fixtures. + */ + final Configuration userdevTestFixtures; + + // + // Resolvable configurations. + // + + /** + * Resolvable {@link #neoFormDependencies}. + */ + final Configuration neoFormClasspath; + /** + * Resolvable {@link #moduleLibraries}. + */ + final Configuration modulePath; + /** + * Userdev dependencies (written to a json file in the userdev jar). + * This should contain all of NeoForge's dependencies for userdev and NeoForm, + * but does not need to include all of Minecraft's libraries. + */ + final Configuration userdevClasspath; + /** + * Resolvable {@link #userdevCompileOnly}, to add these entries to the ignore list of BootstrapLauncher. + */ + final Configuration userdevCompileOnlyClasspath; + /** + * Resolvable {@link #userdevTestFixtures}, to write it in the userdev jar. + */ + final Configuration userdevTestClasspath; + /** + * Libraries that need to be added to the classpath when launching NeoForge through the launcher. + * This contains all dependencies added by NeoForge, but does not include all of Minecraft's libraries. + * This is also used to produce the legacy classpath file for server installs. + */ + final Configuration launcherProfileClasspath; + /** + * Libraries that need to be downloaded and installed by the installer. + * This includes all dependencies added by NeoForge, the NeoForm archive, + * and all dependencies needed by the various tools that the installer runs. + */ + final Configuration installerProfileLibraries; + + // + // The configurations for resolution only are declared in the build.gradle file. + // + + private static Configuration dependencyScope(ConfigurationContainer configurations, String name) { + return configurations.create(name, configuration -> { + configuration.setCanBeConsumed(false); + configuration.setCanBeResolved(false); + }); + } + + private static Configuration resolvable(ConfigurationContainer configurations, String name) { + return configurations.create(name, configuration -> { + configuration.setCanBeConsumed(false); + configuration.setCanBeDeclared(false); + }); + } + + private NeoDevConfigurations(ConfigurationContainer configurations) { + neoFormData = dependencyScope(configurations, "neoFormData"); + neoFormDependencies = dependencyScope(configurations, "neoFormDependencies"); + libraries = dependencyScope(configurations, "libraries"); + moduleLibraries = dependencyScope(configurations, "moduleLibraries"); + userdevCompileOnly = dependencyScope(configurations, "userdevCompileOnly"); + userdevTestFixtures = dependencyScope(configurations, "userdevTestFixtures"); + + neoFormClasspath = resolvable(configurations, "neoFormClasspath"); + modulePath = resolvable(configurations, "modulePath"); + userdevClasspath = resolvable(configurations, "userdevClasspath"); + userdevCompileOnlyClasspath = resolvable(configurations, "userdevCompileOnlyClasspath"); + userdevTestClasspath = resolvable(configurations, "userdevTestClasspath"); + launcherProfileClasspath = resolvable(configurations, "launcherProfileClasspath"); + installerProfileLibraries = resolvable(configurations, "installerProfileLibraries"); + + // Libraries & module libraries & MC dependencies need to be available when compiling in NeoDev, + // and on the runtime classpath too for IDE debugging support. + configurations.getByName("implementation").extendsFrom(libraries, moduleLibraries, neoFormDependencies); + + // runtimeClasspath is our reference for all dependency versions. + // Make sure that any configuration we resolve are consistent with it. + var runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + + modulePath.extendsFrom(moduleLibraries); + modulePath.shouldResolveConsistentlyWith(runtimeClasspath); + + userdevClasspath.extendsFrom(libraries, moduleLibraries, userdevCompileOnly, neoFormData); // TODO: is neoFormData necessary here? + userdevClasspath.shouldResolveConsistentlyWith(runtimeClasspath); + + userdevCompileOnlyClasspath.extendsFrom(userdevCompileOnly); + userdevCompileOnlyClasspath.shouldResolveConsistentlyWith(runtimeClasspath); + + userdevTestClasspath.extendsFrom(userdevTestFixtures); + userdevTestClasspath.shouldResolveConsistentlyWith(runtimeClasspath); + + launcherProfileClasspath.extendsFrom(libraries, moduleLibraries); + launcherProfileClasspath.shouldResolveConsistentlyWith(runtimeClasspath); + + installerProfileLibraries.extendsFrom(libraries, moduleLibraries, neoFormData); + installerProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); + // Installer tools are also added to `installerProfileLibraries`, from NeoDevPlugin. + } +} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java index 883d0b29d7..c33cc346bf 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -29,22 +29,15 @@ public void apply(Project project) { var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); - var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version"); - var minecraftVersion = project.getProviders().gradleProperty("minecraft_version"); - var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); + var modulePathDependency = projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-moddev-module-path"); // TODO: this is temporary - var modulesConfiguration = project.getConfigurations().create("moduleOnly", spec -> { - spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "moduleOnly")); - }); - var downloadAssets = neoForgeProject.getTasks().named("downloadAssets", DownloadAssets.class); var createArtifacts = neoForgeProject.getTasks().named("createSourceArtifacts", CreateMinecraftArtifacts.class); var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", WriteUserDevConfig.class); Consumer configureLegacyClasspath = spec -> { - spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "modDevRuntimeElements")); - spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "userdevCompileOnly")); + spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-dependencies")); // TODO: Convert into a cross-project dependency too spec.getDependencies().add( dependencyFactory.create( @@ -61,7 +54,7 @@ public void apply(Project project) { neoDevBuildDir, extension.getRuns(), writeNeoDevConfig, - modulePath -> modulePath.extendsFrom(modulesConfiguration), + modulePath -> modulePath.getDependencies().add(modulePathDependency), configureLegacyClasspath, downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) ); @@ -77,15 +70,17 @@ public void apply(Project project) { writeNeoDevConfig, testExtension.getLoadedMods(), testExtension.getTestedMod(), - modulePath -> modulePath.extendsFrom(modulesConfiguration), + modulePath -> modulePath.getDependencies().add(modulePathDependency), configureLegacyClasspath, downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) ); } - private static ProjectDependency projectDep(DependencyFactory dependencyFactory, Project project, String configurationName) { + private static ProjectDependency projectDep(DependencyFactory dependencyFactory, Project project, String capabilityNotation) { var dep = dependencyFactory.create(project); - dep.setTargetConfiguration(configurationName); + dep.capabilities(caps -> { + caps.requireCapability(capabilityNotation); + }); return dep; } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index bc1b97a4e5..14908ca745 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -14,11 +14,11 @@ import net.neoforged.nfrtgradle.NeoFormRuntimeTask; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; import org.gradle.api.plugins.BasePluginExtension; -import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Sync; @@ -32,17 +32,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; public class NeoDevPlugin implements Plugin { @Override public void apply(Project project) { project.getPlugins().apply(MinecraftDependenciesPlugin.class); - var createSourceArtifacts = configureMinecraftDecompilation(project); - - var configurations = project.getConfigurations(); - var runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); var dependencyFactory = project.getDependencyFactory(); var tasks = project.getTasks(); var neoDevBuildDir = project.getLayout().getBuildDirectory().dir("neodev"); @@ -54,35 +49,24 @@ public void apply(Project project) { var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); + var configurations = NeoDevConfigurations.createAndSetup(project); - var neoFormDependencies = configurations.create("neoFormDependencies", spec -> { - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.getDependencies().addLater(mcAndNeoFormVersion.map(version -> { - var dep = dependencyFactory.create("net.neoforged:neoform:" + version).capabilities(caps -> { - caps.requireCapability("net.neoforged:neoform-dependencies"); - }); - dep.endorseStrictVersions(); - return dep; - })); - }); - - var jstConfiguration = configurations.create("javaSourceTransformer", files -> { - files.setCanBeConsumed(false); - files.setCanBeResolved(true); - files.getDependencies().add(Tools.JST.asDependency(project)); - }); + /* + * MINECRAFT SOURCES SETUP + */ + // 1. Obtain decompiled Minecraft sources jar using NeoForm. + var createSourceArtifacts = configureMinecraftDecompilation(project); + // 2. Apply AT to the jar from 1. var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); - var applyAt = tasks.register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { - task.classpath(jstConfiguration); - task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); - task.getAccessTransformer().set(atFile); - task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar"))); - task.getLibraries().from(neoFormDependencies); - task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt"))); - }); + var applyAt = configureAccessTransformer( + project, + configurations.neoFormClasspath, + createSourceArtifacts, + neoDevBuildDir, + atFile); + // 3. Apply patches to the jar from 2. var patchesFolder = project.getRootProject().file("patches"); var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> { task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); @@ -91,31 +75,19 @@ public void apply(Project project) { task.getRejectsFolder().set(project.getRootProject().file("rejects")); }); + // 4. Decompile jar from 3. var mcSourcesPath = project.file("src/main/java"); tasks.register("setup", Sync.class, task -> { task.from(project.zipTree(applyPatches.flatMap(ApplyPatches::getPatchedJar))); task.into(mcSourcesPath); }); - var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> { - task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(v -> "net.neoforged:neoform:" + v + "@zip")); - task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); - }); - - var installerConfiguration = project.getConfigurations().create("installer"); - var installerLibrariesConfiguration = configurations.create("installerLibraries"); - var modulesConfiguration = configurations.create("moduleOnly"); - var userDevCompileOnlyConfiguration = configurations.create("userdevCompileOnly"); - var userDevTestImplementationConfiguration = configurations.create("userdevTestImplementation"); - userDevTestImplementationConfiguration.shouldResolveConsistentlyWith(runtimeClasspath); - - var devLibrariesConfiguration = configurations.create("devLibraries", files -> { - files.setCanBeConsumed(false); - files.setCanBeResolved(true); + // Modules are on the classpath too but should be ignored by BootstrapLauncher + var modulePathIgnoreList = configurations.modulePath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); }); - devLibrariesConfiguration.shouldResolveConsistentlyWith(runtimeClasspath); - devLibrariesConfiguration.extendsFrom(installerLibrariesConfiguration, modulesConfiguration, userDevCompileOnlyConfiguration); + // 1. Write configs that contain the runs in a format understood by MDG/NG/etc. Currently one for neodev and one for userdev. var writeNeoDevConfig = tasks.register("writeNeoDevConfig", WriteUserDevConfig.class, task -> { task.getForNeoDev().set(true); task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("neodev-config.json"))); @@ -130,14 +102,12 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getLibraries().addAll(DependencyUtils.configurationToGavList(devLibrariesConfiguration)); - task.getModules().addAll(DependencyUtils.configurationToGavList(modulesConfiguration)); - task.getTestLibraries().addAll(DependencyUtils.configurationToGavList(userDevTestImplementationConfiguration)); + task.getLibraries().addAll(DependencyUtils.configurationToGavList(configurations.userdevClasspath)); + task.getModules().addAll(DependencyUtils.configurationToGavList(configurations.modulePath)); + task.getTestLibraries().addAll(DependencyUtils.configurationToGavList(configurations.userdevTestClasspath)); task.getTestLibraries().add(neoForgeVersion.map(v -> "net.neoforged:testframework:" + v)); - task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(r -> r.getFile().getName()).toList(); - })); - task.getIgnoreList().addAll(userDevCompileOnlyConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + task.getIgnoreList().addAll(modulePathIgnoreList); + task.getIgnoreList().addAll(configurations.userdevCompileOnlyClasspath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { return results.stream().map(r -> r.getFile().getName()).toList(); })); task.getIgnoreList().addAll("client-extra", "neoforge-"); @@ -145,13 +115,20 @@ public void apply(Project project) { }); } + // 2. Task to download assets. + var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> { + task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(v -> "net.neoforged:neoform:" + v + "@zip")); + task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); + }); + + // 3. Let MDG do the rest of the setup. :) NeoDevFacade.setupRuns( project, neoDevBuildDir, extension.getRuns(), writeNeoDevConfig, modulePath -> { - modulePath.extendsFrom(modulesConfiguration); + modulePath.extendsFrom(configurations.moduleLibraries); }, legacyClassPath -> { legacyClassPath.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> { @@ -162,22 +139,29 @@ public void apply(Project project) { project.files(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) ) ); - legacyClassPath.extendsFrom(installerLibrariesConfiguration, modulesConfiguration, userDevCompileOnlyConfiguration); + legacyClassPath.extendsFrom(configurations.libraries, configurations.moduleLibraries, configurations.userdevCompileOnly); }, downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) ); + /* + * OTHER TASKS + */ + + // Generate source patches into a patch archive. var genSourcePatches = tasks.register("generateSourcePatches", GenerateSourcePatches.class, task -> { task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); task.getModifiedSources().set(project.file("src/main/java")); task.getPatchesJar().set(neoDevBuildDir.map(dir -> dir.file("source-patches.zip"))); }); + // Update the patch/ folder with the current patches. var genPatches = tasks.register("genPatches", Sync.class, task -> { task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar))); task.into(project.getRootProject().file("patches")); }); + // Universal jar = the jar that contains NeoForge classes // TODO: signing? var universalJar = tasks.register("universalJar", Jar.class, task -> { task.getArchiveClassifier().set("universal"); @@ -231,18 +215,13 @@ public void apply(Project project) { patchesFolder ); - var launcherProfileLibraries = configurations.create("launcherProfileLibraries", spec -> { - spec.setCanBeResolved(true); - spec.setCanBeConsumed(false); - }); - launcherProfileLibraries.extendsFrom(installerConfiguration, modulesConfiguration); - launcherProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); + // Launcher profile = the version.json file used by the Minecraft launcher. var createLauncherProfile = tasks.register("createLauncherProfile", CreateLauncherProfile.class, task -> { task.getFmlVersion().set(fmlVersion); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.setLibraries(launcherProfileLibraries); + task.setLibraries(configurations.launcherProfileClasspath); task.getRepositoryURLs().set(project.provider(() -> { List repos = new ArrayList<>(); for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { @@ -254,26 +233,19 @@ public void apply(Project project) { } return repos; })); - task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(r -> r.getFile().getName()).toList(); - })); + task.getIgnoreList().addAll(modulePathIgnoreList); task.getIgnoreList().addAll("client-extra", "neoforge-"); - task.setModules(modulesConfiguration); + task.setModules(configurations.modulePath); task.getLauncherProfile().set(neoDevBuildDir.map(dir -> dir.file("launcher-profile.json"))); }); - var installerProfileLibraries = configurations.create("installerProfileLibraries", spec -> { - spec.setCanBeResolved(true); - spec.setCanBeConsumed(false); - }); - installerProfileLibraries.extendsFrom(installerLibrariesConfiguration); - installerProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); + // Installer profile = the .json file used by the NeoForge installer. var createInstallerProfile = tasks.register("createInstallerProfile", CreateInstallerProfile.class, task -> { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion); task.getIcon().set(project.getRootProject().file("docs/assets/neoforged.ico")); - task.setLibraries(installerProfileLibraries); + task.setLibraries(configurations.installerProfileLibraries); task.getRepositoryURLs().set(project.provider(() -> { List repos = new ArrayList<>(); for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { @@ -290,14 +262,14 @@ public void apply(Project project) { }); for (var installerProcessor : InstallerProcessor.values()) { - var configuration = configurations.create("installerProcessor" + installerProcessor.toString(), files -> { + var configuration = project.getConfigurations().create("installerProcessor" + installerProcessor.toString(), files -> { files.setCanBeConsumed(false); files.setCanBeResolved(true); files.getDependencies().add(installerProcessor.tool.asDependency(project)); }); - installerProfileLibraries.extendsFrom(configuration); + configurations.installerProfileLibraries.extendsFrom(configuration); // Each tool should resolve consistently with the full set of installed libraries. - configuration.shouldResolveConsistentlyWith(installerProfileLibraries); + configuration.shouldResolveConsistentlyWith(configurations.installerProfileLibraries); createInstallerProfile.configure(task -> { task.getProcessorClasspaths().put(installerProcessor, DependencyUtils.configurationToGavList(configuration)); task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project)); @@ -305,11 +277,11 @@ public void apply(Project project) { } var createWindowsServerArgsFile = tasks.register("createWindowsServerArgsFile", CreateArgsFile.class, task -> { - task.setLibraries(";", installerConfiguration, modulesConfiguration); + task.setLibraries(";", configurations.launcherProfileClasspath, configurations.modulePath); task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("windows-server-args.txt"))); }); var createUnixServerArgsFile = tasks.register("createUnixServerArgsFile", CreateArgsFile.class, task -> { - task.setLibraries(":", installerConfiguration, modulesConfiguration); + task.setLibraries(":", configurations.launcherProfileClasspath, configurations.modulePath); task.getArgsFile().set(neoDevBuildDir.map(dir -> dir.file("unix-server-args.txt"))); }); @@ -320,14 +292,12 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getIgnoreList().addAll(modulesConfiguration.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(r -> r.getFile().getName()).toList(); - })); + task.getIgnoreList().addAll(modulePathIgnoreList); task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar)); }); } - var installerConfig = configurations.create("legacyInstaller", files -> { + var installerConfig = project.getConfigurations().create("legacyInstaller", files -> { files.setCanBeConsumed(false); files.setCanBeResolved(true); files.setTransitive(false); @@ -425,6 +395,28 @@ public void apply(Project project) { }); } + private static TaskProvider configureAccessTransformer( + Project project, + Configuration neoFormClasspath, + TaskProvider createSourceArtifacts, + Provider neoDevBuildDir, + File atFile) { + var jstConfiguration = project.getConfigurations().create("javaSourceTransformer", files -> { + files.setCanBeConsumed(false); + files.setCanBeResolved(true); + files.getDependencies().add(Tools.JST.asDependency(project)); + }); + + return project.getTasks().register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { + task.classpath(jstConfiguration); + task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); + task.getAccessTransformer().set(atFile); + task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar"))); + task.getLibraries().from(neoFormClasspath); + task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt"))); + }); + } + private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, TaskProvider createCleanArtifacts, Provider neoDevBuildDir, diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 3e39ba84bf..3ec98a622e 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -77,99 +77,74 @@ neoDev { } } -configurations { - installer { - // TODO: shouldn't have both on - canBeResolved = true - canBeConsumed = true - } - installerLibraries { - extendsFrom installer - } - moduleOnly { - // TODO: shouldn't have both on - canBeResolved = true - canBeConsumed = true - } - implementation.extendsFrom installer -} - dependencies { - implementation("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { + // For an overview of what the nonstandard configurations do, + // have a look at NeoDevConfigurations.java in the buildSrc folder. + + neoFormData("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { + capabilities { + requireCapability 'net.neoforged:neoform' + } + endorseStrictVersions() + } + neoFormDependencies("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { capabilities { requireCapability 'net.neoforged:neoform-dependencies' } endorseStrictVersions() } - runtimeOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" - - moduleOnly "cpw.mods:securejarhandler:${project.securejarhandler_version}" + moduleLibraries "cpw.mods:securejarhandler:${project.securejarhandler_version}" for (var asmModule : ["org.ow2.asm:asm", "org.ow2.asm:asm-commons", "org.ow2.asm:asm-tree", "org.ow2.asm:asm-util", "org.ow2.asm:asm-analysis"]) { - moduleOnly(asmModule) { + moduleLibraries(asmModule) { // Vanilla ships with ASM 9.3 transitively (via their OpenID connect library dependency), we require // ASM in a more recent version and have to strictly require this to override the strict Minecraft version. version { strictly project.asm_version } } - // Also override ASM in implementation - implementation(asmModule) { - version { - strictly project.asm_version - } - } } - moduleOnly "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" - moduleOnly "net.neoforged:JarJarFileSystems:${project.jarjar_version}" + moduleLibraries "cpw.mods:bootstraplauncher:${project.bootstraplauncher_version}" + moduleLibraries "net.neoforged:JarJarFileSystems:${project.jarjar_version}" - installer ("net.neoforged.fancymodloader:loader:${project.fancy_mod_loader_version}") { + libraries ("net.neoforged.fancymodloader:loader:${project.fancy_mod_loader_version}") { exclude group: 'org.slf4j' exclude group: 'net.fabricmc' } - installer ("net.neoforged.fancymodloader:earlydisplay:${project.fancy_mod_loader_version}") { + libraries ("net.neoforged.fancymodloader:earlydisplay:${project.fancy_mod_loader_version}") { exclude group: 'org.lwjgl' exclude group: 'org.slf4j' exclude group: 'net.fabricmc' } - installer "cpw.mods:securejarhandler:${project.securejarhandler_version}" - installer "org.ow2.asm:asm:${project.asm_version}" - installer "org.ow2.asm:asm-commons:${project.asm_version}" - installer "org.ow2.asm:asm-tree:${project.asm_version}" - installer "org.ow2.asm:asm-util:${project.asm_version}" - installer "org.ow2.asm:asm-analysis:${project.asm_version}" - installer "net.neoforged:accesstransformers:${project.accesstransformers_version}" - installer "net.neoforged:bus:${project.eventbus_version}" - installer "net.neoforged:coremods:${project.coremods_version}" - installer "cpw.mods:modlauncher:${project.modlauncher_version}" - installer "net.neoforged:mergetool:${project.mergetool_version}:api" - installer "com.electronwill.night-config:core:${project.nightconfig_version}" - installer "com.electronwill.night-config:toml:${project.nightconfig_version}" - installer "org.apache.maven:maven-artifact:${project.apache_maven_artifact_version}" - installer "net.jodah:typetools:${project.typetools_version}" - installer "net.minecrell:terminalconsoleappender:${project.terminalconsoleappender_version}" - installer("net.fabricmc:sponge-mixin:${project.mixin_version}") { transitive = false } - installer "org.openjdk.nashorn:nashorn-core:${project.nashorn_core_version}" - installer ("net.neoforged:JarJarSelector:${project.jarjar_version}") { + libraries "net.neoforged:accesstransformers:${project.accesstransformers_version}" + libraries "net.neoforged:bus:${project.eventbus_version}" + libraries "net.neoforged:coremods:${project.coremods_version}" + libraries "cpw.mods:modlauncher:${project.modlauncher_version}" + libraries "net.neoforged:mergetool:${project.mergetool_version}:api" + libraries "com.electronwill.night-config:core:${project.nightconfig_version}" + libraries "com.electronwill.night-config:toml:${project.nightconfig_version}" + libraries "org.apache.maven:maven-artifact:${project.apache_maven_artifact_version}" + libraries "net.jodah:typetools:${project.typetools_version}" + libraries "net.minecrell:terminalconsoleappender:${project.terminalconsoleappender_version}" + libraries("net.fabricmc:sponge-mixin:${project.mixin_version}") { transitive = false } + libraries "org.openjdk.nashorn:nashorn-core:${project.nashorn_core_version}" + libraries ("net.neoforged:JarJarSelector:${project.jarjar_version}") { exclude group: 'org.slf4j' } // We depend on apache commons directly as there is a difference between the version the server uses and the one the client does - installer "org.apache.commons:commons-lang3:${project.apache_commons_lang3_version}" - installer ("net.neoforged:JarJarMetadata:${project.jarjar_version}") { + libraries "org.apache.commons:commons-lang3:${project.apache_commons_lang3_version}" + libraries ("net.neoforged:JarJarMetadata:${project.jarjar_version}") { exclude group: 'org.slf4j' } - // Manually override log4j since the version coming from other `installer` dependencies is outdated - installer "org.apache.logging.log4j:log4j-api:${project.log4j_version}" - installer "org.apache.logging.log4j:log4j-core:${project.log4j_version}" - - installerLibraries "net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}@zip" compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}" userdevCompileOnly jarJar("io.github.llamalad7:mixinextras-neoforge:${project.mixin_extras_version}") - // TODO: duplicated with the text fixtures - userdevTestImplementation("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") + userdevTestFixtures("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") { + endorseStrictVersions() + } + // Must be implementation instead of compileOnly so that running dependent projects such as tests will trigger (re)compilation of coremods. // (Only needed when compiling through IntelliJ non-delegated builds - otherwise `compileOnly` would work). implementation(jarJar(project(":neoforge-coremods"))) @@ -214,10 +189,12 @@ javaComponent.withVariantsFromConfiguration(configurations.runtimeElements) { it.skip() } +// Resolvable configurations only configurations { modDevBundle { + canBeDeclared = false canBeResolved = false - canBeConsumed = true + extendsFrom neoFormData attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data")) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -228,8 +205,8 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} // Publish it } modDevConfig { + canBeDeclared = false canBeResolved = false - canBeConsumed = true attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, "data")) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -240,8 +217,8 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} // Publish it } installerJar { + canBeDeclared = false canBeResolved = false - canBeConsumed = true attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) @@ -256,8 +233,8 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} } universalJar { + canBeDeclared = false canBeResolved = false - canBeConsumed = true attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME)) @@ -269,8 +246,8 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} } changelog { + canBeDeclared = false canBeResolved = false - canBeConsumed = true attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION)) attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, "changelog")) @@ -281,9 +258,9 @@ configurations { } } modDevApiElements { + canBeDeclared = false canBeResolved = false - canBeConsumed = true - extendsFrom userdevCompileOnly, installerLibraries, moduleOnly + extendsFrom libraries, moduleLibraries, userdevCompileOnly, neoFormDependencies attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -295,11 +272,9 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} } modDevRuntimeElements { + canBeDeclared = false canBeResolved = false - canBeConsumed = true - afterEvaluate { - extendsFrom installerLibraries, moduleOnly - } + extendsFrom libraries, moduleLibraries, neoFormDependencies attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -311,11 +286,9 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} } modDevModulePath { + canBeDeclared = false canBeResolved = false - canBeConsumed = true - afterEvaluate { - extendsFrom moduleOnly - } + extendsFrom moduleLibraries attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -326,8 +299,9 @@ configurations { javaComponent.addVariantsFromConfiguration(it) {} } modDevTestFixtures { + canBeDeclared = false canBeResolved = false - canBeConsumed = true + extendsFrom userdevTestFixtures attributes { attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY)) attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)) @@ -340,30 +314,6 @@ configurations { } } -dependencies { - modDevBundle("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { - capabilities { - requireCapability 'net.neoforged:neoform' - } - endorseStrictVersions() - } - modDevApiElements("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { - capabilities { - requireCapability 'net.neoforged:neoform-dependencies' - } - endorseStrictVersions() - } - modDevRuntimeElements("net.neoforged:neoform:${project.minecraft_version}-${project.neoform_version}") { - capabilities { - requireCapability 'net.neoforged:neoform-dependencies' - } - endorseStrictVersions() - } - modDevTestFixtures("net.neoforged.fancymodloader:junit-fml:${project.fancy_mod_loader_version}") { - endorseStrictVersions() - } -} - processResources { inputs.property("version", project.version) final version = project.version From 17ab9410f95a090d8bcf568f951028f3011d0a2e Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:32:21 +0100 Subject: [PATCH 17/39] Allow duplicate libraries in the installer profile to make sure they always match specified tool versions --- .../neodev/NeoDevConfigurations.java | 27 ++++++++++--------- .../net/neoforged/neodev/NeoDevPlugin.java | 13 ++++++--- .../installer/CreateInstallerProfile.java | 22 ++++++++++++--- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index 602265f4fe..ba048dd121 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -56,6 +56,12 @@ static NeoDevConfigurations createAndSetup(Project project) { // Resolvable configurations. // + /** + * Resolved {@link #neoFormDataOnly}. + * This is used to add NeoForm to the installer libraries. + * Only the zip is used (for the mappings), not the NeoForm tools, so it's not transitive. + */ + final Configuration neoFormDataOnly; /** * Resolvable {@link #neoFormDependencies}. */ @@ -84,12 +90,6 @@ static NeoDevConfigurations createAndSetup(Project project) { * This is also used to produce the legacy classpath file for server installs. */ final Configuration launcherProfileClasspath; - /** - * Libraries that need to be downloaded and installed by the installer. - * This includes all dependencies added by NeoForge, the NeoForm archive, - * and all dependencies needed by the various tools that the installer runs. - */ - final Configuration installerProfileLibraries; // // The configurations for resolution only are declared in the build.gradle file. @@ -117,22 +117,27 @@ private NeoDevConfigurations(ConfigurationContainer configurations) { userdevCompileOnly = dependencyScope(configurations, "userdevCompileOnly"); userdevTestFixtures = dependencyScope(configurations, "userdevTestFixtures"); + neoFormDataOnly = resolvable(configurations, "neoFormDataOnly"); neoFormClasspath = resolvable(configurations, "neoFormClasspath"); modulePath = resolvable(configurations, "modulePath"); userdevClasspath = resolvable(configurations, "userdevClasspath"); userdevCompileOnlyClasspath = resolvable(configurations, "userdevCompileOnlyClasspath"); userdevTestClasspath = resolvable(configurations, "userdevTestClasspath"); launcherProfileClasspath = resolvable(configurations, "launcherProfileClasspath"); - installerProfileLibraries = resolvable(configurations, "installerProfileLibraries"); // Libraries & module libraries & MC dependencies need to be available when compiling in NeoDev, // and on the runtime classpath too for IDE debugging support. configurations.getByName("implementation").extendsFrom(libraries, moduleLibraries, neoFormDependencies); - // runtimeClasspath is our reference for all dependency versions. - // Make sure that any configuration we resolve are consistent with it. + // runtimeClasspath is our reference for all MC dependency versions. + // Make sure that any classpath we resolve is consistent with it. var runtimeClasspath = configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME); + neoFormDataOnly.setTransitive(false); + neoFormDataOnly.extendsFrom(neoFormData); + + neoFormClasspath.extendsFrom(neoFormDependencies); + modulePath.extendsFrom(moduleLibraries); modulePath.shouldResolveConsistentlyWith(runtimeClasspath); @@ -147,9 +152,5 @@ private NeoDevConfigurations(ConfigurationContainer configurations) { launcherProfileClasspath.extendsFrom(libraries, moduleLibraries); launcherProfileClasspath.shouldResolveConsistentlyWith(runtimeClasspath); - - installerProfileLibraries.extendsFrom(libraries, moduleLibraries, neoFormData); - installerProfileLibraries.shouldResolveConsistentlyWith(runtimeClasspath); - // Installer tools are also added to `installerProfileLibraries`, from NeoDevPlugin. } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 14908ca745..0a075bbab6 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -245,7 +245,11 @@ public void apply(Project project) { task.getNeoForgeVersion().set(neoForgeVersion); task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion); task.getIcon().set(project.getRootProject().file("docs/assets/neoforged.ico")); - task.setLibraries(configurations.installerProfileLibraries); + // Anything that is on the launcher classpath should be downloaded by the installer. + // (At least on the server side). + task.addLibraries(configurations.launcherProfileClasspath); + // We need the NeoForm zip for the SRG mappings. + task.addLibraries(configurations.neoFormDataOnly); task.getRepositoryURLs().set(project.provider(() -> { List repos = new ArrayList<>(); for (var repo : project.getRepositories().withType(MavenArtifactRepository.class)) { @@ -267,10 +271,11 @@ public void apply(Project project) { files.setCanBeResolved(true); files.getDependencies().add(installerProcessor.tool.asDependency(project)); }); - configurations.installerProfileLibraries.extendsFrom(configuration); - // Each tool should resolve consistently with the full set of installed libraries. - configuration.shouldResolveConsistentlyWith(configurations.installerProfileLibraries); createInstallerProfile.configure(task -> { + // Add installer processor. + // Different processors might use different versions of the same library, + // but that is fine because each processor gets its own classpath. + task.addLibraries(configuration); task.getProcessorClasspaths().put(installerProcessor, DependencyUtils.configurationToGavList(configuration)); task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project)); }); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index 5170acf524..171ed2b1d1 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -2,6 +2,7 @@ import com.google.gson.GsonBuilder; import net.neoforged.neodev.utils.FileUtils; +import net.neoforged.neodev.utils.MavenIdentifier; import org.gradle.api.DefaultTask; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.RegularFileProperty; @@ -46,8 +47,8 @@ public CreateInstallerProfile() {} @Nested protected abstract ListProperty getLibraryFiles(); - public void setLibraries(Configuration libraries) { - getLibraryFiles().set(IdentifiedFile.listFromConfiguration(getProject(), libraries)); + public void addLibraries(Configuration libraries) { + getLibraryFiles().addAll(IdentifiedFile.listFromConfiguration(getProject(), libraries)); } @Input @@ -134,8 +135,23 @@ public void createInstallerProfile() throws IOException { ); getLogger().info("Collecting libraries for Installer Profile"); + // Remove potential duplicates. + var libraryFilesToResolve = new LinkedHashMap(getLibraryFiles().get().size()); + for (var libraryFile : getLibraryFiles().get()) { + var existingFile = libraryFilesToResolve.putIfAbsent(libraryFile.getIdentifier().get(), libraryFile); + if (existingFile != null) { + var existing = existingFile.getFile().getAsFile().get(); + var duplicate = libraryFile.getFile().getAsFile().get(); + if (!existing.equals(duplicate)) { + throw new IllegalArgumentException("Cannot resolve installer profile! Library %s has different files: %s and %s.".formatted( + libraryFile.getIdentifier().get(), + existing, + duplicate)); + } + } + } var libraries = new ArrayList<>( - LibraryCollector.resolveLibraries(getRepositoryURLs().get(), getLibraryFiles().get())); + LibraryCollector.resolveLibraries(getRepositoryURLs().get(), libraryFilesToResolve.values())); var universalJar = getUniversalJar().getAsFile().get().toPath(); libraries.add(new Library( From 4c64c12da5fbe3d1534ddc53f2c09335c0bdfea4 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:17:06 +0100 Subject: [PATCH 18/39] Remove neoForm & its tools from userdev libraries --- .../java/net/neoforged/neodev/NeoDevConfigurations.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index ba048dd121..88ccfe1f4f 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -72,8 +72,8 @@ static NeoDevConfigurations createAndSetup(Project project) { final Configuration modulePath; /** * Userdev dependencies (written to a json file in the userdev jar). - * This should contain all of NeoForge's dependencies for userdev and NeoForm, - * but does not need to include all of Minecraft's libraries. + * This should contain all of NeoForge's additional dependencies for userdev, + * but does not need to include Minecraft or NeoForm's libraries. */ final Configuration userdevClasspath; /** @@ -141,7 +141,7 @@ private NeoDevConfigurations(ConfigurationContainer configurations) { modulePath.extendsFrom(moduleLibraries); modulePath.shouldResolveConsistentlyWith(runtimeClasspath); - userdevClasspath.extendsFrom(libraries, moduleLibraries, userdevCompileOnly, neoFormData); // TODO: is neoFormData necessary here? + userdevClasspath.extendsFrom(libraries, moduleLibraries, userdevCompileOnly); userdevClasspath.shouldResolveConsistentlyWith(runtimeClasspath); userdevCompileOnlyClasspath.extendsFrom(userdevCompileOnly); From d86cd3a9ec4c526fde86ccd78eb0c166411569c4 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:33:43 +0100 Subject: [PATCH 19/39] Remove a bit of whitespace diff --- projects/neoforge/build.gradle | 2 -- tests/build.gradle | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index 3ec98a622e..c2403faa1a 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -1,10 +1,8 @@ - import net.neoforged.jarcompatibilitychecker.gradle.JCCPlugin import net.neoforged.jarcompatibilitychecker.gradle.CompatibilityTask import net.neoforged.jarcompatibilitychecker.gradle.ProvideNeoForgeJarTask plugins { - id 'java-library' id 'maven-publish' id 'net.neoforged.jarcompatibilitychecker' version '0.1.12' diff --git a/tests/build.gradle b/tests/build.gradle index 9551414af0..748220980e 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -1,4 +1,3 @@ - plugins { id 'java' id 'com.diffplug.spotless' @@ -20,8 +19,7 @@ sourceSets { srcDir file('src/generated/resources') } } - junit { - } + junit {} } dependencies { From 5aae136a63e24babb144ec6dade50d6a05919ca9 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Fri, 15 Nov 2024 14:44:43 +0100 Subject: [PATCH 20/39] Minor fixes --- buildSrc/README.md | 7 +++++-- .../java/net/neoforged/neodev/NeoDevConfigurations.java | 2 +- .../src/main/java/net/neoforged/neodev/NeoDevPlugin.java | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/buildSrc/README.md b/buildSrc/README.md index c1fe25b726..3734384e66 100644 --- a/buildSrc/README.md +++ b/buildSrc/README.md @@ -36,11 +36,14 @@ Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin). The primary plugin of this repository sets up the `neoforge` project. +#### Setup + It creates a `setup` task that performs the following actions via various subtasks: - Decompile Minecraft using the [NeoForm Runtime](https://github.com/neoforged/neoformruntime) and Minecraft version specific [NeoForm data](https://github.com/neoforged/NeoForm). -- Apply the [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently. -- +- Applies [Access Transformers](../src/main/resources/META-INF/accesstransformer.cfg) to Minecraft sources. +- Applies [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently. +- Finally it unpacks the patched sources to `projects/neoforge/src/main/java`. ### NeoDevExtraPlugin diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index 88ccfe1f4f..b25f559557 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -57,7 +57,7 @@ static NeoDevConfigurations createAndSetup(Project project) { // /** - * Resolved {@link #neoFormDataOnly}. + * Resolved {@link #neoFormData}. * This is used to add NeoForm to the installer libraries. * Only the zip is used (for the mappings), not the NeoForm tools, so it's not transitive. */ diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 0a075bbab6..941c6f82cf 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -57,7 +57,7 @@ public void apply(Project project) { // 1. Obtain decompiled Minecraft sources jar using NeoForm. var createSourceArtifacts = configureMinecraftDecompilation(project); - // 2. Apply AT to the jar from 1. + // 2. Apply AT to the source jar from 1. var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); var applyAt = configureAccessTransformer( project, @@ -66,7 +66,7 @@ public void apply(Project project) { neoDevBuildDir, atFile); - // 3. Apply patches to the jar from 2. + // 3. Apply patches to the source jar from 2. var patchesFolder = project.getRootProject().file("patches"); var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> { task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); @@ -75,7 +75,7 @@ public void apply(Project project) { task.getRejectsFolder().set(project.getRootProject().file("rejects")); }); - // 4. Decompile jar from 3. + // 4. Unpack jar from 3. var mcSourcesPath = project.file("src/main/java"); tasks.register("setup", Sync.class, task -> { task.from(project.zipTree(applyPatches.flatMap(ApplyPatches::getPatchedJar))); From 90878711c9c21fd71e6b44f14711d4718e2fd573 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Sat, 16 Nov 2024 18:11:21 +0100 Subject: [PATCH 21/39] Remove modules from BSL ignore list (newer BSL is smarter) --- .../main/java/net/neoforged/neodev/NeoDevPlugin.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 941c6f82cf..3ee5c92f93 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -82,11 +82,6 @@ public void apply(Project project) { task.into(mcSourcesPath); }); - // Modules are on the classpath too but should be ignored by BootstrapLauncher - var modulePathIgnoreList = configurations.modulePath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { - return results.stream().map(r -> r.getFile().getName()).toList(); - }); - // 1. Write configs that contain the runs in a format understood by MDG/NG/etc. Currently one for neodev and one for userdev. var writeNeoDevConfig = tasks.register("writeNeoDevConfig", WriteUserDevConfig.class, task -> { task.getForNeoDev().set(true); @@ -106,7 +101,6 @@ public void apply(Project project) { task.getModules().addAll(DependencyUtils.configurationToGavList(configurations.modulePath)); task.getTestLibraries().addAll(DependencyUtils.configurationToGavList(configurations.userdevTestClasspath)); task.getTestLibraries().add(neoForgeVersion.map(v -> "net.neoforged:testframework:" + v)); - task.getIgnoreList().addAll(modulePathIgnoreList); task.getIgnoreList().addAll(configurations.userdevCompileOnlyClasspath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { return results.stream().map(r -> r.getFile().getName()).toList(); })); @@ -233,7 +227,6 @@ public void apply(Project project) { } return repos; })); - task.getIgnoreList().addAll(modulePathIgnoreList); task.getIgnoreList().addAll("client-extra", "neoforge-"); task.setModules(configurations.modulePath); task.getLauncherProfile().set(neoDevBuildDir.map(dir -> dir.file("launcher-profile.json"))); @@ -297,7 +290,7 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getIgnoreList().addAll(modulePathIgnoreList); + task.getIgnoreList().set(List.of()); task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar)); }); } @@ -318,7 +311,6 @@ public void apply(Project project) { task.setMetadataCharset("UTF-8"); task.getDestinationDirectory().convention(project.getExtensions().getByType(BasePluginExtension.class).getLibsDirectory()); - // TODO: is this correct? task.from(project.zipTree(project.provider(installerConfig::getSingleFile)), spec -> { spec.exclude("big_logo.png"); }); From 39fd703c8c09e3b220c582e72dfe2c9a352a45b0 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 17 Nov 2024 01:54:18 +0100 Subject: [PATCH 22/39] Refactor Tool Classpaths into NeoDevConfigurations --- .../neodev/NeoDevConfigurations.java | 49 ++++++++++++++++++- .../net/neoforged/neodev/NeoDevPlugin.java | 47 +++++------------- .../main/java/net/neoforged/neodev/Tools.java | 27 +++++----- 3 files changed, 74 insertions(+), 49 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index b25f559557..49f293bd18 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -5,6 +5,10 @@ import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.plugins.JavaPlugin; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + /** * Helper class to keep track of many {@link Configuration}s used for the {@code neoforge} project. * @@ -12,7 +16,7 @@ */ class NeoDevConfigurations { static NeoDevConfigurations createAndSetup(Project project) { - return new NeoDevConfigurations(project.getConfigurations()); + return new NeoDevConfigurations(project); } // @@ -91,6 +95,11 @@ static NeoDevConfigurations createAndSetup(Project project) { */ final Configuration launcherProfileClasspath; + /** + * To download each executable tool, we use a separate resolvable configuration. + */ + final Map toolClasspaths; + // // The configurations for resolution only are declared in the build.gradle file. // @@ -109,7 +118,9 @@ private static Configuration resolvable(ConfigurationContainer configurations, S }); } - private NeoDevConfigurations(ConfigurationContainer configurations) { + private NeoDevConfigurations(Project project) { + var configurations = project.getConfigurations(); + neoFormData = dependencyScope(configurations, "neoFormData"); neoFormDependencies = dependencyScope(configurations, "neoFormDependencies"); libraries = dependencyScope(configurations, "libraries"); @@ -152,5 +163,39 @@ private NeoDevConfigurations(ConfigurationContainer configurations) { launcherProfileClasspath.extendsFrom(libraries, moduleLibraries); launcherProfileClasspath.shouldResolveConsistentlyWith(runtimeClasspath); + + toolClasspaths = createToolClasspaths(project); + } + + private Map createToolClasspaths(Project project) { + var configurations = project.getConfigurations(); + var dependencyFactory = project.getDependencyFactory(); + + var result = new HashMap(); + + for (var tool : Tools.values()) { + var configuration = configurations.create(tool.getGradleConfigurationName(), spec -> { + spec.setDescription("Resolves the executable for tool " + tool.name()); + spec.setCanBeConsumed(false); + // Tools are considered to be executable jars. + // Gradle requires the classpath for JavaExec to only contain a single file for these. + spec.setTransitive(false); + + var gav = tool.asGav(project); + spec.getDependencies().add(dependencyFactory.create(gav)); + }); + result.put(tool, configuration); + } + + return Map.copyOf(result); + } + + /** + * Gets a configuration representing the classpath for an executable tool. + * Since executable tools are assumed to be executable jars, these configurations + * generally only contain a single file. + */ + public Configuration getExecutableTool(Tools tool) { + return Objects.requireNonNull(toolClasspaths.get(tool)); } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 3ee5c92f93..25fddb61e8 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -14,7 +14,6 @@ import net.neoforged.nfrtgradle.NeoFormRuntimeTask; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; @@ -61,7 +60,7 @@ public void apply(Project project) { var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); var applyAt = configureAccessTransformer( project, - configurations.neoFormClasspath, + configurations, createSourceArtifacts, neoDevBuildDir, atFile); @@ -204,6 +203,7 @@ public void apply(Project project) { var binaryPatchOutputs = configureBinaryPatchCreation( project, + configurations, createCleanArtifacts, neoDevBuildDir, patchesFolder @@ -259,11 +259,7 @@ public void apply(Project project) { }); for (var installerProcessor : InstallerProcessor.values()) { - var configuration = project.getConfigurations().create("installerProcessor" + installerProcessor.toString(), files -> { - files.setCanBeConsumed(false); - files.setCanBeResolved(true); - files.getDependencies().add(installerProcessor.tool.asDependency(project)); - }); + var configuration = project.getConfigurations().getByName(installerProcessor.tool.getGradleConfigurationName()); createInstallerProfile.configure(task -> { // Add installer processor. // Different processors might use different versions of the same library, @@ -295,14 +291,9 @@ public void apply(Project project) { }); } - var installerConfig = project.getConfigurations().create("legacyInstaller", files -> { - files.setCanBeConsumed(false); - files.setCanBeResolved(true); - files.setTransitive(false); - files.getDependencies().add(Tools.LEGACYINSTALLER.asDependency(project)); - }); + var installerConfig = configurations.getExecutableTool(Tools.LEGACYINSTALLER); // TODO: signing? - // We want to use the manifest from LegacyInstaller. + // We want to inherit the executable JAR manifest from LegacyInstaller. // - Jar tasks have special manifest handling, so use Zip. // - The manifest must be the first entry in the jar so LegacyInstaller has to be the first input. var installerJar = tasks.register("installerJar", Zip.class, task -> { @@ -394,39 +385,29 @@ public void apply(Project project) { private static TaskProvider configureAccessTransformer( Project project, - Configuration neoFormClasspath, + NeoDevConfigurations configurations, TaskProvider createSourceArtifacts, Provider neoDevBuildDir, File atFile) { - var jstConfiguration = project.getConfigurations().create("javaSourceTransformer", files -> { - files.setCanBeConsumed(false); - files.setCanBeResolved(true); - files.getDependencies().add(Tools.JST.asDependency(project)); - }); return project.getTasks().register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { - task.classpath(jstConfiguration); + task.classpath(configurations.getExecutableTool(Tools.JST)); task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); task.getAccessTransformer().set(atFile); task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar"))); - task.getLibraries().from(neoFormClasspath); + task.getLibraries().from(configurations.neoFormClasspath); task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt"))); }); } private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, + NeoDevConfigurations configurations, TaskProvider createCleanArtifacts, Provider neoDevBuildDir, File sourcesPatchesFolder) { - var configurations = project.getConfigurations(); var tasks = project.getTasks(); - var artConfig = configurations.create("art", spec -> { - spec.setDescription("Used to resolve the jar remapping tool"); - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.getDependencies().add(Tools.AUTO_RENAMING_TOOL.asDependency(project)); - }); + var artConfig = configurations.getExecutableTool(Tools.AUTO_RENAMING_TOOL); var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> { task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client."); task.classpath(artConfig); @@ -444,13 +425,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar"))); }); - var binpatcherConfig = configurations.create("binpatcher", spec -> { - spec.setDescription("Used to resolve the tool for creating binary patches"); - spec.setCanBeConsumed(false); - spec.setCanBeResolved(true); - spec.setTransitive(false); - spec.getDependencies().add(Tools.BINPATCHER.asDependency(project)); - }); + var binpatcherConfig = configurations.getExecutableTool(Tools.BINPATCHER); var generateMergedBinPatches = tasks.register("generateMergedBinPatches", GenerateBinaryPatches.class, task -> { task.setDescription("Creates binary patch files by diffing a merged client/server jar-file and the compiled Minecraft classes in this project."); task.classpath(binpatcherConfig); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java index 8a4ad2d2e7..082ea8286f 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -4,19 +4,28 @@ import org.gradle.api.artifacts.Dependency; public enum Tools { - JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version"), - LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version"), - AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version"), - INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version"), - JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version"), - BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version"); + JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath"), + LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath"), + AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath"), + INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath"), + JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath"), + BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath"); private final String gavPattern; private final String versionProperty; + private final String gradleConfigurationName; - Tools(String gavPattern, String versionProperty) { + Tools(String gavPattern, String versionProperty, String gradleConfigurationName) { this.gavPattern = gavPattern; this.versionProperty = versionProperty; + this.gradleConfigurationName = gradleConfigurationName; + } + + /** + * The name of the Gradle {@link org.gradle.api.artifacts.Configuration} used to resolve this particular tool. + */ + public String getGradleConfigurationName() { + return gradleConfigurationName; } public String asGav(Project project) { @@ -26,8 +35,4 @@ public String asGav(Project project) { } return gavPattern.formatted(version); } - - public Dependency asDependency(Project project) { - return project.getDependencyFactory().create(asGav(project)); - } } From b689ae55f4b249f28b31b4801486e19236c098ab Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 17 Nov 2024 02:28:46 +0100 Subject: [PATCH 23/39] Task Groups --- .../neoforged/neodev/NeoDevBasePlugin.java | 1 + .../net/neoforged/neodev/NeoDevPlugin.java | 28 +++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java index 9a5c382b4b..7680fffa9f 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevBasePlugin.java @@ -15,6 +15,7 @@ public void apply(Project project) { var createSources = NeoDevPlugin.configureMinecraftDecompilation(project); project.getTasks().register("setup", Sync.class, task -> { + task.setGroup(NeoDevPlugin.GROUP); task.setDescription("Replaces the contents of the base project sources with the unpatched, decompiled Minecraft source code."); task.from(project.zipTree(createSources.flatMap(CreateMinecraftArtifacts::getSourcesArtifact))); task.into(project.file("src/main/java/")); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 25fddb61e8..1dcf13bc17 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -33,6 +33,9 @@ import java.util.Map; public class NeoDevPlugin implements Plugin { + static final String GROUP = "neoforge development"; + static final String INTERNAL_GROUP = "neoforge development/internal"; + @Override public void apply(Project project) { project.getPlugins().apply(MinecraftDependenciesPlugin.class); @@ -68,6 +71,7 @@ public void apply(Project project) { // 3. Apply patches to the source jar from 2. var patchesFolder = project.getRootProject().file("patches"); var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); task.getPatchesFolder().set(patchesFolder); task.getPatchedJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/patched-sources.jar"))); @@ -77,6 +81,7 @@ public void apply(Project project) { // 4. Unpack jar from 3. var mcSourcesPath = project.file("src/main/java"); tasks.register("setup", Sync.class, task -> { + task.setGroup(GROUP); task.from(project.zipTree(applyPatches.flatMap(ApplyPatches::getPatchedJar))); task.into(mcSourcesPath); }); @@ -92,6 +97,7 @@ public void apply(Project project) { }); for (var taskProvider : List.of(writeNeoDevConfig, writeUserDevConfig)) { taskProvider.configure(task -> { + task.setGroup(INTERNAL_GROUP); task.getFmlVersion().set(fmlVersion); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); @@ -110,6 +116,7 @@ public void apply(Project project) { // 2. Task to download assets. var downloadAssets = tasks.register("downloadAssets", DownloadAssets.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(v -> "net.neoforged:neoform:" + v + "@zip")); task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); }); @@ -136,6 +143,7 @@ public void apply(Project project) { }, downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) ); + // TODO: Gradle run tasks should be moved to gradle group GROUP /* * OTHER TASKS @@ -143,13 +151,15 @@ public void apply(Project project) { // Generate source patches into a patch archive. var genSourcePatches = tasks.register("generateSourcePatches", GenerateSourcePatches.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getOriginalJar().set(applyAt.flatMap(ApplyAccessTransformer::getOutputJar)); task.getModifiedSources().set(project.file("src/main/java")); task.getPatchesJar().set(neoDevBuildDir.map(dir -> dir.file("source-patches.zip"))); }); // Update the patch/ folder with the current patches. - var genPatches = tasks.register("genPatches", Sync.class, task -> { + tasks.register("genPatches", Sync.class, task -> { + task.setGroup(GROUP); task.from(project.zipTree(genSourcePatches.flatMap(GenerateSourcePatches::getPatchesJar))); task.into(project.getRootProject().file("patches")); }); @@ -157,6 +167,7 @@ public void apply(Project project) { // Universal jar = the jar that contains NeoForge classes // TODO: signing? var universalJar = tasks.register("universalJar", Jar.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getArchiveClassifier().set("universal"); task.from(project.zipTree( @@ -189,9 +200,11 @@ public void apply(Project project) { }); var jarJarTask = JarJar.registerWithConfiguration(project, "jarJar"); + jarJarTask.configure(task -> task.setGroup(INTERNAL_GROUP)); universalJar.configure(task -> task.from(jarJarTask)); var createCleanArtifacts = tasks.register("createCleanArtifacts", CreateCleanArtifacts.class, task -> { + task.setGroup(INTERNAL_GROUP); var cleanArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts/clean")); task.getCleanClientJar().set(cleanArtifactsDir.map(dir -> dir.file("client.jar"))); task.getRawServerJar().set(cleanArtifactsDir.map(dir -> dir.file("raw-server.jar"))); @@ -211,6 +224,7 @@ public void apply(Project project) { // Launcher profile = the version.json file used by the Minecraft launcher. var createLauncherProfile = tasks.register("createLauncherProfile", CreateLauncherProfile.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getFmlVersion().set(fmlVersion); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); @@ -234,6 +248,7 @@ public void apply(Project project) { // Installer profile = the .json file used by the NeoForge installer. var createInstallerProfile = tasks.register("createInstallerProfile", CreateInstallerProfile.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getMcAndNeoFormVersion().set(mcAndNeoFormVersion); @@ -281,6 +296,7 @@ public void apply(Project project) { for (var taskProvider : List.of(createWindowsServerArgsFile, createUnixServerArgsFile)) { taskProvider.configure(task -> { + task.setGroup(INTERNAL_GROUP); task.getTemplate().set(project.getRootProject().file("server_files/args.txt")); task.getFmlVersion().set(fmlVersion); task.getMinecraftVersion().set(minecraftVersion); @@ -297,6 +313,7 @@ public void apply(Project project) { // - Jar tasks have special manifest handling, so use Zip. // - The manifest must be the first entry in the jar so LegacyInstaller has to be the first input. var installerJar = tasks.register("installerJar", Zip.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getArchiveClassifier().set("installer"); task.getArchiveExtension().set("jar"); task.setMetadataCharset("UTF-8"); @@ -351,6 +368,7 @@ public void apply(Project project) { }); var userdevJar = tasks.register("userdevJar", Jar.class, task -> { + task.setGroup(INTERNAL_GROUP); task.getArchiveClassifier().set("userdev"); task.from(writeUserDevConfig.flatMap(WriteUserDevConfig::getUserDevConfig), spec -> { @@ -368,7 +386,7 @@ public void apply(Project project) { }); project.getExtensions().getByType(JavaPluginExtension.class).withSourcesJar(); - final TaskProvider sourcesJarProvider = project.getTasks().named("sourcesJar", Jar.class); + var sourcesJarProvider = project.getTasks().named("sourcesJar", Jar.class); sourcesJarProvider.configure(task -> { task.exclude("net/minecraft/**"); task.exclude("com/**"); @@ -391,6 +409,7 @@ private static TaskProvider configureAccessTransformer( File atFile) { return project.getTasks().register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { + task.setGroup(INTERNAL_GROUP); task.classpath(configurations.getExecutableTool(Tools.JST)); task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); task.getAccessTransformer().set(atFile); @@ -409,6 +428,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, var artConfig = configurations.getExecutableTool(Tools.AUTO_RENAMING_TOOL); var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> { + task.setGroup(INTERNAL_GROUP); task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client."); task.classpath(artConfig); task.getMainClass().set("net.neoforged.art.Main"); @@ -417,6 +437,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar"))); }); var remapServerJar = tasks.register("remapServerJar", RemapJar.class, task -> { + task.setGroup(INTERNAL_GROUP); task.setDescription("Creates a Minecraft dedicated server jar with the official mappings applied. Used as the base for generating binary patches for the client."); task.classpath(artConfig); task.getMainClass().set("net.neoforged.art.Main"); @@ -427,6 +448,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, var binpatcherConfig = configurations.getExecutableTool(Tools.BINPATCHER); var generateMergedBinPatches = tasks.register("generateMergedBinPatches", GenerateBinaryPatches.class, task -> { + task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged client/server jar-file and the compiled Minecraft classes in this project."); task.classpath(binpatcherConfig); task.getCleanJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanJoinedJar)); @@ -436,6 +458,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("merged-binpatches.lzma"))); }); var generateClientBinPatches = tasks.register("generateClientBinPatches", GenerateBinaryPatches.class, task -> { + task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged client jar-file and the compiled Minecraft classes in this project."); task.classpath(binpatcherConfig); task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getMojmapJar)); @@ -445,6 +468,7 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("client-binpatches.lzma"))); }); var generateServerBinPatches = tasks.register("generateServerBinPatches", GenerateBinaryPatches.class, task -> { + task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged server jar-file and the compiled Minecraft classes in this project."); task.classpath(binpatcherConfig); task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getMojmapJar)); From f9d096a0ef53d759fd9d96459984bd7f9c7f9ac0 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 17 Nov 2024 18:44:29 +0100 Subject: [PATCH 24/39] Slight refactor for configurations --- .../neodev/NeoDevConfigurations.java | 4 +++- .../net/neoforged/neodev/NeoDevPlugin.java | 12 ++++------ .../main/java/net/neoforged/neodev/Tools.java | 24 +++++++++++++------ .../installer/CreateInstallerProfile.java | 4 ++-- .../installer/CreateLauncherProfile.java | 4 ++-- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index 49f293bd18..7fdc3e2edd 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -179,7 +179,9 @@ private Map createToolClasspaths(Project project) { spec.setCanBeConsumed(false); // Tools are considered to be executable jars. // Gradle requires the classpath for JavaExec to only contain a single file for these. - spec.setTransitive(false); + if (tool.isIgnoreTransitiveDependencies()) { + spec.setTransitive(false); + } var gav = tool.asGav(project); spec.getDependencies().add(dependencyFactory.create(gav)); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 1dcf13bc17..e540a9257e 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -271,19 +271,17 @@ public void apply(Project project) { })); task.getUniversalJar().set(universalJar.flatMap(AbstractArchiveTask::getArchiveFile)); task.getInstallerProfile().set(neoDevBuildDir.map(dir -> dir.file("installer-profile.json"))); - }); - for (var installerProcessor : InstallerProcessor.values()) { - var configuration = project.getConfigurations().getByName(installerProcessor.tool.getGradleConfigurationName()); - createInstallerProfile.configure(task -> { - // Add installer processor. + // Make all installer processor tools available to the profile + for (var installerProcessor : InstallerProcessor.values()) { + var configuration = configurations.getExecutableTool(installerProcessor.tool); // Different processors might use different versions of the same library, // but that is fine because each processor gets its own classpath. task.addLibraries(configuration); task.getProcessorClasspaths().put(installerProcessor, DependencyUtils.configurationToGavList(configuration)); task.getProcessorGavs().put(installerProcessor, installerProcessor.tool.asGav(project)); - }); - } + } + }); var createWindowsServerArgsFile = tasks.register("createWindowsServerArgsFile", CreateArgsFile.class, task -> { task.setLibraries(";", configurations.launcherProfileClasspath, configurations.modulePath); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java index 082ea8286f..ae70dfeb11 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -4,21 +4,23 @@ import org.gradle.api.artifacts.Dependency; public enum Tools { - JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath"), - LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath"), - AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath"), - INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath"), - JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath"), - BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath"); + JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", false), + LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath", false), + AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath", false), + INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", false), + JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", false), + BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath", false); private final String gavPattern; private final String versionProperty; private final String gradleConfigurationName; + private final boolean ignoreTransitiveDependencies; - Tools(String gavPattern, String versionProperty, String gradleConfigurationName) { + Tools(String gavPattern, String versionProperty, String gradleConfigurationName, boolean ignoreTransitiveDependencies) { this.gavPattern = gavPattern; this.versionProperty = versionProperty; this.gradleConfigurationName = gradleConfigurationName; + this.ignoreTransitiveDependencies = ignoreTransitiveDependencies; } /** @@ -28,6 +30,14 @@ public String getGradleConfigurationName() { return gradleConfigurationName; } + /** + * Some tools may be incorrectly packaged and declare transitive dependencies even for their "fatjar" variants. + * Gradle will not run these, so we ignore them. + */ + public boolean isIgnoreTransitiveDependencies() { + return ignoreTransitiveDependencies; + } + public String asGav(Project project) { var version = project.property(versionProperty); if (version == null) { diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index 171ed2b1d1..a7b7af89e9 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -187,8 +187,8 @@ public void createInstallerProfile() throws IOException { FileUtils.writeStringSafe( getInstallerProfile().getAsFile().get().toPath(), new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile), - // TODO: Not sure what this should be? Most likely the file is ASCII. - StandardCharsets.UTF_8); + StandardCharsets.UTF_8 + ); } } diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index a72d078c22..c934353a30 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -112,8 +112,8 @@ public void createLauncherProfile() throws IOException { FileUtils.writeStringSafe( getLauncherProfile().getAsFile().get().toPath(), new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(profile), - // TODO: Not sure what this should be? Most likely the file is ASCII. - StandardCharsets.UTF_8); + StandardCharsets.UTF_8 + ); } } From be62f5739b79478dd133903e721f1801b4abc0b4 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Sun, 17 Nov 2024 22:36:51 +0100 Subject: [PATCH 25/39] More docs --- buildSrc/README.md | 23 +++++++++++++++++-- .../neodev/ApplyAccessTransformer.java | 6 +++++ .../net/neoforged/neodev/ApplyPatches.java | 5 ++++ ...evConfig.java => CreateUserDevConfig.java} | 13 +++++++++-- .../neoforged/neodev/NeoDevExtraPlugin.java | 2 +- .../net/neoforged/neodev/NeoDevPlugin.java | 6 ++--- .../java/net/neoforged/neodev/RemapJar.java | 9 ++++++++ .../neodev/installer/CreateArgsFile.java | 3 +++ .../installer/CreateInstallerProfile.java | 5 ++++ .../installer/CreateLauncherProfile.java | 3 +++ .../neodev/installer/InstallerProcessor.java | 3 +++ .../neoforged/neodev/installer/Library.java | 4 +--- 12 files changed, 71 insertions(+), 11 deletions(-) rename buildSrc/src/main/java/net/neoforged/neodev/{WriteUserDevConfig.java => CreateUserDevConfig.java} (92%) diff --git a/buildSrc/README.md b/buildSrc/README.md index 3734384e66..2e63a427c0 100644 --- a/buildSrc/README.md +++ b/buildSrc/README.md @@ -34,7 +34,7 @@ Sources: [NeoDevPlugin.java](src/main/java/net/neoforged/neodev/NeoDevPlugin.jav Implicitly applies: [MinecraftDependenciesPlugin](#minecraftdependenciesplugin). -The primary plugin of this repository sets up the `neoforge` project. +This is the primary of this repository and is used to configure the `neoforge` subproject. #### Setup @@ -43,7 +43,26 @@ It creates a `setup` task that performs the following actions via various subtas - Decompile Minecraft using the [NeoForm Runtime](https://github.com/neoforged/neoformruntime) and Minecraft version specific [NeoForm data](https://github.com/neoforged/NeoForm). - Applies [Access Transformers](../src/main/resources/META-INF/accesstransformer.cfg) to Minecraft sources. - Applies [NeoForge patches](../patches) to Minecraft sources. Any rejects are saved to the `/rejects` folder in the repository for manual inspection. During updates to new versions, the task can be run with `-Pupdating=true` to apply patches more leniently. -- Finally it unpacks the patched sources to `projects/neoforge/src/main/java`. +- Unpacks the patched sources to `projects/neoforge/src/main/java`. + +#### Config Generation + +The plugin creates and configures the tasks to create various configuration files used downstream to develop +mods with this version of NeoForge ([CreateUserDevConfig](src/main/java/net/neoforged/neodev/CreateUserDevConfig.java)), or install it ([CreateInstallerProfile](src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java) and [CreateLauncherProfile](src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java)). + +A separate userdev profile is created for use by other subprojects in this repository. +The only difference is that it uses the FML launch types ending in `dev` rather than `userdev`. + +#### Patch Generation + +NeoForge injects its hooks into Minecraft by patching the decompiled source code. +Changes are made locally to the decompiled and patched source. +Since that source cannot be published, patches need to be generated before checking in. +The plugin configures the necessary task to do this +([GenerateSourcePatches](src/main/java/net/neoforged/neodev/GenerateSourcePatches.java)). + +The source patches are only used during development of NeoForge itself and development of mods that use Gradle plugins implementing the decompile/patch/recompile pipeline. +For use by the installer intended for players as well as Gradle plugins wanting to replicate the production artifacts more closely, binary patches are generated using the ([GenerateBinaryPatches](src/main/java/net/neoforged/neodev/GenerateBinaryPatches.java)) task. ### NeoDevExtraPlugin diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java index a0a5433b3e..c17120b51e 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java @@ -16,6 +16,12 @@ import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; +/** + * Runs JavaSourceTransformer to apply + * access transformers to the Minecraft source code for extending the access level of existing classes/methods/etc. + *

+ * Note that at runtime, FML also applies access transformers. + */ abstract class ApplyAccessTransformer extends JavaExec { @InputFile public abstract RegularFileProperty getInputJar(); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java index 58ed2fc31b..037e5f9bf6 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyPatches.java @@ -22,6 +22,11 @@ import javax.inject.Inject; import java.io.IOException; +/** + * Applies Java source patches to a source jar and produces a patched source jar as an output. + * It can optionally store rejected hunks into a given folder, which is primarily used for updating + * when the original sources changed and some hunks are expected to fail. + */ @DisableCachingByDefault(because = "Not worth caching") abstract class ApplyPatches extends DefaultTask { @InputFile diff --git a/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java b/buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java similarity index 92% rename from buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java rename to buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java index 51edf284aa..ab24c91d4d 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/WriteUserDevConfig.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/CreateUserDevConfig.java @@ -19,10 +19,19 @@ import java.util.List; import java.util.Map; -abstract class WriteUserDevConfig extends DefaultTask { +/** + * Creates the userdev configuration file used by the various Gradle plugins used to develop + * mods for NeoForge, such as Architectury Loom, + * ModDevGradle + * or NeoGradle. + */ +abstract class CreateUserDevConfig extends DefaultTask { @Inject - public WriteUserDevConfig() {} + public CreateUserDevConfig() {} + /** + * Toggles the launch type written to the userdev configuration between *dev and *userdev. + */ @Input abstract Property getForNeoDev(); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java index c33cc346bf..5573f09e73 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -34,7 +34,7 @@ public void apply(Project project) { // TODO: this is temporary var downloadAssets = neoForgeProject.getTasks().named("downloadAssets", DownloadAssets.class); var createArtifacts = neoForgeProject.getTasks().named("createSourceArtifacts", CreateMinecraftArtifacts.class); - var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", WriteUserDevConfig.class); + var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", CreateUserDevConfig.class); Consumer configureLegacyClasspath = spec -> { spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-dependencies")); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index e540a9257e..81c60fb904 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -87,11 +87,11 @@ public void apply(Project project) { }); // 1. Write configs that contain the runs in a format understood by MDG/NG/etc. Currently one for neodev and one for userdev. - var writeNeoDevConfig = tasks.register("writeNeoDevConfig", WriteUserDevConfig.class, task -> { + var writeNeoDevConfig = tasks.register("writeNeoDevConfig", CreateUserDevConfig.class, task -> { task.getForNeoDev().set(true); task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("neodev-config.json"))); }); - var writeUserDevConfig = tasks.register("writeUserDevConfig", WriteUserDevConfig.class, task -> { + var writeUserDevConfig = tasks.register("writeUserDevConfig", CreateUserDevConfig.class, task -> { task.getForNeoDev().set(false); task.getUserDevConfig().set(neoDevBuildDir.map(dir -> dir.file("userdev-config.json"))); }); @@ -369,7 +369,7 @@ public void apply(Project project) { task.setGroup(INTERNAL_GROUP); task.getArchiveClassifier().set("userdev"); - task.from(writeUserDevConfig.flatMap(WriteUserDevConfig::getUserDevConfig), spec -> { + task.from(writeUserDevConfig.flatMap(CreateUserDevConfig::getUserDevConfig), spec -> { spec.rename(s -> "config.json"); }); task.from(atFile, spec -> { diff --git a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java index d9f3d943be..0c235db979 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java @@ -12,6 +12,15 @@ import java.io.FileOutputStream; import java.io.IOException; +/** + * Produces a remapped jar-file that has almost no other changes applied with the intent of being + * the base against which we {@link GenerateBinaryPatches generate binary patches}. + *

+ * The installer produces the same Jar file as this task does and then applies the patches against that. + *

+ * Any changes to the options used here have to be reflected in the {@link net.neoforged.neodev.installer.CreateInstallerProfile installer profile} + * and vice versa, to ensure the patches are generated against the same binary files as they are applied to later. + */ abstract class RemapJar extends JavaExec { @Inject public RemapJar() {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java index 1739aae1e8..6b403d5b94 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateArgsFile.java @@ -21,6 +21,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +/** + * Creates the JVM/program argument files used by the dedicated server launcher. + */ public abstract class CreateArgsFile extends DefaultTask { @Inject public CreateArgsFile() {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index a7b7af89e9..df6b8dcb42 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -28,6 +28,10 @@ import java.util.Map; import java.util.function.BiConsumer; +/** + * Creates the JSON profile used by legacyinstaller for installing the client into the vanilla launcher, + * or installing a dedicated server. + */ public abstract class CreateInstallerProfile extends DefaultTask { @Inject public CreateInstallerProfile() {} @@ -127,6 +131,7 @@ public void createInstallerProfile() throws IOException { serverProcessor.accept(InstallerProcessor.JARSPLITTER, List.of("--input", "{MC_UNPACKED}", "--slim", "{MC_SLIM}", "--extra", "{MC_EXTRA}", "--srg", "{MERGED_MAPPINGS}") ); + // Note that the options supplied here have to match the ones used in the RemapJar task used to generate the binary patches commonProcessor.accept(InstallerProcessor.FART, List.of("--input", "{MC_SLIM}", "--output", "{MC_SRG}", "--names", "{MERGED_MAPPINGS}", "--ann-fix", "--ids-fix", "--src-fix", "--record-fix") ); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index c934353a30..886df1aa3f 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -24,6 +24,9 @@ import java.util.List; import java.util.Map; +/** + * Creates the JSON file for running NeoForge via the Vanilla launcher. + */ public abstract class CreateLauncherProfile extends DefaultTask { @Inject public CreateLauncherProfile() {} diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java index e5c5b60b45..970fa6d1cb 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/InstallerProcessor.java @@ -2,6 +2,9 @@ import net.neoforged.neodev.Tools; +/** + * Identifies the tools used by the {@link InstallerProfile} to install NeoForge. + */ public enum InstallerProcessor { BINPATCHER(Tools.BINPATCHER), FART(Tools.AUTO_RENAMING_TOOL), diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java index d064dfa4da..46669967f4 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/Library.java @@ -1,5 +1,3 @@ package net.neoforged.neodev.installer; -record Library( - String name, - LibraryDownload downloads) {} +record Library(String name, LibraryDownload downloads) {} From 9e1672a7ebc512edaaf44a6db26122415ec60ecc Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Mon, 18 Nov 2024 01:26:28 +0100 Subject: [PATCH 26/39] Request tools with "shadowed" bundling if possible. --- .../neodev/NeoDevConfigurations.java | 7 ++++-- .../main/java/net/neoforged/neodev/Tools.java | 23 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index 7fdc3e2edd..0b0d631b69 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -3,6 +3,7 @@ import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.attributes.Bundling; import org.gradle.api.plugins.JavaPlugin; import java.util.HashMap; @@ -179,8 +180,10 @@ private Map createToolClasspaths(Project project) { spec.setCanBeConsumed(false); // Tools are considered to be executable jars. // Gradle requires the classpath for JavaExec to only contain a single file for these. - if (tool.isIgnoreTransitiveDependencies()) { - spec.setTransitive(false); + if (tool.isRequestFatJar()) { + spec.attributes(attr -> { + attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.getObjects().named(Bundling.class, Bundling.SHADOWED)); + }); } var gav = tool.asGav(project); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java index ae70dfeb11..f67412727d 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -1,26 +1,25 @@ package net.neoforged.neodev; import org.gradle.api.Project; -import org.gradle.api.artifacts.Dependency; public enum Tools { - JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", false), - LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath", false), - AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath", false), - INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", false), - JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", false), - BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath", false); + JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", true), + LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath", true), + AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath", true), + INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", true), + JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", true), + BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath", true); private final String gavPattern; private final String versionProperty; private final String gradleConfigurationName; - private final boolean ignoreTransitiveDependencies; + private final boolean requestFatJar; - Tools(String gavPattern, String versionProperty, String gradleConfigurationName, boolean ignoreTransitiveDependencies) { + Tools(String gavPattern, String versionProperty, String gradleConfigurationName, boolean requestFatJar) { this.gavPattern = gavPattern; this.versionProperty = versionProperty; this.gradleConfigurationName = gradleConfigurationName; - this.ignoreTransitiveDependencies = ignoreTransitiveDependencies; + this.requestFatJar = requestFatJar; } /** @@ -34,8 +33,8 @@ public String getGradleConfigurationName() { * Some tools may be incorrectly packaged and declare transitive dependencies even for their "fatjar" variants. * Gradle will not run these, so we ignore them. */ - public boolean isIgnoreTransitiveDependencies() { - return ignoreTransitiveDependencies; + public boolean isRequestFatJar() { + return requestFatJar; } public String asGav(Project project) { From 3323698f4d705f0715b577e530d6db184c731e5d Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:14:31 +0100 Subject: [PATCH 27/39] Minor comment fixes --- .../neoforged/neodev/NeoDevConfigurations.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index 0b0d631b69..d79e9af857 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -11,9 +11,7 @@ import java.util.Objects; /** - * Helper class to keep track of many {@link Configuration}s used for the {@code neoforge} project. - * - *

Self-contained configurations (e.g. used to resolve tools) are not included here. + * Helper class to keep track of the many {@link Configuration}s used for the {@code neoforge} project. */ class NeoDevConfigurations { static NeoDevConfigurations createAndSetup(Project project) { @@ -96,15 +94,16 @@ static NeoDevConfigurations createAndSetup(Project project) { */ final Configuration launcherProfileClasspath; - /** - * To download each executable tool, we use a separate resolvable configuration. - */ - final Map toolClasspaths; - // // The configurations for resolution only are declared in the build.gradle file. // + /** + * To download each executable tool, we use a resolvable configuration. + * These configurations support both declaration and resolution. + */ + final Map toolClasspaths; + private static Configuration dependencyScope(ConfigurationContainer configurations, String name) { return configurations.create(name, configuration -> { configuration.setCanBeConsumed(false); From 7d76110fefbaf5b701ab52cf8c602d3009654e0b Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:35:24 +0100 Subject: [PATCH 28/39] Deduplicate RemapJar and GenerateBinaryPatches configuration code --- .../net/neoforged/neodev/NeoDevPlugin.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 81c60fb904..5f20e93e1d 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -426,55 +426,49 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, var artConfig = configurations.getExecutableTool(Tools.AUTO_RENAMING_TOOL); var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> { - task.setGroup(INTERNAL_GROUP); task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client."); - task.classpath(artConfig); - task.getMainClass().set("net.neoforged.art.Main"); task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanClientJar)); - task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar"))); }); var remapServerJar = tasks.register("remapServerJar", RemapJar.class, task -> { - task.setGroup(INTERNAL_GROUP); task.setDescription("Creates a Minecraft dedicated server jar with the official mappings applied. Used as the base for generating binary patches for the client."); - task.classpath(artConfig); - task.getMainClass().set("net.neoforged.art.Main"); task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanServerJar)); - task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar"))); }); + for (var remapTask : List.of(remapClientJar, remapServerJar)) { + remapTask.configure(task -> { + task.setGroup(INTERNAL_GROUP); + task.classpath(artConfig); + task.getMainClass().set("net.neoforged.art.Main"); + task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + }); + } var binpatcherConfig = configurations.getExecutableTool(Tools.BINPATCHER); var generateMergedBinPatches = tasks.register("generateMergedBinPatches", GenerateBinaryPatches.class, task -> { - task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged client/server jar-file and the compiled Minecraft classes in this project."); - task.classpath(binpatcherConfig); task.getCleanJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanJoinedJar)); - task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); - task.getSourcePatchesFolder().set(sourcesPatchesFolder); - task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("merged-binpatches.lzma"))); }); var generateClientBinPatches = tasks.register("generateClientBinPatches", GenerateBinaryPatches.class, task -> { - task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged client jar-file and the compiled Minecraft classes in this project."); - task.classpath(binpatcherConfig); task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getMojmapJar)); - task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); - task.getSourcePatchesFolder().set(sourcesPatchesFolder); - task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("client-binpatches.lzma"))); }); var generateServerBinPatches = tasks.register("generateServerBinPatches", GenerateBinaryPatches.class, task -> { - task.setGroup(INTERNAL_GROUP); task.setDescription("Creates binary patch files by diffing a merged server jar-file and the compiled Minecraft classes in this project."); - task.classpath(binpatcherConfig); task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getMojmapJar)); - task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); - task.getSourcePatchesFolder().set(sourcesPatchesFolder); - task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("server-binpatches.lzma"))); }); + for (var generateBinPatchesTask : List.of(generateMergedBinPatches, generateClientBinPatches, generateServerBinPatches)) { + generateBinPatchesTask.configure(task -> { + task.setGroup(INTERNAL_GROUP); + task.classpath(binpatcherConfig); + task.getPatchedJar().set(tasks.named("jar", Jar.class).flatMap(Jar::getArchiveFile)); + task.getSourcePatchesFolder().set(sourcesPatchesFolder); + task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + }); + } return new BinaryPatchOutputs( generateMergedBinPatches.flatMap(GenerateBinaryPatches::getOutputFile), From 34b45017ba310ae9499817a8ae9feb0bca8a0a2d Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:32:00 +0100 Subject: [PATCH 29/39] Refactor tool transitiveness handling a little --- .../java/net/neoforged/neodev/NeoDevConfigurations.java | 5 ++--- buildSrc/src/main/java/net/neoforged/neodev/Tools.java | 9 +++++++-- .../neodev/installer/CreateInstallerProfile.java | 7 ++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java index d79e9af857..35eb5b570d 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevConfigurations.java @@ -167,7 +167,7 @@ private NeoDevConfigurations(Project project) { toolClasspaths = createToolClasspaths(project); } - private Map createToolClasspaths(Project project) { + private static Map createToolClasspaths(Project project) { var configurations = project.getConfigurations(); var dependencyFactory = project.getDependencyFactory(); @@ -196,8 +196,7 @@ private Map createToolClasspaths(Project project) { /** * Gets a configuration representing the classpath for an executable tool. - * Since executable tools are assumed to be executable jars, these configurations - * generally only contain a single file. + * Some tools are assumed to be executable jars, and their configurations only contain a single file. */ public Configuration getExecutableTool(Tools tool) { return Objects.requireNonNull(toolClasspaths.get(tool)); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java index f67412727d..b01d5a429c 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -3,11 +3,16 @@ import org.gradle.api.Project; public enum Tools { + // Fatjar jst-cli-bundle instead of jst-cli because publication of the latter is currently broken. JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", true), + // Fatjar because the contents are copy/pasted into the installer jar which must be standalone. LEGACYINSTALLER("net.neoforged:legacyinstaller:%s:shrunk", "legacyinstaller_version", "toolLegacyinstallerClasspath", true), + // Fatjar because the slim jar currently does not have the main class set in its manifest. AUTO_RENAMING_TOOL("net.neoforged:AutoRenamingTool:%s:all", "art_version", "toolAutoRenamingToolClasspath", true), - INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", true), - JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", true), + INSTALLERTOOLS("net.neoforged.installertools:installertools:%s", "installertools_version", "toolInstallertoolsClasspath", false), + JARSPLITTER("net.neoforged.installertools:jarsplitter:%s", "installertools_version", "toolJarsplitterClasspath", false), + // Fatjar because it was like that in the userdev json in the past. + // To reconsider, we need to get in touch with 3rd party plugin developers or wait for a BC window. BINPATCHER("net.neoforged.installertools:binarypatcher:%s:fatjar", "installertools_version", "toolBinpatcherClasspath", true); private final String gavPattern; diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java index df6b8dcb42..3b745f744a 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateInstallerProfile.java @@ -71,7 +71,12 @@ public void addLibraries(Configuration libraries) { public abstract RegularFileProperty getInstallerProfile(); private void addProcessor(List processors, @Nullable List sides, InstallerProcessor processor, List args) { - processors.add(new ProcessorEntry(sides, getProcessorGavs().get().get(processor), getProcessorClasspaths().get().get(processor), args)); + var classpath = getProcessorClasspaths().get().get(processor); + var mainJar = getProcessorGavs().get().get(processor); + if (!classpath.contains(mainJar)) { + throw new IllegalStateException("Processor %s is not included in its own classpath %s".formatted(mainJar, classpath)); + } + processors.add(new ProcessorEntry(sides, mainJar, classpath, args)); } @TaskAction From 2af9644118069d03167fc4491b651e15614c8ef5 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 18 Nov 2024 23:48:48 +0100 Subject: [PATCH 30/39] Add back module path to prod server ignoreList --- .../src/main/java/net/neoforged/neodev/NeoDevPlugin.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 5f20e93e1d..e1822dac44 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -300,7 +300,12 @@ public void apply(Project project) { task.getMinecraftVersion().set(minecraftVersion); task.getNeoForgeVersion().set(neoForgeVersion); task.getRawNeoFormVersion().set(rawNeoFormVersion); - task.getIgnoreList().set(List.of()); + // In theory, new BootstrapLauncher shouldn't need the module path in the ignore list anymore. + // However, in server installs libraries are passed as relative paths here. + // Module path detection doesn't currently work with relative paths (BootstrapLauncher #20). + task.getIgnoreList().set(configurations.modulePath.getIncoming().getArtifacts().getResolvedArtifacts().map(results -> { + return results.stream().map(r -> r.getFile().getName()).toList(); + })); task.getRawServerJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getRawServerJar)); }); } From 15cfcaf0135b2964973383496069f3991b4ece89 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Wed, 20 Nov 2024 22:51:37 +0100 Subject: [PATCH 31/39] A few easy comments --- .../src/main/java/net/neoforged/neodev/NeoDevPlugin.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index e1822dac44..68df1280ee 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -47,7 +47,7 @@ public void apply(Project project) { var rawNeoFormVersion = project.getProviders().gradleProperty("neoform_version"); var fmlVersion = project.getProviders().gradleProperty("fancy_mod_loader_version"); var minecraftVersion = project.getProviders().gradleProperty("minecraft_version"); - var neoForgeVersion = project.provider(() -> (String) project.getVersion()); // TODO: is this correct? + var neoForgeVersion = project.provider(() -> project.getVersion().toString()); var mcAndNeoFormVersion = minecraftVersion.zip(rawNeoFormVersion, (mc, nf) -> mc + "-" + nf); var extension = project.getExtensions().create(NeoDevExtension.NAME, NeoDevExtension.class); @@ -178,6 +178,7 @@ public void apply(Project project) { task.manifest(manifest -> { manifest.attributes(Map.of("FML-System-Mods", "neoforge")); + // These attributes are used from NeoForgeVersion.java to find the NF version without command line arguments. manifest.attributes( Map.of( "Specification-Title", "NeoForge", @@ -362,7 +363,8 @@ public void apply(Project project) { }); // This is true by default (see gradle.properties), and needs to be disabled explicitly when building (see release.yml). - if (project.getProperties().containsKey("neogradle.runtime.platform.installer.debug") && Boolean.parseBoolean(project.getProperties().get("neogradle.runtime.platform.installer.debug").toString())) { + String installerDebugProperty = "neogradle.runtime.platform.installer.debug"; + if (project.getProperties().containsKey(installerDebugProperty) && Boolean.parseBoolean(project.getProperties().get(installerDebugProperty).toString())) { task.from(universalJar.flatMap(AbstractArchiveTask::getArchiveFile), spec -> { spec.into(String.format("/maven/net/neoforged/neoforge/%s/", neoForgeVersion.get())); spec.rename(name -> String.format("neoforge-%s-universal.jar", neoForgeVersion.get())); @@ -444,7 +446,6 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, remapTask.configure(task -> { task.setGroup(INTERNAL_GROUP); task.classpath(artConfig); - task.getMainClass().set("net.neoforged.art.Main"); task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); }); } From 27142915af663c86d42a140dcb285b447e6e12cf Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:53:04 +0100 Subject: [PATCH 32/39] Make RemapJar property names more generic --- .../java/net/neoforged/neodev/NeoDevPlugin.java | 14 +++++++------- .../main/java/net/neoforged/neodev/RemapJar.java | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 68df1280ee..c4f20f339b 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -434,19 +434,19 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, var artConfig = configurations.getExecutableTool(Tools.AUTO_RENAMING_TOOL); var remapClientJar = tasks.register("remapClientJar", RemapJar.class, task -> { task.setDescription("Creates a Minecraft client jar with the official mappings applied. Used as the base for generating binary patches for the client."); - task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanClientJar)); - task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar"))); + task.getInputJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanClientJar)); + task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-client.jar"))); }); var remapServerJar = tasks.register("remapServerJar", RemapJar.class, task -> { task.setDescription("Creates a Minecraft dedicated server jar with the official mappings applied. Used as the base for generating binary patches for the client."); - task.getObfSlimJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanServerJar)); - task.getMojmapJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar"))); + task.getInputJar().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getCleanServerJar)); + task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("remapped-server.jar"))); }); for (var remapTask : List.of(remapClientJar, remapServerJar)) { remapTask.configure(task -> { task.setGroup(INTERNAL_GROUP); task.classpath(artConfig); - task.getMergedMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); + task.getMappings().set(createCleanArtifacts.flatMap(CreateCleanArtifacts::getMergedMappings)); }); } @@ -458,12 +458,12 @@ private static BinaryPatchOutputs configureBinaryPatchCreation(Project project, }); var generateClientBinPatches = tasks.register("generateClientBinPatches", GenerateBinaryPatches.class, task -> { task.setDescription("Creates binary patch files by diffing a merged client jar-file and the compiled Minecraft classes in this project."); - task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getMojmapJar)); + task.getCleanJar().set(remapClientJar.flatMap(RemapJar::getOutputJar)); task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("client-binpatches.lzma"))); }); var generateServerBinPatches = tasks.register("generateServerBinPatches", GenerateBinaryPatches.class, task -> { task.setDescription("Creates binary patch files by diffing a merged server jar-file and the compiled Minecraft classes in this project."); - task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getMojmapJar)); + task.getCleanJar().set(remapServerJar.flatMap(RemapJar::getOutputJar)); task.getOutputFile().set(neoDevBuildDir.map(dir -> dir.file("server-binpatches.lzma"))); }); for (var generateBinPatchesTask : List.of(generateMergedBinPatches, generateClientBinPatches, generateServerBinPatches)) { diff --git a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java index 0c235db979..e93ccb8b94 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/RemapJar.java @@ -26,19 +26,19 @@ abstract class RemapJar extends JavaExec { public RemapJar() {} @InputFile - abstract RegularFileProperty getObfSlimJar(); + abstract RegularFileProperty getInputJar(); @InputFile - abstract RegularFileProperty getMergedMappings(); + abstract RegularFileProperty getMappings(); @OutputFile - abstract RegularFileProperty getMojmapJar(); + abstract RegularFileProperty getOutputJar(); @Override public void exec() { - args("--input", getObfSlimJar().get().getAsFile().getAbsolutePath()); - args("--output", getMojmapJar().get().getAsFile().getAbsolutePath()); - args("--names", getMergedMappings().get().getAsFile().getAbsolutePath()); + args("--input", getInputJar().get().getAsFile().getAbsolutePath()); + args("--output", getOutputJar().get().getAsFile().getAbsolutePath()); + args("--names", getMappings().get().getAsFile().getAbsolutePath()); args("--ann-fix", "--ids-fix", "--src-fix", "--record-fix"); var logFile = new File(getTemporaryDir(), "console.log"); @@ -47,7 +47,7 @@ public void exec() { setStandardOutput(out); super.exec(); } catch (IOException e) { - throw new GradleException("Failed to create binary patches.", e); + throw new GradleException("Failed to remap jar.", e); } } } From 914445256c01ab23eb080af6eae626024991fcc4 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:47:18 +0100 Subject: [PATCH 33/39] Add back AT validation --- .../net/neoforged/neodev/ApplyAccessTransformer.java | 6 ++++++ .../main/java/net/neoforged/neodev/NeoDevPlugin.java | 3 +++ projects/neoforge/build.gradle | 12 ------------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java index c17120b51e..7131cc8fdd 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/ApplyAccessTransformer.java @@ -3,7 +3,9 @@ import net.neoforged.neodev.utils.FileUtils; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.JavaExec; @@ -29,6 +31,9 @@ abstract class ApplyAccessTransformer extends JavaExec { @InputFile public abstract RegularFileProperty getAccessTransformer(); + @Input + public abstract Property getValidate(); + @OutputFile public abstract RegularFileProperty getOutputJar(); @@ -57,6 +62,7 @@ public void exec() { args( "--enable-accesstransformers", "--access-transformer", getAccessTransformer().getAsFile().get().getAbsolutePath(), + "--access-transformer-validation", getValidate().get() ? "error" : "log", "--libraries-list", getLibrariesFile().getAsFile().get().getAbsolutePath(), getInputJar().getAsFile().get().getAbsolutePath(), getOutputJar().getAsFile().get().getAbsolutePath()); diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index c4f20f339b..297242a612 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -413,11 +413,14 @@ private static TaskProvider configureAccessTransformer( Provider neoDevBuildDir, File atFile) { + // Pass -PvalidateAccessTransformers to validate ATs. + var validateAts = project.getProviders().gradleProperty("validateAccessTransformers").map(p -> true).orElse(false); return project.getTasks().register("applyAccessTransformer", ApplyAccessTransformer.class, task -> { task.setGroup(INTERNAL_GROUP); task.classpath(configurations.getExecutableTool(Tools.JST)); task.getInputJar().set(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getSourcesArtifact)); task.getAccessTransformer().set(atFile); + task.getValidate().set(validateAts); task.getOutputJar().set(neoDevBuildDir.map(dir -> dir.file("artifacts/access-transformed-sources.jar"))); task.getLibraries().from(configurations.neoFormClasspath); task.getLibrariesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft-libraries-for-jst.txt"))); diff --git a/projects/neoforge/build.gradle b/projects/neoforge/build.gradle index c2403faa1a..91177ff691 100644 --- a/projects/neoforge/build.gradle +++ b/projects/neoforge/build.gradle @@ -52,18 +52,6 @@ checkJarCompatibility { baseJar = createCompatJar.flatMap { it.output } } -// TODO: re-enable AT validation -//tasks.configureEach { tsk -> -// if (tsk.name == 'neoFormApplyUserAccessTransformer' && project.hasProperty('validateAccessTransformers')) { -// tsk.inputs.property('validation', 'error') -// tsk.logLevel('ERROR') -// tsk.doFirst { -// tsk.getRuntimeProgramArguments().addAll(tsk.getRuntimeProgramArguments().get()) -// tsk.getRuntimeProgramArguments().add('--access-transformer-validation=error') -// } -// } -//} - neoDev { mods { minecraft { From 0d893b68e840afb03a812259a02aae4b9903abd6 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:53:13 +0100 Subject: [PATCH 34/39] Bump JUnit dependency to match fml-junit --- gradle.properties | 2 +- tests/build.gradle | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index 3d624b021e..cffa9262c6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -52,7 +52,7 @@ nashorn_core_version=15.3 lwjgl_glfw_version=3.3.2 mixin_extras_version=0.4.1 -jupiter_api_version=5.7.0 +jupiter_api_version=5.10.2 vintage_engine_version=5.+ assertj_core=3.25.1 diff --git a/tests/build.gradle b/tests/build.gradle index 748220980e..1a528210c6 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -37,8 +37,6 @@ dependencies { compileOnly "org.jetbrains:annotations:${project.jetbrains_annotations_version}" } -// Required to fix compile classpath using an older junit version. Alternatively we could also just bump junit. -configurations.junitCompileClasspath.shouldResolveConsistentlyWith configurations.junitRuntimeClasspath junitTest { useJUnitPlatform() @@ -106,7 +104,7 @@ neoDev { } neoDevTest { - loadedMods = [ project(":neoforge").neoDev.mods.minecraft, neoDev.mods.testframework, neoDev.mods.junit ] + loadedMods = [ project(":neoforge").neoDev.mods.minecraft, neoDev.mods.testframework, neoDev.mods.coremods, neoDev.mods.junit ] } license { From 5d2a23511d00ce8d3d476648488a6768094d5ee7 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:39:46 +0100 Subject: [PATCH 35/39] Add renovate comments for tool dependencies. Use + version for installer --- buildSrc/src/main/java/net/neoforged/neodev/Tools.java | 1 + gradle.properties | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java index b01d5a429c..a9f9106697 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/Tools.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/Tools.java @@ -2,6 +2,7 @@ import org.gradle.api.Project; +// If a GAV is changed, make sure to change the corresponding renovate comment in gradle.properties. public enum Tools { // Fatjar jst-cli-bundle instead of jst-cli because publication of the latter is currently broken. JST("net.neoforged.jst:jst-cli-bundle:%s", "jst_version", "toolJstClasspath", true), diff --git a/gradle.properties b/gradle.properties index cffa9262c6..43f48c57a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,9 @@ org.gradle.configuration-cache=true org.gradle.debug=false #org.gradle.warning.mode=fail +# renovate: net.neoforged:moddev-gradle moddevgradle_plugin_version=2.0.43-beta +# renovate: io.codechicken:DiffPatch diffpatch_version=2.0.0.35 java_version=21 @@ -18,11 +20,13 @@ neoform_version=20241023.131943 # on snapshot versions, used to prefix the version neoforge_snapshot_next_stable=21.4 +# renovate: net.neoforged.jst:jst-cli-bundle jst_version=1.0.45 -legacyinstaller_version=3.0.27 +legacyinstaller_version=3.0.+ +# renovate: net.neoforged:AutoRenamingTool art_version=2.0.3 +# renovate: net.neoforged.installertools:installertools installertools_version=2.1.2 -devlaunch_version=1.0.1 mergetool_version=2.0.0 accesstransformers_version=11.0.1 From e8f227543f11fe574778ccd5cd8ca0e4c8567aeb Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:29:11 +0100 Subject: [PATCH 36/39] Remove dynamic installer version --- gradle.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 43f48c57a9..7c33670a93 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,8 @@ neoforge_snapshot_next_stable=21.4 # renovate: net.neoforged.jst:jst-cli-bundle jst_version=1.0.45 -legacyinstaller_version=3.0.+ +# renovate: net.neoforged:legacyinstaller +legacyinstaller_version=3.0.33 # renovate: net.neoforged:AutoRenamingTool art_version=2.0.3 # renovate: net.neoforged.installertools:installertools From 4e4e33e5bfea697af5e6422e7b5f70a4928ac26e Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:34:50 +0100 Subject: [PATCH 37/39] Remove legacy mergeModules --- .../net/neoforged/neodev/installer/CreateLauncherProfile.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java index 886df1aa3f..5968190304 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/installer/CreateLauncherProfile.java @@ -83,8 +83,6 @@ public void createLauncherProfile() throws IOException { var jvmArguments = new ArrayList<>(List.of( "-Djava.net.preferIPv6Addresses=system", "-DignoreList=" + String.join(",", getIgnoreList().get()), - // TODO: is this still relevant in any way? - "-DmergeModules=jna-5.10.0.jar,jna-platform-5.10.0.jar", "-DlibraryDirectory=${library_directory}")); jvmArguments.add("-p"); From dad13e810c995e078dda90538e7ff7abf1413420 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:33:32 +0100 Subject: [PATCH 38/39] Revert "Remove dynamic installer version" This reverts commit e8f227543f11fe574778ccd5cd8ca0e4c8567aeb. --- gradle.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7c33670a93..43f48c57a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,8 +22,7 @@ neoforge_snapshot_next_stable=21.4 # renovate: net.neoforged.jst:jst-cli-bundle jst_version=1.0.45 -# renovate: net.neoforged:legacyinstaller -legacyinstaller_version=3.0.33 +legacyinstaller_version=3.0.+ # renovate: net.neoforged:AutoRenamingTool art_version=2.0.3 # renovate: net.neoforged.installertools:installertools From a71e50cabe457910873f013348ee534032b5ff85 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:52:50 +0100 Subject: [PATCH 39/39] Rename MC resources jar and move it to the normal classpath --- .../neoforged/neodev/NeoDevExtraPlugin.java | 7 ------- .../net/neoforged/neodev/NeoDevPlugin.java | 21 +++++++++++++------ gradle.properties | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java index 5573f09e73..912fd4bfe5 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevExtraPlugin.java @@ -33,17 +33,10 @@ public void apply(Project project) { // TODO: this is temporary var downloadAssets = neoForgeProject.getTasks().named("downloadAssets", DownloadAssets.class); - var createArtifacts = neoForgeProject.getTasks().named("createSourceArtifacts", CreateMinecraftArtifacts.class); var writeNeoDevConfig = neoForgeProject.getTasks().named("writeNeoDevConfig", CreateUserDevConfig.class); Consumer configureLegacyClasspath = spec -> { spec.getDependencies().add(projectDep(dependencyFactory, neoForgeProject, "net.neoforged:neoforge-dependencies")); - // TODO: Convert into a cross-project dependency too - spec.getDependencies().add( - dependencyFactory.create( - project.files(createArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) - ) - ); }; extension.getRuns().configureEach(run -> { diff --git a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java index 297242a612..e7f9f77934 100644 --- a/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java +++ b/buildSrc/src/main/java/net/neoforged/neodev/NeoDevPlugin.java @@ -18,6 +18,7 @@ import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Sync; @@ -58,6 +59,8 @@ public void apply(Project project) { */ // 1. Obtain decompiled Minecraft sources jar using NeoForm. var createSourceArtifacts = configureMinecraftDecompilation(project); + // Task must run on sync to have MC resources available for IDEA nondelegated builds. + NeoDevFacade.runTaskOnProjectSync(project, createSourceArtifacts); // 2. Apply AT to the source jar from 1. var atFile = project.getRootProject().file("src/main/resources/META-INF/accesstransformer.cfg"); @@ -86,6 +89,10 @@ public void apply(Project project) { task.into(mcSourcesPath); }); + /* + * RUNS SETUP + */ + // 1. Write configs that contain the runs in a format understood by MDG/NG/etc. Currently one for neodev and one for userdev. var writeNeoDevConfig = tasks.register("writeNeoDevConfig", CreateUserDevConfig.class, task -> { task.getForNeoDev().set(true); @@ -121,6 +128,13 @@ public void apply(Project project) { task.getAssetPropertiesFile().set(neoDevBuildDir.map(dir -> dir.file("minecraft_assets.properties"))); }); + // FML needs Minecraft resources on the classpath to find it. Add to runtimeOnly so subprojects also get it at runtime. + var runtimeClasspath = project.getConfigurations().getByName(JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME); + runtimeClasspath.getDependencies().add( + dependencyFactory.create( + project.files(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) + ) + ); // 3. Let MDG do the rest of the setup. :) NeoDevFacade.setupRuns( project, @@ -134,11 +148,6 @@ public void apply(Project project) { legacyClassPath.getDependencies().addLater(mcAndNeoFormVersion.map(v -> dependencyFactory.create("net.neoforged:neoform:" + v).capabilities(caps -> { caps.requireCapability("net.neoforged:neoform-dependencies"); }))); - legacyClassPath.getDependencies().add( - dependencyFactory.create( - project.files(createSourceArtifacts.flatMap(CreateMinecraftArtifacts::getResourcesArtifact)) - ) - ); legacyClassPath.extendsFrom(configurations.libraries, configurations.moduleLibraries, configurations.userdevCompileOnly); }, downloadAssets.flatMap(DownloadAssets::getAssetPropertiesFile) @@ -524,7 +533,7 @@ static TaskProvider configureMinecraftDecompilation(Pr return tasks.register("createSourceArtifacts", CreateMinecraftArtifacts.class, task -> { var minecraftArtifactsDir = neoDevBuildDir.map(dir -> dir.dir("artifacts")); task.getSourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("base-sources.jar"))); - task.getResourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("minecraft-local-resources-aka-client-extra.jar"))); + task.getResourcesArtifact().set(minecraftArtifactsDir.map(dir -> dir.file("minecraft-resources.jar"))); task.getNeoFormArtifact().set(mcAndNeoFormVersion.map(version -> "net.neoforged:neoform:" + version + "@zip")); }); } diff --git a/gradle.properties b/gradle.properties index 43f48c57a9..ef1a37e3b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ org.gradle.debug=false #org.gradle.warning.mode=fail # renovate: net.neoforged:moddev-gradle -moddevgradle_plugin_version=2.0.43-beta +moddevgradle_plugin_version=2.0.46-beta # renovate: io.codechicken:DiffPatch diffpatch_version=2.0.0.35