diff --git a/build.gradle b/build.gradle index 6d7f7fd..a771dae 100644 --- a/build.gradle +++ b/build.gradle @@ -89,11 +89,6 @@ gradlePlugin { tags = ['minecraftforge'] } - register('forge') { - id = 'net.minecraftforge.forge.build.forge' - implementationClass = 'net.minecraftforge.forgedev.legacy.ForgeBuildPlugin' - } - register('convention') { id = 'net.minecraftforge.forge.build.convention' implementationClass = 'net.minecraftforge.forgedev.legacy.SharedBuildPlugin' diff --git a/settings.gradle b/settings.gradle index 0bdd055..adc899c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,7 +20,7 @@ gradle.beforeProject { Project project -> //@formatter:off dependencyResolutionManagement.versionCatalogs.register('libs') { - version 'gradleutils', '3.4.4' + version 'gradleutils', '3.4.5' plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0' plugin 'gradleutils', 'net.minecraftforge.gradleutils' versionRef 'gradleutils' @@ -41,6 +41,9 @@ dependencyResolutionManagement.versionCatalogs.register('libs') { // Plugins library 'gradle-download', 'de.undercouch.download', 'de.undercouch.download.gradle.plugin' version '5.6.0' + // Access to git info, namely finding out version for the validation tasks + library 'jgit', 'org.eclipse.jgit:org.eclipse.jgit:7.6.0.202603022253-r' + version 'asm', '9.8' library 'asm', 'org.ow2.asm', 'asm' versionRef 'asm' library 'asm-tree', 'org.ow2.asm', 'asm-tree' versionRef 'asm' diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CleanProperties.java b/src/main/groovy/net/minecraftforge/forgedev/CleanProperties.java similarity index 98% rename from src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CleanProperties.java rename to src/main/groovy/net/minecraftforge/forgedev/CleanProperties.java index 2add1c9..0c6ec6f 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CleanProperties.java +++ b/src/main/groovy/net/minecraftforge/forgedev/CleanProperties.java @@ -2,7 +2,7 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev.legacy.tasks; +package net.minecraftforge.forgedev; import java.io.ByteArrayOutputStream; import java.io.File; diff --git a/src/main/groovy/net/minecraftforge/forgedev/Constants.java b/src/main/groovy/net/minecraftforge/forgedev/Constants.java index fac8b04..98a7be7 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/Constants.java +++ b/src/main/groovy/net/minecraftforge/forgedev/Constants.java @@ -4,10 +4,11 @@ */ package net.minecraftforge.forgedev; -/// The package-private constants used throughout ForgeGradle. -final class Constants { +/// The constants used throughout ForgeGradle. +public final class Constants { private Constants() { } - static final String FORGE_MAVEN = "https://maven.minecraftforge.net/"; - static final String MAVEN_CENTRAL = "https://repo1.maven.org/maven2/"; + public static final String FORGE_MAVEN = "https://maven.minecraftforge.net/"; + public static final String FORGE_FILES = "https://files.minecraftforge.net/"; + public static final String MAVEN_CENTRAL = "https://repo1.maven.org/maven2/"; } diff --git a/src/main/groovy/net/minecraftforge/forgedev/ForgeDevExtension.java b/src/main/groovy/net/minecraftforge/forgedev/ForgeDevExtension.java index c65a91d..71de5ca 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/ForgeDevExtension.java +++ b/src/main/groovy/net/minecraftforge/forgedev/ForgeDevExtension.java @@ -4,66 +4,56 @@ */ package net.minecraftforge.forgedev; -import net.minecraftforge.forgedev.legacy.tasks.Util; -import net.minecraftforge.forgedev.legacy.values.CIRuntime; +import net.minecraftforge.forgedev.base.MCPBase; +import net.minecraftforge.forgedev.base.MCPBaseImpl; +import net.minecraftforge.forgedev.values.CIRuntime; +import net.minecraftforge.forgedev.patches.BinaryPatches; +import net.minecraftforge.forgedev.patches.BinaryPatchesImpl; +import net.minecraftforge.forgedev.patches.Patches; +import net.minecraftforge.forgedev.patches.PatchesImpl; +import net.minecraftforge.forgedev.publishvalidate.ValidatePublish; +import net.minecraftforge.forgedev.runs.Run; +import net.minecraftforge.forgedev.tasks.DownloadHashedFile; import net.minecraftforge.forgedev.tasks.MethodCallFinder; import net.minecraftforge.forgedev.tasks.ValidateDeprecations; import net.minecraftforge.forgedev.tasks.checks.Checks; -import net.minecraftforge.forgedev.tasks.compat.LegacyExtractZip; -import net.minecraftforge.forgedev.tasks.compat.LegacyMergeFilesTask; -import net.minecraftforge.forgedev.tasks.filtering.LegacyFilterNewJar; -import net.minecraftforge.forgedev.tasks.generation.GeneratePatcherConfigV2; +import net.minecraftforge.forgedev.tasks.compat.UserdevCompatibility; +import net.minecraftforge.forgedev.tasks.compat.UserdevCompatibilityImpl; import net.minecraftforge.forgedev.tasks.installer.Installer; -import net.minecraftforge.forgedev.tasks.installertools.DownloadMappings; -import net.minecraftforge.forgedev.tasks.launcher.SlimeLauncherExec; -import net.minecraftforge.forgedev.tasks.mappings.LegacyApplyMappings; -import net.minecraftforge.forgedev.tasks.mappings.LegacyGenerateSRG; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerMCPDataTask; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerMCPMavenValue; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerMCPSetup; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerRawArtifact; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerSyncMappingsValue; -import net.minecraftforge.forgedev.tasks.obfuscation.LegacyReobfuscateJar; -import net.minecraftforge.forgedev.tasks.patching.binary.CreateBinPatches; -import net.minecraftforge.forgedev.tasks.patching.diff.ApplyPatches; -import net.minecraftforge.forgedev.tasks.patching.diff.BakePatches; -import net.minecraftforge.forgedev.tasks.patching.diff.GeneratePatches; -import net.minecraftforge.forgedev.tasks.sas.CreateFakeSASPatches; import net.minecraftforge.forgedev.tasks.shim.Shim; -import net.minecraftforge.forgedev.tasks.srg2source.ApplyRangeMap; -import net.minecraftforge.forgedev.tasks.srg2source.ExtractRangeMap; -import net.minecraftforge.gradleutils.shared.Closures; +import net.minecraftforge.forgedev.tasks.userdev.UserDev; +import net.minecraftforge.forgedev.values.GitVersionValueSource; +import net.minecraftforge.forgedev.values.MavenArtifact; +import net.minecraftforge.forgedev.values.MinecraftFiles; import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.ExternalModuleDependency; -import org.gradle.api.artifacts.ModuleIdentifier; +import org.gradle.api.Task; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; import org.gradle.api.attributes.Attribute; -import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileTreeElement; import org.gradle.api.file.ProjectLayout; import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; -import org.gradle.api.tasks.SourceSet; +import org.gradle.api.specs.Spec; 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 org.gradle.api.tasks.compile.JavaCompile; import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.jetbrains.annotations.VisibleForTesting; +import org.jspecify.annotations.Nullable; import javax.inject.Inject; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.util.List; -import java.util.function.Function; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.zip.ZipFile; // TODO [ForgeDev] Hide this and make a public interface @VisibleForTesting @@ -82,11 +72,13 @@ public abstract class ForgeDevExtension { protected abstract @Inject ProviderFactory getProviders(); protected abstract @Inject ProjectLayout getProjectLayout(); + private final ForgeDevPlugin plugin; private final Project project; private final Provider isCi; @Inject public ForgeDevExtension(ForgeDevPlugin plugin, Project project) { + this.plugin = plugin; this.mavenizerRepo.set(plugin.globalCaches().dir("repo").map(this.problems.ensureFileLocation())); this.project = project; this.isCi = getProviders().of(CIRuntime.class, it -> {}); @@ -110,12 +102,218 @@ public boolean isCi() { return this.isCi.get(); } - private Configuration minecraftDepsConfiguration; - public Configuration getMinecraftConfiguration() { - return this.minecraftDepsConfiguration; + // Couple helpers + public MavenArtifact getMavenArtifact() { + return mavenArtifact(this.project); } + public ForgeDevProblems getProblems() { + return this.problems; + } + public MavenArtifact mavenArtifact(Project project) { + return MavenArtifact.from(project); + } + public MavenArtifact mavenArtifact(String descriptor) { + return MavenArtifact.from(this.getObjects(), descriptor); + } + public Provider mavenArtifact(Provider provider) { + return MavenArtifact.from(this.getObjects(), provider); + } + public ForgeDevPlugin getPlugin() { + return this.plugin; + } + + // Replaces the need for the FilterNew tasks, we just want to remove the vanilla classes from our jar + public Spec filterVanilla(Provider vanilla) { + var blacklist = new HashSet(); + try (var zip = new ZipFile(vanilla.get())) { + for (var itr = zip.entries().asIterator(); itr.hasNext(); ) { + var entry = itr.next(); + blacklist.add(entry.getName()); + } + } catch (IOException e) { + return Util.sneak(e); + } + return cfg -> { + if (cfg.isDirectory()) + return true; + + var path = cfg.getRelativePath().getPathString(); + if (blacklist.contains(path)) + return true; + + if (!path.endsWith(".class")) + return false; + + int idx = path.lastIndexOf('$'); + while (idx != -1) { + if (blacklist.contains(path.substring(0, idx) + ".class")) + return true; + idx = path.lastIndexOf('$', idx - 1); + } + + return false; + }; + } + // Tasks creation helpers + // region Crowdin ====================================================== + // Downloads the crowdin zip, which is created nightly by a cron job + // ===================================================================== + private @Nullable TaskProvider downloadCrowdin; + public TaskProvider getCrowdin() { + if (this.downloadCrowdin == null) { + this.downloadCrowdin = project.getTasks().register("downloadCrowdin", DownloadHashedFile.class); + this.downloadCrowdin.configure(task -> { + task.setDescription("Download crowdin based translations"); + task.getSrc().set("https://files.minecraftforge.net/crowdin.zip"); + }); + } + return this.downloadCrowdin; + } + public TaskProvider crowdin(Action action) { + getCrowdin().configure(action); + return getCrowdin(); + } + // endregion =========================================================== + + // region Run Configs ================================================== + // Adds genEclipseRuns task and SlimeLauncherExec task for each run config + // ===================================================================== + private @Nullable TaskProvider genEclipseRuns; + public TaskProvider getGenEclipseRuns() { + if (this.genEclipseRuns == null) { + genEclipseRuns = project.getTasks().register("genEclipseRuns", task -> { + task.setGroup("IDE"); + task.setDescription("Generates the run configuration launch files for Eclipse."); + }); + } + return this.genEclipseRuns; + } + + private NamedDomainObjectContainer runs; + public NamedDomainObjectContainer getRuns() { + if (this.runs == null) { + this.runs = this.getObjects().domainObjectContainer(Run.class, name -> + getObjects().newInstance(Run.class, name, this.project, getGenEclipseRuns(), getMcpBase().getMcpVersion().get()) + ); + } + return this.runs; + } + public NamedDomainObjectContainer runs(Action> action) { + action.execute(getRuns()); + return getRuns(); + } + // endregion =========================================================== + + // region Validate Artifacts =========================================== + // Publish validation task, the intention of this is to provide a diff of what this version and the last released version + // actually publish, so that we can know what actually changes between vesions. + // Mainly this is for validating converting to ForgeDev from ForgeGradle + // ===================================================================== + private ValidatePublish.@Nullable Consumer validatePublish = null; + public ValidatePublish.Consumer getValidatePublish() { + if (validatePublish == null) + validatePublish = ValidatePublish.registerConsumer(this.project, this); + return validatePublish; + } + public ValidatePublish.Consumer validatePublish(Action action) { + var ret = getValidatePublish(); + action.execute(ret); + return ret; + } + // endregion =========================================================== + + // region Version Numbers ============================================== + // Get version numbers for projects from git + // ===================================================================== + public Provider gitVersion() { + return gitVersion(Util.noop()); + } + public Provider gitVersion(Action action) { + return GitVersionValueSource.provider(this.getProviders(), this.plugin, action); + } + public Provider gitVersion(Project project) { + return gitVersion(project, Util.noop()); + } + public Provider gitVersion(Project project, Action action) { + return gitVersion(params -> { + params.getProjectDirectory().set(project.getLayout().getProjectDirectory()); + action.execute(params); + }); + } + public Provider gitVersion(Project project, String commit) { + return gitVersion(project, commit, Util.noop()); + } + public Provider gitVersion(Project project, String commit, Action action) { + return gitVersion(params -> { + params.getProjectDirectory().set(project.getLayout().getProjectDirectory()); + params.getCommit().set(commit); + action.execute(params); + }); + } + // endregion =========================================================== + + // region Vanilla Files ================================================ + // Access to vanilla files, such as the client/server jar, version json + // ===================================================================== + private final Map minecraftFiles = new HashMap<>(); + public MinecraftFiles minecraftFiles(String version) { + var ret = this.minecraftFiles.get(version); + if (ret == null) { + ret = this.getObjects().newInstance(MinecraftFiles.class, this.project, this.plugin, version); + this.minecraftFiles.put(version, ret); + } + return ret; + } + // endregion =========================================================== + + // region Patcher Base ================================================= + // This is the 'base' of a patcher project. It should provide the 'clean' + // jar that we generate our patches against. + // This should also provide basically anything we need from the 'base' + // MCPConfig setup. Libraries, Mappings, client-extra, + // ===================================================================== + private MCPBaseImpl base; + public MCPBase getMcpBase() { + if (this.base == null) + return getObjects().newInstance(MCPBaseImpl.class, this.project, this.plugin, "mcpbase"); + return base; + } + public MCPBase mcpBase(Action action) { + var ret = this.getMcpBase(); + action.execute(ret); + return ret; + } + // endregion =========================================================== + + // region Patches ====================================================== + // This is for configuring how we patch raw source files. + // Typically, we only have one but might as well support multiple. + // ===================================================================== + private final Map patches = new HashMap<>(); + public Patches getPatches(String name) { + var ret = this.patches.get(name); + if (ret == null) { + ret = getObjects().newInstance(PatchesImpl.class, this, name, this.project); + this.patches.put(name, ret); + } + return ret; + } + public Patches getPatches() { + return getPatches("default"); + } + public Patches patches(String name, Action action) { + var ret = this.getPatches(name); + action.execute(ret); + return ret; + } + public Patches patches(Action action) { + return patches("default", action); + } + // endregion =========================================================== + + // region Installer ==================================================== // ===================================================================== public Installer installer() { return installer(Installer.DEFAULT_NAME); @@ -131,9 +329,9 @@ public Installer installer(String name, Action action) { action.execute(ret); return ret; } - // ===================================================================== + // endregion =========================================================== - // ===================================================================== + // region Checks ======================================================= // 'Check' and 'Fix' tasks, Registers the same task twice with a 'fix' // boolean input. Registers the 'check{Name}' version to the 'check' group // and the 'checkAndFix{Name}' task to the 'checkAndFix' group. @@ -149,8 +347,10 @@ public Checks checks(Action action) { action.execute(ret); return ret; } - // =============================================================================== + // endregion =========================================================== + // region Shim Dedicated Server Luncher ================================ + // ===================================================================== public Shim shim() { return shim(Util.noop()); } @@ -165,14 +365,36 @@ public Shim shim(String name, Action action) { action.execute(ret); return ret; } - // =============================================================================== + // endregion =========================================================== + + // region Bytecode Finder ============================================== public TaskProvider methodCallFinder(String name) { return methodCallFinder(name, Util.noop()); } public TaskProvider methodCallFinder(String name, Action action) { return this.project.getTasks().register(name, MethodCallFinder.class, action); } - // =============================================================================== + // endregion =========================================================== + + // region UserDev ====================================================== + // ===================================================================== + private @Nullable UserDev userDev; + public UserDev getUserDev() { + if (userDev == null) + this.userDev = this.getObjects().newInstance(UserDev.class, "userdev", this.project, this); + return userDev; + } + public UserDev userdev(Action action) { + action.execute(getUserDev()); + return getUserDev(); + } + // endregion =========================================================== + + // region Published Artifacts Validation =============================== + // Validates the published artifacts for the entire project tree. + // This is basically the gradelized version of our 'dump.sh' script + // Not Fully implemented + // ===================================================================== public TaskProvider validateDeprecations() { return validateDeprecations(this.project.getTasks().named("jar", Jar.class)); } @@ -181,195 +403,48 @@ public TaskProvider validateDeprecations(TaskProvider { task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); task.getInput().set(jar.flatMap(AbstractArchiveTask::getArchiveFile)); - var patcher = this.project.getExtensions().findByType(LegacyPatcherExtension.class); - if (patcher != null) - task.getMcVersion().set(patcher.getMcVersion()); }); tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME, check -> check.dependsOn(ret)); return ret; } - // =============================================================================== - - - + // endregion =========================================================== + + // region Compatibility checking ======================================= + // For now, this is just a creation helper so we don't have to reference the hard object and can it accepts groovy closures correctly + private final Map userdevCompatibility = new HashMap<>(); + public UserdevCompatibility userdevCompatibility(String minecraftVersion) { + var ret = this.userdevCompatibility.get(minecraftVersion); + if (ret == null) { + ret = getObjects().newInstance(UserdevCompatibilityImpl.class, this.project, minecraftVersion, userdevCompatibility.isEmpty()); + this.userdevCompatibility.put(minecraftVersion, ret); + } + return ret; + } + public UserdevCompatibility userdevCompatibility(String minecraftVersion, Action action) { + var ret = userdevCompatibility(minecraftVersion); + action.execute(ret); + return ret; + } + // endregion =========================================================== + + // region Binary Patches =============================================== + private final Map binaryPatches = new HashMap<>(); + public BinaryPatches binaryPatches(String name) { + var ret = this.binaryPatches.get(name); + if (ret == null) { + ret = getObjects().newInstance(BinaryPatchesImpl.class, name, this, this.project.getTasks()); + this.binaryPatches.put(name, ret); + } + return ret; + } + public BinaryPatches binaryPatches(String name, Action action) { + var ret = binaryPatches(name); + action.execute(ret); + return ret; + } + // endregion =========================================================== - @SuppressWarnings("removal") private void setup(ForgeDevPlugin plugin, Project project) { - var tasks = project.getTasks(); - - var legacyPatcher = project.getExtensions().create(LegacyPatcherExtension.EXTENSION_NAME, LegacyPatcherExtension.class); - var legacyMcp = project.getExtensions().create(LegacyMCPExtension.EXTENSION_NAME, LegacyMCPExtension.class, plugin); - var java = project.getExtensions().getByType(JavaPluginExtension.class); - - var jar = tasks.named(JavaPlugin.JAR_TASK_NAME, Jar.class); - var compileJava = tasks.named(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaCompile.class); - var main = java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME); - - // needs to exist because it's currently referenced in the buildscript - // TODO STOP DOING THAT SHIT - var setupMCP = tasks.register("setupMCP", MavenizerMCPSetup.class); - var setupMCPSrg = tasks.register("setupMCPSrg", MavenizerMCPSetup.class, task -> task.getRename().set(false)); - - var downloadClientMappings = tasks.register("downloadClientMappings", DownloadMappings.class, task -> task.getSide().set("client")); - var downloadServerMappings = tasks.register("downloadServerMappings", DownloadMappings.class, task -> task.getSide().set("server")); - - minecraftDepsConfiguration = project.getConfigurations().detachedConfiguration(); - var mappingsConfiguration = project.getConfigurations().detachedConfiguration(); - - var applyPatches = tasks.register("applyPatches", ApplyPatches.class, task -> { - final Provider workDir = project.getLayout().getBuildDirectory().dir(task.getName()); - task.getOutput().set(workDir.map(s -> s.file("output.zip"))); - task.getArchive().set("zip"); - task.setRejects(workDir.map(s -> s.file("rejects.zip"))); - task.getArchiveRejects().set("zip"); - task.getPatches().set(legacyPatcher.getPatches()); - task.getMode().set("access"); - if (project.hasProperty("UPDATING")) { - task.getMode().set("fuzzy"); - task.setRejects(project.getLayout().getProjectDirectory().dir("rejects")); - task.getArchiveRejects().unset(); - task.getFailOnError().set(false); - } - }); - - var toMCPConfig = tasks.register("srg2mcp", LegacyApplyMappings.class, task -> { - task.getInput().set(applyPatches.flatMap(ApplyPatches::getOutput)); - task.getMappings().setFrom(mappingsConfiguration); - task.getLambdas().set(false); - }); - var extractMapped = tasks.register("extractMapped", LegacyExtractZip.class, task -> { - task.getInput().set(toMCPConfig.flatMap(LegacyApplyMappings::getOutput)); - task.getOutput().set(legacyPatcher.getPatchedSrc()); - }); - - var extractRangeMap = tasks.register("extractRangeMap", ExtractRangeMap.class, task -> { - task.getDependencies().from(jar.flatMap(Jar::getArchiveFile)); - - // Only add main source, as we inject the patchedSrc into it as a sourceset. - task.getSources().from(main.map(s -> s.getJava().getSourceDirectories())); - task.getDependencies().from(compileJava.map(JavaCompile::getClasspath)); - }); - - var createMcp2Srg = tasks.register("createMcp2Srg", LegacyGenerateSRG.class, task -> { - task.getReverse().set(true); - task.getOutput().set(task.getOutputFile("mcp2srg.tsrg")); - }); - var createSrg2Mcp = tasks.register("createSrg2Mcp", LegacyGenerateSRG.class, task -> { - task.getReverse().set(false); - task.getOutput().set(task.getOutputFile("srg2mcp.tsrg")); - }); - var createMcp2Obf = tasks.register("createMcp2Obf", LegacyGenerateSRG.class, task -> { - task.getNotch().set(true); - task.getReverse().set(true); - task.getOutput().set(task.getOutputFile("mcp2obf.tsrg")); - }); - - // TODO DOES NOTHING! - var createExc = tasks.register("createExc"); - - var applyRangeMap = tasks.register("applyRangeMap", ApplyRangeMap.class, task -> { - task.getSources().from(main.map(s -> s.getJava().getSourceDirectories().minus(project.files(legacyPatcher.getPatchedSrc())))); - task.setOnlyIf(t -> !((ApplyRangeMap) t).getSources().isEmpty()); - task.getRangeMap().set(extractRangeMap.flatMap(ExtractRangeMap::getOutput)); - task.getSrgFiles().from(createMcp2Srg.flatMap(LegacyGenerateSRG::getOutput)); - task.getExcFiles().from(/*createExc.flatMap(CreateExc::getOutput), */legacyPatcher.getExcs()); - task.getKeepImports().set(true); - }); - - var applyRangeMapBase = tasks.register("applyRangeMapBase", ApplyRangeMap.class, task -> { - task.setOnlyIf(t -> legacyPatcher.getPatches().isPresent()); - task.getSources().from(legacyPatcher.getPatchedSrc()); - task.getRangeMap().set(extractRangeMap.flatMap(ExtractRangeMap::getOutput)); - task.getSrgFiles().from(createMcp2Srg.flatMap(LegacyGenerateSRG::getOutput)); - task.getExcFiles().from(/*createExc.flatMap(CreateExc::getOutput), */legacyPatcher.getExcs()); - task.getKeepImports().set(true); - }); - - var userdevConfig = tasks.register("userdevConfig", GeneratePatcherConfigV2.class); - - var genPatches = tasks.register("genPatches", GeneratePatches.class, task -> { - task.setOnlyIf(t -> legacyPatcher.getPatches().isPresent()); - task.getOutput().set(legacyPatcher.getPatches()); - }); - - var bakePatches = tasks.register("bakePatches", BakePatches.class, task -> { - task.dependsOn(genPatches); - task.getInput().set(legacyPatcher.getPatches()); - task.getOutput().set(new File(task.getTemporaryDir(), "output.zip")); - }); - - var reobfJar = tasks.register("reobfJar", LegacyReobfuscateJar.class, task -> { - task.getInput().set(jar.flatMap(Jar::getArchiveFile)); - task.getLibraries().from(minecraftDepsConfiguration); - task.getOutput().convention(task.getDefaultOutputFile()); - }); - - var genJoinedBinPatches = tasks.register("genJoinedBinPatches", CreateBinPatches.class, task -> { - task.getCreate().from(reobfJar.flatMap(LegacyReobfuscateJar::getOutput)); - task.getOutput().convention(project.getLayout().getBuildDirectory().dir(task.getName()).map(d -> d.file("joined.lzma"))); - }); - var genClientBinPatches = tasks.register("genClientBinPatches", CreateBinPatches.class, task -> { - task.getCreate().from(reobfJar.flatMap(LegacyReobfuscateJar::getOutput)); - task.getOutput().convention(project.getLayout().getBuildDirectory().dir(task.getName()).map(d -> d.file("client.lzma"))); - }); - var genServerBinPatches = tasks.register("genServerBinPatches", CreateBinPatches.class, task -> { - task.getCreate().from(reobfJar.flatMap(LegacyReobfuscateJar::getOutput)); - task.getOutput().convention(project.getLayout().getBuildDirectory().dir(task.getName()).map(d -> d.file("server.lzma"))); - }); - var genBinPatchesTasks = List.of(genJoinedBinPatches, genClientBinPatches, genServerBinPatches); - var genBinPatches = tasks.register("genBinPatches", task -> task.dependsOn(genJoinedBinPatches, genClientBinPatches, genServerBinPatches)); - - var filterNew = tasks.register("filterJarNew", LegacyFilterNewJar.class, task -> task.getInput().set(reobfJar.flatMap(LegacyReobfuscateJar::getOutput))); - - /* - * All sources in SRG names. - * patches in /patches/ - */ - // TODO This may conflict with normal sources jar if enabled - // Remember that we want to generalize ForgeDev to be used by both Forge and ForgeLoader - var srgSourcesJar = tasks.register("legacySourcesJar", Jar.class, task -> { - task.setGroup(LifecycleBasePlugin.BUILD_GROUP); - task.setOnlyIf(t -> applyRangeMap.flatMap(ApplyRangeMap::getOutput).map(rf -> rf.getAsFile().exists()).getOrElse(false)); - task.dependsOn(applyRangeMap); - task.from(project.zipTree(applyRangeMap.flatMap(ApplyRangeMap::getOutput))); - task.getArchiveClassifier().set("sources"); - }); - - /* Universal: - * All of our classes and resources as normal jar. - * Should only be OUR classes, not parent patcher projects. - */ - var universalJar = tasks.register("universalJar", Jar.class, task -> { - task.setGroup(LifecycleBasePlugin.BUILD_GROUP); - task.dependsOn(filterNew); - task.from(project.zipTree(filterNew.flatMap(LegacyFilterNewJar::getOutput))); - task.from(java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getResources)); - task.getArchiveClassifier().set("universal"); - }); - - /*UserDev: - * config.json - * joined.lzma - * sources.jar - * patches/ - * net/minecraft/item/Item.java.patch - * ats/ - * at1.cfg - * at2.cfg - */ - var userdevJar = tasks.register("userdevJar", Jar.class, task -> { - task.setGroup(LifecycleBasePlugin.BUILD_GROUP); - task.dependsOn(srgSourcesJar, bakePatches); - task.setOnlyIf(t -> legacyPatcher.isSrgPatches()); - task.from(userdevConfig.flatMap(GeneratePatcherConfigV2::getOutput), e -> e.rename(f -> "config.json")); - task.from(genJoinedBinPatches.flatMap(CreateBinPatches::getOutput), e -> e.rename(f -> "joined.lzma")); - task.from(project.zipTree(bakePatches.flatMap(BakePatches::getOutput)), e -> e.into("patches/")); - task.getArchiveClassifier().set("userdev"); - }); - var assemble = tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME, task -> - task.dependsOn(universalJar, userdevJar) - ); - var sourceSetsDir = this.getObjects().directoryProperty().value(this.getProjectLayout().getBuildDirectory().dir("sourceSets")); var mergeSourceSets = this.problems.test("net.minecraftforge.gradle.merge-source-sets"); project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().configureEach(sourceSet -> { @@ -388,275 +463,5 @@ private void setup(ForgeDevPlugin plugin, Project project) { System.out.println("WARNING: Source set will not be merged for " + sourceSet.getName() + "!"); }); }); - - project.afterEvaluate(p -> { - downloadClientMappings.configure(task -> task.getVersion().set(legacyPatcher.getMappingVersion())); - downloadServerMappings.configure(task -> task.getVersion().set(legacyPatcher.getMappingVersion())); - - // TODO Add mappings as a dependency to FG7??? - // Add mappings so that it can be used by reflection tools. - // net.minecraft:mappings_CHANNEL:VERSION@zip - var mappingsDependency = getProviders().zip( - getProviders().of(MavenizerSyncMappingsValue.class, spec -> spec.parameters(parameters -> { - parameters.init(plugin, problems); - parameters.getOutput().set(mavenizerRepo); - parameters.getVersion().set(legacyPatcher.getMappingVersion()); - })), - getProviders().zip(legacyPatcher.getMappingChannel(), legacyPatcher.getMappingVersion(), (c, v) -> c + ':' + v), - (ret, version) -> { - return project.getDependencies().create( - "net.minecraft:mappings_%s@zip".formatted(version) - ); - }); - var minecraftDependency = getProviders().zip( - getProviders().of(MavenizerMCPMavenValue.class, spec -> spec.parameters(parameters -> { - parameters.init(plugin, problems); - parameters.getOutput().set(mavenizerRepo); - parameters.getArtifact().set(legacyMcp.getVersion()); - })), - legacyMcp.getVersion(), - (ret, version) -> { - return project.getDependencies().create( - "net.minecraft:joined:" + version, - Closures.consumer(dependency -> { - dependency.attributes(a -> { - a.attributeProvider(OS, getProviders().of(OSValueSource.class, spec -> { })); - a.attributeProvider(MAPPINGS_CHANNEL, legacyPatcher.getMappingChannel()); - a.attributeProvider(MAPPINGS_VERSION, legacyPatcher.getMappingVersion()); - }); - }) - ); - } - ); - var minecraftExtraDependency = getProviders().zip( - getProviders().of(MavenizerMCPMavenValue.class, spec -> spec.parameters(parameters -> { - parameters.init(plugin, problems); - parameters.getOutput().set(mavenizerRepo); - parameters.getArtifact().set(legacyMcp.getVersion().map(v -> "net.minecraft:client-extra:" + v)); - })), - legacyMcp.getVersion(), - (ret, version) -> { - return project.getDependencies().create( - "net.minecraft:client-extra:" + version, - Closures.consumer(dependency -> dependency.setTransitive(false)) - ); - } - ); - //syncMavenizer.configure(task -> task.getArtifact().set(legacyMcp.getVersion())); - //syncMavenizerForExtra.configure(task -> task.getArtifact().set(legacyMcp.getVersion().map(v -> "net.minecraft:client-extra:" + v))); - //syncMappingsMaven.configure(task -> task.getVersion().set(legacyPatcher.getMappingVersion())); - project.getDependencies().addProvider(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, minecraftDependency); - project.getDependencies().addProvider(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, minecraftExtraDependency); - project.getDependencies().addProvider(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME, mappingsDependency); - minecraftDepsConfiguration.withDependencies(d -> d.addLater(minecraftDependency)); - mappingsConfiguration.withDependencies(d -> d.addLater(mappingsDependency)); - - // Add the patched source as a source dir during afterEvaluate, to not be overwritten by buildscripts - main.configure(s -> s.getJava().srcDir(legacyPatcher.getPatchedSrc())); - - // Automatically create the patches folder if it does not exist - if (legacyPatcher.getPatches().isPresent()) { - try { - Files.createDirectories(legacyPatcher.getPatches().get().getAsFile().toPath()); - } catch (IOException e) { - throw new RuntimeException("Failed to create patches folder", e); - } - srgSourcesJar.configure(task -> task.from(genPatches.flatMap(GeneratePatches::getOutput), copy -> copy.into("patches/"))); - } - - setupMCP.configure(task -> { - task.getPipeline().set(legacyMcp.getPipeline()); - task.getArtifact().set(legacyMcp.getConfig()); - }); - setupMCPSrg.configure(task -> { - task.getPipeline().set(legacyMcp.getPipeline()); - task.getArtifact().set(legacyMcp.getConfig()); - }); - legacyPatcher.getCleanSrc().set(setupMCP.flatMap(MavenizerMCPSetup::getOutput)); - applyPatches.configure(task -> task.getInput().convention(legacyPatcher.getCleanSrc())); - genPatches.configure(task -> task.getInput().convention(legacyPatcher.getCleanSrc())); - - var extractSrg = tasks.register("extractSrg", MavenizerMCPDataTask.class, task -> { - task.getArtifact().set(legacyMcp.getConfig()); - task.getOutput().convention(task.getOutputFile("obf2srg.tsrg")); - }); - createMcp2Srg.configure(task -> task.getMcpSrgData().convention(extractSrg.flatMap(MavenizerMCPDataTask::getOutput))); - - // This was actually filtering the PARENT jar file. Since we don't support parent Patchers anymore, this is not needed. - //filterNew.configure(task -> task.getBlacklist().from(jar.flatMap(AbstractArchiveTask::getArchiveFile))); - - tasks.withType(LegacyGenerateSRG.class, task -> { - task.getMappings().setFrom(mappingsConfiguration); - }); - - createMcp2Obf.configure(task -> task.getMcpSrgData().convention(createMcp2Srg.flatMap(LegacyGenerateSRG::getMcpSrgData))); - createSrg2Mcp.configure(task -> task.getMcpSrgData().convention(createMcp2Srg.flatMap(LegacyGenerateSRG::getMcpSrgData))); - - // TODO CLIENT EXTRA? - if (!legacyPatcher.getAccessTransformers().isEmpty()) { - var mergeATs = tasks.register("mergeATs", LegacyMergeFilesTask.class, task -> { - task.getFilesToMerge().setFrom(legacyPatcher.getAccessTransformers()); - - task.getOutput().set(project.getLayout().getBuildDirectory().file("legacy-forgedev/merged_ats.cfg")); - }); - setupMCP.configure(task -> { - task.dependsOn(mergeATs); - task.getAccessTransformerConfig().set(mergeATs.flatMap(LegacyMergeFilesTask::getOutput)); - }); - setupMCPSrg.configure(task -> { - task.dependsOn(mergeATs); - task.getAccessTransformerConfig().set(mergeATs.flatMap(LegacyMergeFilesTask::getOutput)); - }); - for (var f : legacyPatcher.getAccessTransformers()) { - userdevJar.configure(t -> t.from(f, e -> e.into("ats/"))); - userdevConfig.configure(t -> t.getATs().from(f)); - } - } - - if (!legacyPatcher.getSideAnnotationStrippers().isEmpty()) { - setupMCP.configure(task -> { - // TODO do this better - task.getSideAnnotationStripperConfig().fileProvider(getProviders().provider(() -> legacyPatcher.getSideAnnotationStrippers().getSingleFile())); - }); - setupMCPSrg.configure(task -> { - // TODO do this better - task.getSideAnnotationStripperConfig().fileProvider(getProviders().provider(() -> legacyPatcher.getSideAnnotationStrippers().getSingleFile())); - }); - - userdevConfig.configure(task -> task.getSASs().from(legacyPatcher.getSideAnnotationStrippers())); - for (var sas : legacyPatcher.getSideAnnotationStrippers()) { - userdevJar.configure(task -> task.from(sas, copy -> copy.into("sas/"))); - } - - var fakePatches = tasks.register("createFakeSASPatches", CreateFakeSASPatches.class, task -> - task.getFiles().from(legacyPatcher.getSideAnnotationStrippers()) - ); - for (var genBinPatchesTask : genBinPatchesTasks) { - genBinPatchesTask.configure(task -> task.getPatches().from(fakePatches.flatMap(CreateFakeSASPatches::getOutput))); - } - } - - if (!legacyPatcher.getExtraMappings().isEmpty()) { - for (var extraMapping : legacyPatcher.getExtraMappings()) { - if (extraMapping instanceof File e) { - userdevJar.configure(t -> t.from(e, c -> c.into("srgs/"))); - userdevConfig.configure(t -> t.getSRGs().from(e)); - } else if (extraMapping instanceof String e) { - userdevConfig.configure(t -> t.getSRGLines().add(e)); - } - } - } - - //UserDev Config Default Values - userdevConfig.configure(task -> { - task.getMCPConfig().set(legacyMcp.getConfig()); - task.getBinpatcherVersion().convention(Tools.BINPATCH.getModule().toString()); - task.getBinpatcherArguments().addAll("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}"); - task.getUniversal().convention(universalJar.flatMap(t -> - t.getArchiveBaseName().flatMap(baseName -> - t.getArchiveClassifier().flatMap(classifier -> - t.getArchiveExtension().map(jarExt -> - project.getGroup().toString() + ':' + baseName + ':' + project.getVersion() + ':' + classifier + '@' + jarExt - ))))); - task.getSource().convention(srgSourcesJar.flatMap(t -> - t.getArchiveBaseName().flatMap(baseName -> - t.getArchiveClassifier().flatMap(classifier -> - t.getArchiveExtension().map(jarExt -> - project.getGroup().toString() + ':' + baseName + ':' + project.getVersion() + ':' + classifier + '@' + jarExt - ))))); - task.getPatchesOriginalPrefix().set(genPatches.flatMap(GeneratePatches::getBasePathPrefix)); - task.getPatchesModifiedPrefix().set(genPatches.flatMap(GeneratePatches::getModifiedPathPrefix)); - task.getNotchObf().set(legacyPatcher.getNotchObf()); - }); - - if (legacyPatcher.isSrgPatches()) { - genPatches.configure(task -> task.getModified().set(applyRangeMapBase.flatMap(ApplyRangeMap::getOutput))); - } else { - // Remap the 'clean' with out mappings. - TaskProvider toMCPClean = tasks.register("srg2mcpClean", LegacyApplyMappings.class, task -> { - task.getInput().set(legacyPatcher.getCleanSrc()); - task.getMappings().setFrom(mappingsConfiguration); - task.getLambdas().set(false); - }); - - var dirtyZip = tasks.register("patchedZip", Zip.class, task -> { - task.from(legacyPatcher.getPatchedSrc()); - task.getArchiveFileName().set("output.zip"); - task.getDestinationDirectory().set(project.getLayout().getBuildDirectory().dir(task.getName())); - }); - - // Fixup the inputs. - applyPatches.configure(task -> { - task.dependsOn(toMCPClean); - task.getInput().set(toMCPClean.flatMap(LegacyApplyMappings::getOutput)); - }); - genPatches.configure(task -> { - task.getInput().set(toMCPClean.flatMap(LegacyApplyMappings::getOutput)); - task.getModified().set(dirtyZip.flatMap(AbstractArchiveTask::getArchiveFile)); - }); - - // don't remember why this is blocked off, but it was in FG6 so i'm keeping it in here for now as well - { - var mcpConfigArtifact = legacyMcp.getConfig(); - var srgNames = this.getProviders().provider(() -> !legacyPatcher.getNotchObf()); - - Function> rawJarTask = pipeline -> { - MavenizerRawArtifact.register(project, pipeline, mcpConfigArtifact, srgNames.map(b -> !b)); - return MavenizerRawArtifact.register(project, pipeline, mcpConfigArtifact, srgNames); - }; - var rawJoinedJar = rawJarTask.apply("joined"); - var rawClientJar = rawJarTask.apply("client"); - var rawServerJar = rawJarTask.apply("server"); - - var srg = legacyPatcher.getNotchObf() ? createMcp2Obf : createMcp2Srg; - reobfJar.configure(task -> task.getSrg().set(srg.flatMap(LegacyGenerateSRG::getOutput))); - - genJoinedBinPatches.configure(task -> task.getClean().builtBy(rawJoinedJar)); - genClientBinPatches.configure(task -> task.getClean().builtBy(rawClientJar)); - genServerBinPatches.configure(task -> task.getClean().builtBy(rawServerJar)); - tasks.withType(CreateBinPatches.class, task -> { - task.getSrg().from(srg.flatMap(LegacyGenerateSRG::getOutput)); - if (legacyPatcher.getPatches().isPresent()) { - task.mustRunAfter(genPatches); - task.getPatches().from(legacyPatcher.getPatches()); - } - }); - - filterNew.configure(task -> { - task.getSrg().set(srg.flatMap(LegacyGenerateSRG::getOutput)); - task.getBlacklist().builtBy(rawJoinedJar); - }); - } - } - - // TODO Clean up please - record SimpleModuleIdentifier(String getGroup, String getName) implements ModuleIdentifier { - SimpleModuleIdentifier() { - this("net.minecraft", "joined"); - } - } - - if (!legacyPatcher.runs.isEmpty()) { - var genEclipseRuns = project.getTasks().register("genEclipseRuns", task -> { - task.setGroup("IDE"); - task.setDescription("Generates the run configuration launch files for Eclipse."); - }); - - File eclipseOutputDir; - var eclipse = project.getExtensions().findByType(EclipseModel.class); - if (eclipse != null) { - eclipse.synchronizationTasks(genEclipseRuns); - eclipseOutputDir = eclipse.getClasspath().getDefaultOutputDir(); - } else { - eclipseOutputDir = getProjectLayout().getProjectDirectory().dir("bin").getAsFile(); - } - - for (var sourceSet : List.of(main.get(), java.getSourceSets().named(SourceSet.TEST_SOURCE_SET_NAME).get())) { - legacyPatcher.runs.forEach(options -> { - var task = SlimeLauncherExec.register(project, sourceSet, options, new SimpleModuleIdentifier(), legacyMcp.getVersion().get(), eclipseOutputDir); - }); - } - } - }); } } diff --git a/src/main/groovy/net/minecraftforge/forgedev/ForgeDevPlugin.java b/src/main/groovy/net/minecraftforge/forgedev/ForgeDevPlugin.java index d57338e..e48cf8c 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/ForgeDevPlugin.java +++ b/src/main/groovy/net/minecraftforge/forgedev/ForgeDevPlugin.java @@ -21,6 +21,8 @@ public abstract class ForgeDevPlugin extends EnhancedPlugin { public static final Logger LOGGER = Logging.getLogger("ForgeDev"); + private ForgeDevExtension extension; + @Inject public ForgeDevPlugin() { super(NAME, DISPLAY_NAME, "fdtools"); @@ -28,6 +30,10 @@ public ForgeDevPlugin() { @Override public void setup(ExtensionAware target) { - target.getExtensions().create(ForgeDevExtension.NAME, ForgeDevExtension.class, this, target); + this.extension = target.getExtensions().create(ForgeDevExtension.NAME, ForgeDevExtension.class, this, target); + } + + public ForgeDevExtension getExtension() { + return extension; } } diff --git a/src/main/groovy/net/minecraftforge/forgedev/LegacyMCPExtension.java b/src/main/groovy/net/minecraftforge/forgedev/LegacyMCPExtension.java deleted file mode 100644 index de51f1c..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/LegacyMCPExtension.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev; - -import net.minecraftforge.forgedev.legacy.values.MavenInfo; -import net.minecraftforge.forgedev.tasks.mcp.MCPSetupFiles; -import net.minecraftforge.gradleutils.shared.EnhancedProblems; -import net.minecraftforge.util.data.json.JsonData; -import org.gradle.api.Transformer; -import org.gradle.api.file.RegularFile; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -import org.gradle.api.provider.ProviderFactory; -import org.gradle.jvm.toolchain.JavaLauncher; - -import javax.inject.Inject; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public abstract class LegacyMCPExtension { - public static final String EXTENSION_NAME = "mcp"; - - private final Property config = this.getObjects().property(String.class); - private final Property version = this.getObjects().property(String.class).value( - config.map(s -> MavenInfo.from(s).art().version()) - ); - private final Map files = new HashMap<>(); - private final ForgeDevPlugin plugin; - - protected abstract @Inject ObjectFactory getObjects(); - protected abstract @Inject ProviderFactory getProviders(); - - private final Problems problems = getObjects().newInstance(Problems.class); - public static abstract class Problems extends EnhancedProblems { - @Inject - public Problems() { - super(ForgeDevPlugin.NAME + '.' + EXTENSION_NAME, ForgeDevPlugin.DISPLAY_NAME + " - MCP"); - } - } - - @Inject - public LegacyMCPExtension(ForgeDevPlugin plugin) { - this.plugin = plugin; - } - - public Property getConfig() { - return this.config; - } - - public Property getVersion() { - return this.version; - } - - public void setConfig(Provider value) { - getConfig().set(value.map(s -> { - if (s.indexOf(':') != -1) { // Full artifact - return s; - } else { - return "de.oceanlabs.mcp:mcp_config:" + s + "@zip"; - } - })); - } - - public void setConfig(String value) { - setConfig(this.getProviders().provider(() -> value)); - } - - public abstract Property getPipeline(); - - public String getArtifact() { - return this.getConfig().get(); - } - - public Provider getArtifact(String classifier) { - return getArtifact(classifier, "jar"); - } - public Provider getArtifact(String classifier, String extension) { - return this.getConfig().map(str -> { - var info = MavenInfo.from(str).art(); - var _new = MavenInfo.from(info.group(), info.name(), info.version(), classifier, extension); - return _new.name(); - }); - } - - public MinecraftFiles getFiles() { - return getFiles(getVersion().get()); - } - public MinecraftFiles getFiles(String version) { - var ret = this.files.get(version); - if (ret == null) { - //var client = mavenizer("client", version, true); - //var server = mavenizer("server", version, true); - var joined = mavenizer("joined", version, true); - - ret = getObjects().newInstance(MinecraftFiles.class, plugin, joined.info, joined.output); - this.files.put(version, ret); - } - return ret; - } - - private record Output(Provider info, File output) {} - private Output mavenizer(String pipeline, String version, boolean searge) { - var fileName = "mavenizer/mcp-" + version + "-files-" + pipeline; - if (searge) - fileName += "-searge"; - - // TODO: [ForgeDevPlugin] Make single mavenizer task to get the 'vanilla' files we need - // We also need a way to get the 'slim' artifacts for older versions which don't use bundled server jar - // Could still name it 'serverExtracted' in the json - var output = this.plugin.localCaches().file("mavenizer/" + fileName + ".jar").get().getAsFile(); - var outputJson = this.plugin.localCaches().file("mavenizer/" + fileName + ".json").get().getAsFile(); - - var info = this.getProviders().of(MavenizerValueSource.class, spec -> { - spec.parameters(params -> { - var tool = this.plugin.getTool(Tools.MAVENIZER); - params.getClasspath().setFrom(tool.getClasspath()); - params.getJavaLauncher().set(tool.getJavaLauncher().map(JavaLauncher::getExecutablePath)); - params.getArguments().set(this.getProviders().provider(() -> { - var toolCache = this.plugin.globalCaches() - .dir(tool.getName().toLowerCase(Locale.ENGLISH)) - .map(this.problems.ensureFileLocation()); - var cache = toolCache.get().dir("caches").getAsFile().getAbsolutePath(); - - var ret = new ArrayList<>(List.of( - "--mcp", - "--cache", cache, - "--jdk-cache", cache, - "--version", version, - "--raw", - "--pipeline", pipeline, - "--output", output.getAbsolutePath(), - "--output-files", outputJson.getAbsolutePath() - )); - if (searge) - ret.add("--searge"); - return ret; - })); - }); - }) - .map(v -> JsonData.fromJson(outputJson, MCPSetupFiles.class)); - - return new Output(info, output); - } - - public static abstract class MinecraftFiles { - private final ForgeDevPlugin plugin; - private final Provider info; - private final File joinedSearge; - protected abstract @Inject ObjectFactory getObjects(); - protected abstract @Inject ProviderFactory getProviders(); - - @Inject - public MinecraftFiles(ForgeDevPlugin plugin, Provider info, File joinedSearge) { - this.plugin = plugin; - this.info = info; - this.joinedSearge = joinedSearge; - } - - private Provider get(Transformer field) { - return this.info.map(field).flatMap(this.plugin.rootProjectDirectory()::file); - } - public Provider getLauncherManifest() { - return get(info -> info.versionManifest); - } - public Provider getVersionJson() { - return get(info -> info.versionJson); - } - public Provider getClient() { - return get(info -> info.clientRaw); - } - public Provider getClientMappings() { - return get(info -> info.clientMappings); - } - public Provider getServer() { - return get(info -> info.serverRaw); - } - public Provider getServerExtracted() { - return get(info -> info.serverExtracted); - } - public Provider getServerMappings() { - return get(info -> info.serverMappings); - } - public Provider getLibraryList() { - return get(info -> info.librariesList); - } - public Provider getJoinedSearge() { - return get(info -> this.joinedSearge.getAbsolutePath()); - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/LegacyPatcherExtension.java b/src/main/groovy/net/minecraftforge/forgedev/LegacyPatcherExtension.java deleted file mode 100644 index 4c4d839..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/LegacyPatcherExtension.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev; - -import net.minecraftforge.util.data.json.PatcherConfig; -import org.gradle.api.Project; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.MapProperty; -import org.gradle.api.provider.Property; -import org.jetbrains.annotations.VisibleForTesting; - -import javax.annotation.Nullable; -import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -@VisibleForTesting -public abstract class LegacyPatcherExtension extends LegacyMinecraftExtension { - public static final String EXTENSION_NAME = "patcher"; - - private boolean srgPatches = true; - private boolean notchObf = false; - - private List extraExcs, extraMappings; - - @Nullable - private PatcherConfig.V2.DataFunction processor; - - @Inject - public LegacyPatcherExtension() { } - - public abstract Property getParent(); - - public abstract RegularFileProperty getCleanSrc(); - - public abstract DirectoryProperty getPatchedSrc(); - - public abstract DirectoryProperty getPatches(); - - public abstract Property getMcVersion(); - - public boolean isSrgPatches() { - return srgPatches; - } - - public void setSrgPatches(boolean srgPatches) { - this.srgPatches = srgPatches; - } - - public boolean getNotchObf() { - return this.notchObf; - } - - public void setNotchObf(boolean value) { - this.notchObf = value; - } - - public abstract ConfigurableFileCollection getExcs(); - - public void setExtraExcs(List extraExcs) { - this.extraExcs = new ArrayList<>(extraExcs); - } - - public void extraExcs(Object... excs) { - getExtraExcs().addAll(Arrays.asList(excs)); // TODO: Type check! - } - - public void extraExc(Object exc) { - extraExcs(exc); // TODO: Type check! - } - - public List getExtraExcs() { - if (extraExcs == null) { - extraExcs = new ArrayList<>(); - } - - return extraExcs; - } - - public void extraMapping(Object mapping) { - if (mapping instanceof String || mapping instanceof File) { - getExtraMappings().add(mapping); - } else { - throw new IllegalArgumentException("Extra mappings must be a file or a string!"); - } - } - - public void setExtraMappings(List extraMappings) { - this.extraMappings = new ArrayList<>(extraMappings); - } - - public List getExtraMappings() { - if (extraMappings == null) { - extraMappings = new ArrayList<>(); - } - - return extraMappings; - } - - @Nullable - public PatcherConfig.V2.DataFunction getProcessor() { - return this.processor; - } - - public abstract MapProperty getProcessorData(); - - void copyFrom(LegacyPatcherExtension other) { - getMappingChannel().set(other.getMappingChannel()); - getMappingVersion().set(other.getMappingVersion()); - getMcVersion().set(other.getMcVersion()); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/Tools.java b/src/main/groovy/net/minecraftforge/forgedev/Tools.java index 61bf02d..6113f1c 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/Tools.java +++ b/src/main/groovy/net/minecraftforge/forgedev/Tools.java @@ -10,18 +10,16 @@ public final class Tools { private Tools() { } // EXECUTABLE - public static final Tool MAVENIZER = Tool.ofForge("mavenizer", "net.minecraftforge:minecraft-mavenizer:0.4.9", 25, "net.minecraftforge.mcmaven.cli.Main"); + public static final Tool MAVENIZER = Tool.ofForge("mavenizer", "net.minecraftforge:minecraft-mavenizer:0.4.58", 25); public static final Tool DIFFPATCH = Tool.of("diffpatch", "io.codechicken:DiffPatch:2.1.0.42:all", Constants.MAVEN_CENTRAL, 8); - public static final Tool BINPATCH = Tool.ofForge("binpatcher", "net.minecraftforge:binarypatcher:1.2.2:fatjar", 8); + public static final Tool BINPATCH = Tool.ofForge("binpatcher", "net.minecraftforge:binarypatcher:1.3.1:fatjar", 8); public static final Tool INSTALLERTOOLS = Tool.ofForge("installertools", "net.minecraftforge:installertools:1.4.4:fatjar", 8); public static final Tool INSTALLER = Tool.ofForge("installer", "net.minecraftforge:installer:2.2.9:fatjar", 8); public static final Tool SHIM = Tool.ofForge("shim", "net.minecraftforge:bootstrap-shim:2.1.8", 8); public static final Tool JARCOMPATIBILITYCHECKER = Tool.ofForge("jarcompatibilitychecker", "net.minecraftforge:JarCompatibilityChecker:0.1.28:all", 8); - public static final Tool RENAMER = Tool.ofForge("renamer", "net.minecraftforge:ForgeAutoRenamingTool:1.1.1:all", 8); - public static final Tool SRG2SRC = Tool.ofForge("srg2source", "net.minecraftforge:Srg2Source:8.1.1:fatjar", 17); public static final Tool SLIMELAUNCHER = Tool.ofForge("slimelauncher", "net.minecraftforge:slime-launcher:0.1.8", 8, "net.minecraftforge.launcher.Main"); + public static final Tool GITVERSION = Tool.ofForge("gitversion", "net.minecraftforge:gitversion:0.8.0:fatjar", 17); // LIBRARIES - public static final Tool SRGUTILS = Tool.ofForge("srgutils", "net.minecraftforge:srgutils:0.5.14", 8); public static final Tool FASTCSV = Tool.of("fastcsv", "de.siegmar:fastcsv:3.7.0", Constants.MAVEN_CENTRAL, 11); } diff --git a/src/main/groovy/net/minecraftforge/forgedev/Util.java b/src/main/groovy/net/minecraftforge/forgedev/Util.java index 03d3544..a64a394 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/Util.java +++ b/src/main/groovy/net/minecraftforge/forgedev/Util.java @@ -5,13 +5,42 @@ package net.minecraftforge.forgedev; import net.minecraftforge.gradleutils.shared.SharedUtil; +import net.minecraftforge.util.hash.HashFunction; +import org.gradle.api.Action; import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.provider.HasConfigurableValue; import org.gradle.api.specs.Spec; import org.gradle.plugins.ide.eclipse.model.EclipseModel; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.zip.ZipFile; + public final class Util extends SharedUtil { private Util() { } + private static final Logger LOGGER = Logging.getLogger(Util.class); public static final Spec IS_NOT_BLANK = s -> !s.isBlank(); public static String getProjectEclipseName(Project project) { @@ -19,4 +48,154 @@ public static String getProjectEclipseName(Project project) { var name = eclipse == null ? null : eclipse.getProject().getName(); return name != null ? name : project.getName(); } + + public static void logFiles(Logger logger, String prefix, Collection files) { + var itr = files.iterator(); + if (itr.hasNext()) + logger.info("{}: null", prefix); + else { + var padding = " ".repeat(prefix.length()); + logger.info("{}: {}", prefix, itr.next().getAbsolutePath()); + while (itr.hasNext()) + logger.info("{}: {}", padding, itr.next().getAbsolutePath()); + } + } + + public static void logArgs(Logger logger, String prefix, List args) { + var padding = " ".repeat(prefix.length()); + for (int x = 0; x < args.size(); x++) { + var current = args.get(x); + var next = args.size() > x + 1 ? args.get(x + 1) : null; + var line = current; + if (current.startsWith("--") && next != null && !next.startsWith("--")) { + x++; + line += ' ' + next; + } + logger.info("{}{}", x == 0 ? prefix : padding, line); + } + } + + @SuppressWarnings("unchecked") + public static R sneak(Throwable e) throws E { + throw (E)e; + } + + + public static String kebab(String s) { + var buf = new StringBuilder(s.length()); + for (var chr : s.toCharArray()) { + if (chr <= 'A' || chr >= 'Z') + buf.append('-').append(chr - 'A'); + else + buf.append(chr); + } + return buf.toString(); + } + + private static final Action DO_NOTHING = input -> {}; + @SuppressWarnings("unchecked") + public static Action noop() { + return (Action)DO_NOTHING; + } + + public static String capitalize(String s) { + return s.substring(0, 1).toUpperCase(Locale.ENGLISH) + s.substring(1); + } + + public static void extractZip(File source, File dest, boolean deleteExtra) { + try { + LOGGER.debug("Extracting {} to {}", source.getAbsolutePath(), dest.getAbsolutePath()); + Files.createDirectories(dest.toPath()); + var existing = deleteExtra ? Files.walk(dest.toPath()).filter(Files::isRegularFile).collect(Collectors.toSet()) : new HashSet(); + + try (var zip = new ZipFile(source)) { + for (var itr = zip.entries().asIterator(); itr.hasNext(); ) { + var entry = itr.next(); + if (entry.isDirectory()) + continue; + + var target = new File(dest, entry.getName()); + if (!existing.isEmpty()) + existing.remove(target.toPath()); + var data = zip.getInputStream(entry).readAllBytes(); + + // I do a hash check before writing because reading a file is faster than writing, and less destructive to hard drives. + var write = !target.exists() || target.length() != data.length || !HashFunction.SHA1.hash(target).equals(HashFunction.SHA1.hash(data)); + if (!write) + continue; + + LOGGER.debug("Extracting {}", entry.getName()); + Files.createDirectories(target.getParentFile().toPath()); + Files.write(target.toPath(), data); + } + } + + if (!deleteExtra) + return; + + for (var old : existing) { + LOGGER.debug("Deleting {}", old); + Files.delete(old); + } + + Files.walkFileTree(dest.toPath(), new SimpleFileVisitor<>() { + private final LinkedList count = new LinkedList<>(List.of(new AtomicInteger())); + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + count.getLast().incrementAndGet(); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + count.getLast().incrementAndGet(); + count.add(new AtomicInteger()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + // Remove and check counter for this directory + if (count.removeLast().get() == 0) { + count.getLast().decrementAndGet(); + LOGGER.debug("Delete: {}", dir); + Files.delete(dir); + } + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + sneak(e); + } + } + + public static String iso8601Now() { + return iso8601(new Date()); + } + + public static String iso8601(Date self) { + var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + var result = format.format(self); + return result.substring(0, 21) + ':' + result.substring(22); + } + + public static R finalize(Project project, R ret) { + ret.disallowChanges(); + ret.finalizeValueOnRead(); + return ret; + } + + private static final HttpClient HTTP = HttpClient.newBuilder().build(); + public static boolean checkExists(String url) { + try { + return HTTP.send(HttpRequest.newBuilder(new URI(url)) + .method("HEAD", HttpRequest.BodyPublishers.noBody()).build(), HttpResponse.BodyHandlers.discarding() + ).statusCode() == 200; + } catch (IOException | InterruptedException | URISyntaxException e) { + if (e.toString().contains("unable to find valid certification path to requested target")) + throw new RuntimeException("Failed to connect to $url: Missing certificate root authority, try updating Java"); + return sneak(e); + } + } } diff --git a/src/main/groovy/net/minecraftforge/forgedev/base/MCPBase.java b/src/main/groovy/net/minecraftforge/forgedev/base/MCPBase.java new file mode 100644 index 0000000..bdd5d74 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/base/MCPBase.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.base; + +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +public interface MCPBase extends PatcherBase { + Property<@NotNull String> getMcpArtifact(); + Property<@NotNull String> getMcpVersion(); + Property<@NotNull String> getMcpPipeline(); + + Provider getClasses(); + Provider getClassesRaw(); + + Configuration getDependencyConfiguration(); + Provider> getDependencies(); + Provider getExtra(); +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/base/MCPBaseImpl.java b/src/main/groovy/net/minecraftforge/forgedev/base/MCPBaseImpl.java new file mode 100644 index 0000000..1fb791e --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/base/MCPBaseImpl.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.base; + +import net.minecraftforge.forgedev.ForgeDevPlugin; +import net.minecraftforge.forgedev.values.MCPData; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; + +import javax.inject.Inject; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public abstract class MCPBaseImpl implements MCPBase { + private final Project project; + private final ForgeDevPlugin plugin; + private final String name; + private final MCPData mavenizer; + private final Configuration depConfig; + + protected abstract @Inject ObjectFactory getObjects(); + protected abstract @Inject ProviderFactory getProviders(); + + @Inject + public MCPBaseImpl(Project project, ForgeDevPlugin plugin, String name) { + this.project = project; + this.plugin = plugin; + this.name = name; + this.depConfig = project.getConfigurations().create(name + "Dependencies", cfg -> { + cfg.setCanBeResolved(true); + cfg.setCanBeConsumed(false); + }); + + this.getMcpPipeline().convention("joined"); + this.getMappingChannel().convention("official"); + this.getMappingVersion().convention(this.getMcpVersion().map(MCPBaseImpl::mcpToMinecraft)); + + this.getMcpArtifact().convention(this.getMcpVersion().map(s -> { + if (s.indexOf(':') != -1) // Full artifact + return s; + return "de.oceanlabs.mcp:mcp_config:" + s + "@zip"; + })); + + this.mavenizer = this.getObjects().newInstance(MCPData.class, this.project, this.plugin, name); + this.mavenizer.getMcpVersion().convention(this.getMcpVersion()); + this.mavenizer.getMcpPipeline().convention(this.getMcpPipeline()); + this.mavenizer.getMappingChannelInput().convention(this.getMappingChannel()); + this.mavenizer.getMappingVersionInput().convention(this.getMappingVersion()); + this.mavenizer.getAccessTransformers().convention(this.getAccessTransformers()); + this.mavenizer.getSideAnnotationStrippers().convention(this.getSideAnnotationStrippers()); + + var depFactory = project.getDependencies(); + this.depConfig.getDependencies().addAllLater(getDependencies().map(list -> { + var ret = new ArrayList(list.size()); + for (var desc : list) { + ret.add(depFactory.create(desc)); + } + return ret; + })); + } + + private static String mcpToMinecraft(String version) { + return version; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public Provider getNamedSources() { + return this.mavenizer.getSources(); + } + + @Override + public Provider getUnnamedSources() { + return this.mavenizer.getSourcesSrg(); + } + + @Override + public Provider getObf2Srg() { + return this.mavenizer.getObf2Srg(); + } + + @Override + public Provider getMap2Srg() { + return this.mavenizer.getMap2Srg(); + } + + @Override + public Provider getClasses() { + return this.mavenizer.getClasses().orElse(this.getClassesRaw()); + } + + @Override + public Provider getClassesRaw() { + return this.mavenizer.getClassesRaw(); + } + + @Override + public Provider getMappingZip() { + return this.mavenizer.getMappingZip(); + } + + @Override + public Provider> getDependencies() { + return this.mavenizer.getDependencies(); + } + + @Override + public Configuration getDependencyConfiguration() { + return this.depConfig; + } + + @Override + public Provider getExtra() { + return this.mavenizer.getExtra(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/base/PatcherBase.java b/src/main/groovy/net/minecraftforge/forgedev/base/PatcherBase.java new file mode 100644 index 0000000..47505bc --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/base/PatcherBase.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.base; + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +public interface PatcherBase { + String getName(); + Property<@NotNull String> getMappingChannel(); + Property<@NotNull String> getMappingVersion(); + + // Inputs + ConfigurableFileCollection getAccessTransformers(); + ConfigurableFileCollection getSideAnnotationStrippers(); + + // Outputs + Provider getNamedSources(); + Provider getUnnamedSources(); + Provider getObf2Srg(); + Provider getMap2Srg(); + Provider getMappingZip(); + + +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildPlugin.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildPlugin.java deleted file mode 100644 index e9fdd5c..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildPlugin.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy; - -import net.minecraftforge.forgedev.tasks.filtering.LegacyFilterNewJar; -import net.minecraftforge.forgedev.tasks.generation.GeneratePatcherConfigV2; -import net.minecraftforge.forgedev.tasks.installertools.DownloadMappings; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerMCPSetup; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerRawArtifact; -import net.minecraftforge.forgedev.tasks.obfuscation.LegacyRenameJar; -import net.minecraftforge.forgedev.tasks.obfuscation.LegacyReobfuscateJar; -import net.minecraftforge.forgedev.tasks.patching.binary.ApplyBinPatches; -import net.minecraftforge.forgedev.tasks.patching.binary.CreateBinPatches; -import net.minecraftforge.forgedev.tasks.patching.diff.ApplyPatches; -import net.minecraftforge.gradleutils.shared.EnhancedPlugin; -import org.gradle.api.Project; -import org.gradle.api.file.ArchiveOperations; -import org.gradle.api.file.Directory; -import org.gradle.api.file.DuplicatesStrategy; -import org.gradle.api.file.ProjectLayout; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ProviderFactory; -import org.gradle.api.tasks.bundling.Jar; -import org.gradle.api.tasks.bundling.Zip; - -import javax.inject.Inject; - -abstract class ForgeBuildPlugin extends EnhancedPlugin { - static final String NAME = "forge-build"; - static final String DISPLAY_NAME = "Forge Build"; - - private final ForgeBuildProblems problems = getObjects().newInstance(ForgeBuildProblems.class); - - protected abstract @Inject ObjectFactory getObjects(); - - protected abstract @Inject ProviderFactory getProviders(); - - protected abstract @Inject ProjectLayout getLayout(); - - protected abstract @Inject ArchiveOperations getArchiveOperations(); - - @Inject - public ForgeBuildPlugin() { - super(NAME, DISPLAY_NAME, "forgeTools"); - } - - @SuppressWarnings("removal") - @Override - public void setup(Project project) { - project.getPluginManager().apply("de.undercouch.download"); - - var providers = getProviders(); - var layout = getLayout(); - var archiveOperations = getArchiveOperations(); - - var tasks = project.getTasks(); - - project.getPluginManager().withPlugin("net.minecraftforge.forgedev", forgedevAppliedPlugin -> { - var setupMCP = tasks.named("setupMCP", MavenizerMCPSetup.class); - var downloadClientMappings = tasks.named("downloadClientMappings", DownloadMappings.class); - var downloadServerMappings = tasks.named("downloadServerMappings", DownloadMappings.class); - var jar = tasks.named("jar", Jar.class); - - var createClientOfficial = tasks.register("createClientOfficial", LegacyRenameJar.class, task -> { - task.dependsOn(setupMCP); - - task.getAdditionalArgs().addAll("--ann-fix", "--ids-fix", "--src-fix", "--record-fix", "--strip-sigs", "--reverse"); - task.getMappings().set(downloadClientMappings.flatMap(DownloadMappings::getOutput)); - task.getInput().set(setupMCP.flatMap(MavenizerMCPSetup::getClientRaw)); - task.getOutput().set(task.getDefaultOutputFile()); - }); - - var createServerOfficial = tasks.register("createServerOfficial", LegacyRenameJar.class, task -> { - task.dependsOn(setupMCP); - - task.getAdditionalArgs().addAll("--ann-fix", "--ids-fix", "--src-fix", "--record-fix", "--strip-sigs", "--reverse"); - task.getMappings().set(downloadServerMappings.flatMap(DownloadMappings::getOutput)); - task.getInput().set(setupMCP.flatMap(MavenizerMCPSetup::getServerExtracted)); - task.getOutput().set(task.getDefaultOutputFile()); - }); - - var genClientBinPatches = tasks.named("genClientBinPatches", CreateBinPatches.class, task -> { - task.getClean().setFrom(createClientOfficial.flatMap(LegacyRenameJar::getOutput)); - task.getCreate().setFrom(jar.flatMap(Jar::getArchiveFile)); - }); - - var genServerBinPatches = tasks.named("genServerBinPatches", CreateBinPatches.class, task -> { - task.getClean().setFrom(createServerOfficial.flatMap(LegacyRenameJar::getOutput)); - task.getCreate().setFrom(jar.flatMap(Jar::getArchiveFile)); - }); - - var genJoinedBinPatches = tasks.named("genJoinedBinPatches", CreateBinPatches.class, task -> { - task.getClean().setFrom(tasks.named("rawJoinedJarSrg", MavenizerRawArtifact.class).map(t -> t.getOutputs().getFiles())); - }); - - var applyClientBinPatches = tasks.register("applyClientBinPatches", ApplyBinPatches.class, task -> { - task.getClean().setFrom(createClientOfficial.flatMap(LegacyRenameJar::getOutput)); - task.getApply().setFrom(genClientBinPatches.flatMap(CreateBinPatches::getOutput)); - task.getData().set(true); - task.getUnpatched().set(true); - }); - - var applyServerBinPatches = tasks.register("applyServerBinPatches", ApplyBinPatches.class, task -> { - task.getClean().setFrom(createServerOfficial.flatMap(LegacyRenameJar::getOutput)); - task.getApply().setFrom(genServerBinPatches.flatMap(CreateBinPatches::getOutput)); - task.getData().set(true); - task.getUnpatched().set(true); - }); - - var applyJoinedBinPatches = tasks.register("applyJoinedBinPatches", ApplyBinPatches.class, task -> { - task.getClean().setFrom(genJoinedBinPatches.map(CreateBinPatches::getClean)); - task.getApply().setFrom(genJoinedBinPatches.flatMap(CreateBinPatches::getOutput)); - }); - - var applyPatches = tasks.named("applyPatches", ApplyPatches.class, task -> { - task.getFailOnError().set(!problems.test("net.minecraftforge.forge.build.updating")); - task.getRejects().convention(project.getRootProject().getLayout().getProjectDirectory().dir(providers.provider(() -> "rejects")).map(Directory::getAsFile)); - task.getArchiveRejects().unsetConvention(); - }); - - var reobfJar = tasks.named("reobfJar", LegacyReobfuscateJar.class); - - var officialClassesJar = tasks.register("officialClassesJar", Zip.class, task -> { - task.dependsOn(jar); - - task.getDestinationDirectory().set(layout.getBuildDirectory().dir("libs")); - task.getArchiveClassifier().set("official-classes"); - task.getArchiveExtension().set("jar"); - - task.from(providers.provider(() -> archiveOperations.zipTree(jar.flatMap(Jar::getArchiveFile))), copy -> copy - .include("**/*.class") - .exclude("mcp/**")); - }); - - var filterJarNew = tasks.named("filterJarNew", LegacyFilterNewJar.class, task -> { - task.dependsOn(officialClassesJar); - - task.getInput().set(officialClassesJar.flatMap(Zip::getArchiveFile)); - }); - - var filterJarNewSrg = tasks.register("filterJarNewSrg", LegacyFilterNewJar.class, task -> { - task.dependsOn(reobfJar, filterJarNew); - - task.getInput().set(reobfJar.flatMap(LegacyReobfuscateJar::getOutput)); - task.getSrg().set(filterJarNew.flatMap(LegacyFilterNewJar::getSrg)); - task.getBlacklist().setFrom(filterJarNew.map(LegacyFilterNewJar::getBlacklist)); - }); - - var universalJar = tasks.named("universalJar", Jar.class); - - var universalJarSrg = tasks.register("universalJarSrg", Jar.class, task -> { - task.dependsOn(filterJarNewSrg, universalJar); - - var filterNewJarSrgOutput = filterJarNewSrg.flatMap(LegacyFilterNewJar::getOutput); - var universalJarOutput = universalJar.flatMap(Jar::getArchiveFile); - task.from(providers.provider(() -> archiveOperations.zipTree(filterNewJarSrgOutput))); - task.from(providers.provider(() -> archiveOperations.zipTree(universalJarOutput))); - task.setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE); - - task.getArchiveClassifier().set("universal-srg"); - task.setManifest(universalJar.map(Jar::getManifest).get()); - }); - - var userdevConfig = tasks.named("userdevConfig", GeneratePatcherConfigV2.class, task -> { - task.getUniversal().set("%s:%s:%s:universal-srg@jar".formatted(project.getGroup(), project.getName(), project.getVersion())); - }); - }); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildProblems.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildProblems.java deleted file mode 100644 index 46504a7..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildProblems.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy; - -import net.minecraftforge.gradleutils.shared.EnhancedProblems; - -import javax.inject.Inject; -import java.io.Serial; - -public abstract class ForgeBuildProblems extends EnhancedProblems { - private static final @Serial long serialVersionUID = -34777297274844674L; - - @Inject - public ForgeBuildProblems() { - super(ForgeBuildPlugin.NAME, ForgeBuildPlugin.DISPLAY_NAME); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildTask.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildTask.java deleted file mode 100644 index 479e4ef..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/ForgeBuildTask.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy; - -import net.minecraftforge.gradleutils.shared.EnhancedPlugin; -import net.minecraftforge.gradleutils.shared.EnhancedTask; -import org.gradle.api.Project; - -public interface ForgeBuildTask extends EnhancedTask { - @Override - default Class> pluginType() { - return ForgeBuildPlugin.class; - } - - @Override - default Class problemsType() { - return ForgeBuildProblems.class; - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/SharedBuildPlugin.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/SharedBuildPlugin.java index 7bfe630..ea5809b 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/SharedBuildPlugin.java +++ b/src/main/groovy/net/minecraftforge/forgedev/legacy/SharedBuildPlugin.java @@ -4,13 +4,14 @@ */ package net.minecraftforge.forgedev.legacy; -import net.minecraftforge.forgedev.legacy.tasks.Util; -import net.minecraftforge.forgedev.legacy.tasks.WriteManifest; +import net.minecraftforge.forgedev.tasks.WriteManifest; +import net.minecraftforge.forgedev.publishvalidate.ValidatePublish; import net.minecraftforge.gradleutils.shared.SharedUtil; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.file.ProjectLayout; import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.Jar; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; @@ -25,10 +26,6 @@ import java.util.List; abstract class SharedBuildPlugin implements Plugin { - static { - Util.init(); - } - protected abstract @Inject ProjectLayout getLayout(); @Inject @@ -38,8 +35,6 @@ public SharedBuildPlugin() { } public void apply(Project project) { project.setGroup("net.minecraftforge"); - var layout = getLayout(); - var tasks = project.getTasks(); var generateResources = SharedUtil.runFirst(project, tasks.register("generateResources")); @@ -48,21 +43,42 @@ public void apply(Project project) { ); project.getPluginManager().withPlugin("java", javaAppliedPlugin -> { + var java = project.getExtensions().getByType(JavaPluginExtension.class); tasks.withType(Javadoc.class).configureEach(task -> { + task.setFailOnError(false); task.options(minimalOptions -> { if (minimalOptions instanceof CoreJavadocOptions coreOptions) { coreOptions.setMemberLevel(JavadocMemberLevel.PUBLIC); - coreOptions.addBooleanOption("Xdoclint:all", true); - coreOptions.addBooleanOption("-Xdoclint:missing", true); + coreOptions.addBooleanOption("Xdoclint:all,-missing", true); } }); }); tasks.withType(JavaCompile.class).configureEach(task -> { + task.dependsOn(processResources); var options = task.getOptions(); options.setWarnings(false); // Shutup deprecated for removal warnings options.getForkOptions().setMemoryMaximumSize("6G"); // Needed to make compiling faster, and not run out of heap space in some cases. }); + tasks.withType(Jar.class).configureEach(task -> { + // This is a dirty hack, but there is no way for us to figure out what are just the tasks we want, as they are registered lazily + // in a way that I have not found a way to react to + for (var sourceSet : java.getSourceSets()) { + if ( + task.getName().equals(sourceSet.getCompileJavaTaskName()) || + task.getName().equals(sourceSet.getSourcesJarTaskName()) || + task.getName().equals(sourceSet.getProcessResourcesTaskName()) + ) { + System.out.println("Task: " + task.getName() + " " + sourceSet.getProcessResourcesTaskName()); + task.dependsOn(processResources); + return; + } + } + }); + + // Write the manifest to our resources directory because we use it for version information + var writeManifest = WriteManifest.register(project, tasks.named("jar", Jar.class)); + generateResources.configure(task -> task.dependsOn(writeManifest)); }); project.getPluginManager().withPlugin("eclipse", eclipseAppliedPlugin -> { @@ -75,31 +91,6 @@ public void apply(Project project) { ); }); - project.afterEvaluate(this::finish); - } - - private void finish(Project project) { - var tasks = project.getTasks(); - - var jar = project.getPluginManager().hasPlugin("net.minecraftforge.forgedev") ? "universalJar" : "jar"; - WriteManifest.register(project, tasks.named(jar, Jar.class)); - - for (var sourceSet : project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets()) { - var existing = tasks.getNames(); - if (!existing.contains(sourceSet.getSourcesJarTaskName()) - || !existing.contains(sourceSet.getProcessResourcesTaskName())) - continue; - - var processResources = tasks.named(sourceSet.getProcessResourcesTaskName()); - var names = List.of( - sourceSet.getCompileJavaTaskName(), - sourceSet.getCompileTaskName("groovy"), - sourceSet.getSourcesJarTaskName() - ); - for (var name : names) { - if (existing.contains(name)) - tasks.named(name, task -> task.dependsOn(processResources)); - } - } + project.getPluginManager().withPlugin("maven-publish", maven -> ValidatePublish.onApplyMavenPublish(project)); } } diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/BytecodeFinder.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/BytecodeFinder.groovy deleted file mode 100644 index af5dee3..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/BytecodeFinder.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.json.JsonBuilder -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.TaskAction -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.FieldNode -import org.objectweb.asm.tree.MethodNode - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class BytecodeFinder extends DefaultTask { - @InputFile abstract RegularFileProperty getJar() - // It should be fine to mark the output as internal as we want to control when we run it anyways. - // This also shuts Gradle 8 up about implicit task dependencies. - @Internal abstract RegularFileProperty getOutput() - - @Inject - BytecodeFinder() { - output.convention(project.layout.buildDirectory.dir(name).map { it.file("output.json") }) - } - - @TaskAction - protected void exec() { - var outputFile = output.get().asFile - if (outputFile.exists()) - outputFile.delete() - - pre() - - Util.processClassNodes(jar.get().asFile, this.&process) - - post() - outputFile.text = new JsonBuilder(data).toPrettyString() - } - - - protected process(ClassNode node) { - if (node.fields !== null) node.fields.each { process(node, it) } - if (node.methods !== null) node.methods.each { process(node, it) } - } - - protected pre() {} - protected process(ClassNode parent, FieldNode node) {} - protected process(ClassNode parent, MethodNode node) {} - protected post() {} - @PackageScope abstract @Internal Object getData() -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CheckForgeJarCompatibility.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CheckForgeJarCompatibility.java deleted file mode 100644 index 1c14358..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/CheckForgeJarCompatibility.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks; - -import de.undercouch.gradle.tasks.download.Download; -import net.minecraftforge.forgedev.legacy.values.LatestForgeVersion; -import net.minecraftforge.forgedev.tasks.jarcompat.CheckJarCompatibility; -import net.minecraftforge.forgedev.tasks.mcp.MavenizerRawArtifact; -import net.minecraftforge.forgedev.tasks.obfuscation.LegacyReobfuscateJar; -import net.minecraftforge.forgedev.tasks.patching.binary.ApplyBinPatches; -import org.gradle.api.Action; -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.file.ArchiveOperations; -import org.gradle.api.file.ProjectLayout; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.specs.Spec; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.OutputFile; -import org.gradle.api.tasks.TaskAction; -import org.gradle.api.tasks.TaskProvider; -import org.gradle.language.base.plugins.LifecycleBasePlugin; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -public class CheckForgeJarCompatibility { - private static final String TASK_NAME = "checkJarCompatibility"; - - public static TaskProvider register(Project project, String minecraftVersion, Action action) { - if (project.getTasks().getNames().contains(TASK_NAME)) - throw new IllegalStateException("Cannot register " + TASK_NAME + " more than once"); - - var baseForgeVersion = project.getObjects().property(String.class).value(project.getProviders().of(LatestForgeVersion.class, LatestForgeVersion.parameters(project, minecraftVersion))); - Spec baseForgeVersionOnlyIf = t -> baseForgeVersion.isPresent(); - var baseForgeUserdev = project.getLayout().getBuildDirectory().file(project.provider(() -> TASK_NAME + "/forge-" + baseForgeVersion.getOrElse("null") + "-userdev.jar")); - var baseForgeUniversal = project.getLayout().getBuildDirectory().file(project.provider(() -> TASK_NAME + "/forge-" + baseForgeVersion.getOrElse("null") + "-universal.jar")); - - var downloadBaseForgeUserdev = project.getTasks().register("downloadBaseForgeUserdev", Download.class, task -> { - task.setDescription("Sets up JAR compatibility checking by downloading the latest available UserDev."); - task.onlyIf(baseForgeVersionOnlyIf); - - task.src("https://maven.minecraftforge.net/net/minecraftforge/forge/" + baseForgeVersion.getOrElse("null") + "/forge-" + baseForgeVersion.getOrElse("null") + "-userdev.jar"); - task.dest(baseForgeUserdev); - }); - - var extractBaseForgeUserdevBinPatches = project.getTasks().register("extractBaseForgeUserdevBinPatches", UserdevBinPatches.class, task -> { - task.setDescription("Sets up JAR compatibility checking by extracting the binary patches from the latest available UserDev."); - task.onlyIf(baseForgeVersionOnlyIf); - task.dependsOn(downloadBaseForgeUserdev); - - task.getBaseForgeUserdev().fileProvider(downloadBaseForgeUserdev.map(Download::getDest)); - }); - - var applyBaseCompatibilityJarBinPatchesOutput = project.getLayout().getBuildDirectory().file("applyBaseCompatibilityJarBinPatches/output.jar"); - var applyBaseCompatibilityJarBinPatches = project.getTasks().register("applyBaseCompatibilityJarBinPatches", ApplyBinPatches.class, task -> { - task.setDescription("Sets up JAR compatibility checking by applying the base jar's binary patches from the latest available UserDev."); - task.onlyIf(baseForgeVersionOnlyIf); - task.dependsOn(extractBaseForgeUserdevBinPatches); - - task.getClean().setFrom(task.getProject().getTasks().named("rawJoinedJarSrg", MavenizerRawArtifact.class).flatMap(MavenizerRawArtifact::getOutput)); - task.getApply().setFrom(extractBaseForgeUserdevBinPatches.flatMap(UserdevBinPatches::getBaseBinPatchesOutput)); - task.getOutput().set(applyBaseCompatibilityJarBinPatchesOutput); - }); - - var downloadBaseForgeUniversal = project.getTasks().register("downloadBaseForgeUniversal", Download.class, task -> { - task.setDescription("Sets up JAR compatibility checking by downloading the latest available universal JAR."); - task.onlyIf(baseForgeVersionOnlyIf); - - task.src("https://maven.minecraftforge.net/net/minecraftforge/forge/" + baseForgeVersion.getOrElse("null") + "/forge-" + baseForgeVersion.getOrElse("null") + "-universal.jar"); - task.dest(baseForgeUniversal); - }); - - var mergeBaseForgeJar = project.getTasks().register("mergeBaseForgeJar", MergeJars.class, task -> { - task.setDescription("Sets up JAR compatibility checking by merging the universal JAR with the binary patched JAR."); - task.onlyIf(baseForgeVersionOnlyIf); - task.dependsOn(downloadBaseForgeUniversal); - - task.getInputJars().from( - applyBaseCompatibilityJarBinPatches.flatMap(ApplyBinPatches::getOutput), - downloadBaseForgeUniversal.map(Download::getDest) - ); - }); - - var reobfJar = project.getTasks().named("reobfJar", LegacyReobfuscateJar.class); - var checkJarCompatibility = project.getTasks().register(TASK_NAME, CheckJarCompatibility.class, task -> { - var rawJoinedJarSrg = task.getProject().getTasks().named("rawJoinedJarSrg", MavenizerRawArtifact.class); - - task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); - task.setDescription("Checks the JAR compatibility between the built JAR and the latest available JAR."); - task.onlyIf(t -> baseForgeVersion.isPresent()); - task.dependsOn(rawJoinedJarSrg, mergeBaseForgeJar); - - task.getBaseJar().set(mergeBaseForgeJar.flatMap(MergeJars::getOutput)); - task.getBaseLibraries().from(rawJoinedJarSrg.flatMap(MavenizerRawArtifact::getOutput)); - - task.getInputJar().set(reobfJar.flatMap(LegacyReobfuscateJar::getOutput)); - }); - checkJarCompatibility.configure(action); - - var providers = project.getProviders(); - var hasMaven = providers.environmentVariable("MAVEN_USER").isPresent() && providers.environmentVariable("MAVEN_PASSWORD").isPresent(); - var checkCompatibility = providers.gradleProperty("net.minecraftforge.forge.build.check.compatibility").map(Boolean::parseBoolean).getOrElse(false); - - if (!hasMaven && checkCompatibility) { - project.getTasks().named("check", task -> { - task.dependsOn(checkJarCompatibility); - }); - } - - return checkJarCompatibility; - } - - static abstract class UserdevBinPatches extends DefaultTask { - protected abstract @InputFile @Optional RegularFileProperty getBaseForgeUserdev(); - - abstract @OutputFile RegularFileProperty getBaseBinPatchesOutput(); - - protected abstract @Inject ProjectLayout getLayout(); - protected abstract @Inject ArchiveOperations getArchiveOperations(); - - @Inject - public UserdevBinPatches() { - getBaseBinPatchesOutput().convention(getLayout().getBuildDirectory().file(getName() + "/joined.lzma")); - } - - @TaskAction - protected void exec() { - var joinedLzma = getArchiveOperations().zipTree(getBaseForgeUserdev()).matching(it -> it.include("joined.lzma")).getSingleFile(); - try { - Files.copy(joinedLzma.toPath(), getBaseBinPatchesOutput().getAsFile().get().toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ClosureHelper.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ClosureHelper.groovy deleted file mode 100644 index 27a42b7..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ClosureHelper.groovy +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.stc.FirstParam - -import java.util.function.BiConsumer - -public class ClosureHelper { - BiConsumer callback - - public ClosureHelper(Closure cl, BiConsumer callback) { - this.callback = callback - apply(this, cl) - } - - - def methodMissing(String name, Object args) { - if (!args.class.isArray()) return - Object[] aargs = (Object[])args - - if (aargs.length == 1 && aargs[0] instanceof Closure) { - this.callback.accept(name, (Closure)aargs[0]) - } else { - throw new IllegalArgumentException('Unknown method: "' + name + '" with arguments ' + args + ' for ' + this) - } - } - - static T apply(T obj, @DelegatesTo(value = FirstParam, strategy = Closure.DELEGATE_FIRST) Closure cl) { - cl.delegate = obj - cl.resolveStrategy = Closure.DELEGATE_FIRST - cl() - return obj - } -} \ No newline at end of file diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/DownloadLibraries.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/DownloadLibraries.groovy deleted file mode 100644 index 422e6a0..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/DownloadLibraries.groovy +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -import java.nio.file.Files - -abstract class DownloadLibraries extends DefaultTask { - @InputFile abstract RegularFileProperty getInput() - @OutputDirectory abstract DirectoryProperty getOutput() - @OutputFile abstract RegularFileProperty getLibrariesOutput() - - DownloadLibraries() { - output.convention(project.layout.buildDirectory.dir(name)) - librariesOutput.convention(project.layout.buildDirectory.dir(name).map(d -> d.file('libraries.txt'))) - } - - @TaskAction - def run() { - File outputDir = output.get().asFile - var libraries = new ArrayList() - - def json = input.get().asFile.json().libraries.each { lib -> - //TODO: Thread? - def artifacts = [lib.downloads.artifact] + lib.downloads.get('classifiers', [:]).values() - artifacts.each{ art -> - def target = new File(outputDir, art.path) - libraries.add(target.absolutePath) - if (!target.exists() || art.sha1 != target.sha1()) { - project.logger.lifecycle("Downloading ${art.url}") - if (!target.parentFile.exists()) { - target.parentFile.mkdirs() - } - new URL(art.url).withInputStream { i -> - target.withOutputStream { it << i } - } - if (art.sha1 != target.sha1()) { - throw new IllegalStateException("Failed to download ${art.url} to ${target.canonicalPath} SHA Mismatch") - } - } - } - } - Files.write(librariesOutput.get().asFile.toPath(), libraries) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ExtractFile.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ExtractFile.groovy deleted file mode 100644 index de1ed60..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ExtractFile.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.CompileStatic -import org.gradle.api.DefaultTask -import org.gradle.api.file.ArchiveOperations -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.util.PatternFilterable - -import javax.inject.Inject - -@CompileStatic -abstract class ExtractFile extends DefaultTask { - abstract @InputFile RegularFileProperty getInput() - abstract @Input Property getTarget() - abstract @OutputFile RegularFileProperty getOutput() - - protected abstract @Inject ArchiveOperations getArchiveOperations() - - @TaskAction - protected void exec() { - final target = this.target.get() - var zipTree = archiveOperations.zipTree(input).matching { PatternFilterable pattern -> - pattern.include(target) - } - - File entry - try { - entry = zipTree.singleFile - } catch (IllegalStateException e) { - throw new IllegalStateException("${this.input.asFile.get().absolutePath} does not contain $target", e) - } - - output.asFile.get().tap { parentFile.mkdirs() }.bytes = entry.bytes - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/FieldCompareFinder.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/FieldCompareFinder.groovy deleted file mode 100644 index b462ddc..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/FieldCompareFinder.groovy +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.CompileStatic -import groovy.transform.EqualsAndHashCode -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.objectweb.asm.tree.AbstractInsnNode -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.MethodNode - -abstract class FieldCompareFinder extends BytecodeFinder { - @Nested - Map fields = [:] as HashMap - @Internal - Map fieldsReverse = [:] as HashMap - @Internal - Map> targets = [:] as TreeMap - - @Override - protected pre() { - //fields.each{ k,v -> logger.lifecycle("Fields: " + k + ' ' + v) } - } - - @Override - protected process(ClassNode parent, MethodNode node) { - AbstractInsnNode last = null - def parentInstance = new ObjectTarget(owner: parent.name, name: '', desc: '') - for (int x = 0; x < node.instructions.size(); x++) { - def current = node.instructions.get(x) - if (current.opcode === IF_ACMPEQ || current.opcode === IF_ACMPNE) { - if (last !== null && (last.opcode === GETSTATIC || last.opcode === GETFIELD)) { - def target = new Search(cls: last.owner, name: last.name) - def wanted = fieldsReverse.get(target) - def original = fields.get(wanted) - def instance = new ObjectTarget(owner: parent.name, name: node.name, desc: node.desc) - if (wanted !== null && (original.blacklist === null || (!original.blacklist.contains(instance) && !original.blacklist.contains(parentInstance)))) { - targets.computeIfAbsent(wanted, { k -> new TreeSet() }).add(instance) - } - } - } - last = current - } - } - - @Internal - @Override - protected Object getData() { - def ret = [:] as HashMap - targets.forEach{ k, v -> - def e = fields.get(k) - ret[k] = [ - cls: e.cls, - name: e.name, - replacement: e.replacement, - targets: v - ] - } - return ret - } - - @CompileStatic - @EqualsAndHashCode(excludes = ['replacement', 'blacklist']) - static class Search { - @Input - String cls - - @Input - String name - - @Input - String replacement - - @Nested - @Optional - Set blacklist - - @Override - String toString() { - return cls + '.' + name - } - - def blacklist(String owner, String name, String desc) { - if (blacklist === null) - blacklist = new HashSet<>() - blacklist.add(new ObjectTarget(owner: owner, name: name, desc: desc)) - } - def blacklist(String owner) { - blacklist(owner, '', '') - } - } - - void fields(Closure cl) { - new ClosureHelper(cl, {name, ccl -> - def search = ClosureHelper.apply(new Search(), ccl) - this.fields.put(name, search) - this.fieldsReverse.put(search, name) - }) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJar.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJar.groovy deleted file mode 100644 index d6df54b..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJar.groovy +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import net.minecraftforge.forgedev.legacy.values.CIRuntime -import org.gradle.api.DefaultTask -import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.file.ProjectLayout -import org.gradle.api.provider.Property -import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.bundling.AbstractArchiveTask -import org.gradle.api.tasks.bundling.Zip - -import javax.inject.Inject - -abstract class InstallerJar extends Zip { - @Input @Optional abstract Property getFat() - @Input @Optional abstract Property getOffline() - - protected abstract @Inject ProjectLayout getLayout() - protected abstract @Inject ProviderFactory getProviders() - - InstallerJar() { - archiveClassifier.set('installer') - archiveExtension.set('jar') // Needs to be Zip task to not override Manifest, so set extension - destinationDirectory.set(layout.buildDirectory.dir('libs')) - fat.convention(providers.of(CIRuntime) { }.map { !it }) - offline.convention(false) - - def installerJson = project.tasks.installerJson - def launcherJson = project.tasks.launcherJson - def downloadInstaller = project.tasks.downloadInstaller - - dependsOn(installerJson, launcherJson, downloadInstaller, project.configurations.installer) - from(installerJson, launcherJson) - - from(project.rootProject.file('/src/main/resources/url.png')) - project.afterEvaluate { - from(project.zipTree(downloadInstaller.output)) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - - if (fat.get() || offline.get()) { - def cfg = project.tasks.register(name + "Config", Configure) - cfg.get().configure { - parent = this - dependsOn(project.tasks.installerJson, project.tasks.launcherJson) - } - dependsOn(cfg) - } else { - // Things we ALWAYS bundle, this just the server shim jar, because the installer spec only says to extract the file. - // I should make it allow downloads but thats a spec break, and this is just a ~14KB jar - [ - project.tasks.serverShimJar // Server bootstrap executable jar - ].forEach { AbstractArchiveTask packed -> - def path = Util.getMavenInfoFromTask(packed).path - from(packed) { - rename { "maven/$path" } - } - } - } - } - } - - static abstract class Configure extends DefaultTask { - public Zip parent - private int count = 0; - - @TaskAction - protected void exec() { - def deps = [:] as java.util.TreeMap - - // Gather all things that need downloading - [ - project.tasks.installerJson, - project.tasks.launcherJson - ].each { task -> - def json = task.output.get().asFile.json - json.libraries.each { lib -> - if (lib.downloads?.artifact?.url !== null && !lib.downloads.artifact.url.isEmpty()) - deps.put(lib.name, lib.downloads.artifact) - } - } - - // First find things we build in this project. - [ - project.tasks.universalJar, // Forge runtime code - project.tasks.serverShimJar // Shim jar for dedicated server - ].forEach { AbstractArchiveTask packed -> - def name = Util.getMavenInfoFromTask(packed).name - def info = deps.remove(name) - if (info !== null) { - println("Adding: $packed.path $name") - parent.from(packed) { - rename { "maven/$info.path" } - } - } - } - - // Find any artifacts from the 'installer' config - // This config specifies the runtime files we intend for the interaller to have. - // And are typically what we would be developing and testing alongside Forge. - // So we may have local modified versions - def cfg = project.configurations.installer - while (cfg != null) { - //println('') - def resolved = cfg.resolvedConfiguration.resolvedArtifacts - int found = 0 - for (def dep : resolved) { - def name = Util.getMavenInfoFromDep(dep).name - def info = deps.remove(name) - if (info == null) { - //println("Skipping: $name") - continue - } - //println("-$name") - found++ - addFile(dep.file, info) - } - - if (deps.isEmpty()) { - cfg = null - continue - } - - // Prevent infinite loops if something fucky happens - if (found == 0) - throw new IllegalStateException("Failed to find any installer dependencies") - - def seen = [] as Set - cfg = project.configurations.detachedConfiguration() - cfg.transitive = false - for (def key : deps.keySet()) { - def (group, artifact, other) = key.split(':', 3) - // Only resolve unique group:artifact so we don't get version overrides - if (seen.add(group + ':' + artifact)) { - //println("+$key") - cfg.dependencies.add(project.dependencies.create(key)) - } - } - } - } - - void addFile(file, info) { - boolean pack = parent.offline.get() || info.url.isEmpty() - - // If it's a offline jar just always pack - if (!pack) { - try { - var remote = new URL("${info.url}.sha1").getText('UTF-8') - pack = info.sha1 != remote - } catch (FileNotFoundException e) { - pack = !info.url.startsWith('https://libraries.minecraft.net/') - // Oh noes its not there! - } - } - - if (pack) { - println("Adding: $file.absolutePath") - parent.from(file) { - rename { "maven/$info.path" } - } - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJson.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJson.groovy deleted file mode 100644 index b040722..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InstallerJson.groovy +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.json.JsonBuilder -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -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.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.bundling.AbstractArchiveTask - -import java.nio.file.Files - -abstract class InstallerJson extends DefaultTask { - @OutputFile abstract RegularFileProperty getOutput() - @InputFiles abstract ConfigurableFileCollection getInput() - @Input @Optional final Map libraries = new LinkedHashMap<>() - @Input Map json = new LinkedHashMap<>() - @InputFile abstract RegularFileProperty getIcon() - @Input abstract Property getLauncherJsonName() - @Input abstract Property getLogo() - @Input abstract Property getMirrors() - @Input abstract Property getWelcome() - - InstallerJson() { - launcherJsonName.convention('/version.json') - logo.convention('/big_logo.png') - mirrors.convention('https://files.minecraftforge.net/mirrors-2.0.json') - welcome.convention("Welcome to the ${project.name.capitalize()} installer.") - output.convention(project.layout.buildDirectory.file('libs/install_profile.json')) - - project.afterEvaluate { - [ - project.tasks.universalJar, - project.tasks.serverShimJar - ].forEach { packed -> - dependsOn(packed) - input.from packed.archiveFile - } - } - } - - @TaskAction - protected void exec() { - def libs = libraries - [ - project.tasks.universalJar, - project.tasks.serverShimJar - ].forEach { AbstractArchiveTask packed -> - def info = Util.getMavenInfoFromTask(packed) - libs.put(info.name, [ - name: info.name, - downloads: [ - artifact: [ - path: info.path, - url: "https://maven.minecraftforge.net/$info.path", - sha1: packed.archiveFile.get().asFile.sha1(), - size: packed.archiveFile.get().asFile.length() - ] - ] - ]) - } - json.libraries = libs.values().sort{a,b -> a.name.compareTo(b.name)} - json.icon = "data:image/png;base64," + new String(Base64.getEncoder().encode(Files.readAllBytes(icon.get().asFile.toPath()))) - json.json = launcherJsonName.get() - json.logo = logo.get() - if (!mirrors.get().isEmpty()) - json.mirrorList = mirrors.get() - json.welcome = welcome.get() - - Files.writeString(output.get().getAsFile().toPath(), new JsonBuilder(json).toPrettyString()) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MCPSetupFilePointer.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MCPSetupFilePointer.groovy deleted file mode 100644 index 1ef72ec..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MCPSetupFilePointer.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import net.minecraftforge.util.data.json.JsonData -import net.minecraftforge.forgedev.tasks.mcp.MCPSetupFiles -import org.gradle.api.DefaultTask -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -import javax.inject.Inject - -@CompileStatic -abstract class MCPSetupFilePointer extends DefaultTask { - abstract @InputFile RegularFileProperty getInput() - abstract @Input Property getEntry() - abstract @OutputFile RegularFileProperty getOutput() - - protected abstract @Inject ProjectLayout getLayout() - - @Inject - MCPSetupFilePointer() { - output.convention(layout.buildDirectory.file("setupMCP/$name")) - } - - @TaskAction - @CompileDynamic - protected void exec() { - var setupFiles = JsonData.fromJson(input.asFile.get(), MCPSetupFiles) - output.asFile.get().bytes = new File(setupFiles."${entry.get()}").bytes - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MergeJars.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MergeJars.groovy deleted file mode 100644 index 0429fb6..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/MergeJars.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.CompileStatic -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream -import java.util.zip.ZipOutputStream - -@CompileStatic -abstract class MergeJars extends DefaultTask { - MergeJars() { - output.convention(project.layout.buildDirectory.dir(name).map { it.file('output.jar') }) - } - - @TaskAction - void run() { - def jars = inputJars.files - - try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output.get().asFile))) { - for (def jar : jars) { - try (ZipInputStream zin = new ZipInputStream(new FileInputStream(jar))) { - ZipEntry entry - while ((entry = zin.getNextEntry()) !== null) { - ZipEntry _new = new ZipEntry(entry.getName()) - _new.setTime(0) //SHOULD be the same time as the main entry, but NOOOO _new.setTime(entry.getTime()) throws DateTimeException, so you get 0, screw you! - zout.putNextEntry(_new) - zin.transferTo(zout) - } - } - } - } - } - - abstract @InputFiles ConfigurableFileCollection getInputJars() - - abstract @OutputFile RegularFileProperty getOutput() -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ObjectTarget.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ObjectTarget.groovy deleted file mode 100644 index e2bc0a3..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/ObjectTarget.groovy +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.EqualsAndHashCode -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -@EqualsAndHashCode -public class ObjectTarget implements Comparable { - @Input - String owner - - @Input - String name - - @Input - @Optional - String desc - - @Override - String toString() { - if (desc == null) - return owner + '.' + name - return owner + '.' + name + desc - } - - @Override - int compareTo(ObjectTarget o) { - return toString() <=> o.toString() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/Util.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/Util.groovy deleted file mode 100644 index d82b881..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/Util.groovy +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.json.JsonSlurper -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import groovy.transform.stc.ClosureParams -import groovy.transform.stc.SimpleType -import net.minecraftforge.forgedev.legacy.values.LibraryInfo -import net.minecraftforge.forgedev.legacy.values.MinimalResolvedArtifact -import net.minecraftforge.gradleutils.shared.SharedUtil -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.provider.HasConfigurableValue -import org.gradle.api.provider.MapProperty -import org.gradle.api.provider.Provider -import org.gradle.api.provider.SetProperty -import org.gradle.api.tasks.bundling.AbstractArchiveTask -import org.objectweb.asm.ClassReader -import org.objectweb.asm.Opcodes -import org.objectweb.asm.tree.ClassNode - -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpResponse -import java.security.MessageDigest -import java.util.concurrent.Semaphore -import java.util.zip.ZipEntry -import java.util.zip.ZipInputStream - -@CompileStatic -final class Util { - public static final int ASM_LEVEL = Opcodes.ASM9 - private static final HttpClient HTTP = HttpClient.newBuilder().build() - - /** - * @deprecated Meta-programming like this is highly discouraged since it hurts IDE linting and is very hard to maintain. Move to either using static method or static Kotlin extensions if using Kotlin DSL. - */ - @Deprecated(forRemoval = true) - static void init() { - UtilExtensions.init() - } - - @CompileStatic - @Deprecated(forRemoval = true) - static Map getArtifacts(Configuration config) { - var ret = [:] - var semaphore = new Semaphore(1, true) - config.resolvedConfiguration.resolvedArtifacts.parallelStream().forEachOrdered(dep -> { - var info = getMavenInfoFromDep(dep) - var domain = 'libraries.minecraft.net' - var url = "https://$domain/$info.path" - if (!checkExists(url)) - url.values[0] = 'maven.minecraftforge.net' - - var sha1 = sha1(dep.file) - - semaphore.acquire() - ret[info.key] = [ - name: info.name, - downloads: [ - artifact: [ - path: info.path, - url: url.toString(), - sha1: sha1, - size: dep.file.length() - ] - ] - ] - semaphore.release() - }) - return ret - } - - @CompileDynamic - @Deprecated(forRemoval = true) - static String[] getClasspath(Project project, Map libs, String artifact) { - def ret = [] - artifactTreeOld(project, artifact).each { key, lib -> - libs[lib.name] = lib - if (lib.name != artifact) - ret.add(lib.name) - } - return ret - } - - @CompileDynamic - static String[] getClasspath(Project project, MapProperty libs, String artifact) { - var ret = [] - artifactTree(project, artifact).get().forEach { key, lib -> - libs.put(lib.name(), lib) - if (lib.name() != artifact) - ret.add(lib.name()) - } - return ret - } - - @CompileDynamic - static String[] getClasspath(Project project, SetProperty libs, String artifact) { - var ret = [] - var artifacts = artifactTree(project, artifact).get() - var libraries = LibraryInfo.from(artifacts.values()) - libraries.forEach { key, lib -> - libs.add(artifacts.get(lib.name())) - if (lib.name() != artifact) - ret.add(lib.name()) - } - return ret - } - - @CompileStatic - static Map getMavenInfoFromDep(ResolvedArtifact dep) { - return getMavenInfoFromMap([ - group: dep.moduleVersion.id.group, - name: dep.moduleVersion.id.name, - version: dep.moduleVersion.id.version, - classifier: dep.classifier, - extension: dep.extension - ]) - } - - static Map getMavenInfoFromTask(AbstractArchiveTask task) { - return getMavenInfoFromMap([ - group: task.project.group.toString(), - name: task.project.name, - version: task.project.version.toString(), - classifier: task.archiveClassifier.get(), - extension: task.archiveExtension.get() - ]) - } - - static Map getMavenInfoFromTask(Task task, String classifier) { - return getMavenInfoFromMap([ - group: task.project.group.toString(), - name: task.project.name, - version: task.project.version.toString(), - classifier: classifier, - extension: 'jar' - ]) - } - - private static Map getMavenInfoFromMap(Map art) { - var key = "$art.group:$art.name" - var name = "$art.group:$art.name:$art.version" - var path = "${art.group.replace('.', '/')}/$art.name/$art.version/$art.name-$art.version" - if (art.classifier !== null) { - name += ":$art.classifier" - path += "-$art.classifier" - } - if ('jar' != art.extension) { - name += "@$art.extension" - path += ".$art.extension" - } else { - path += ".jar" - } - return [ - key: key.toString(), - name: name.toString(), - path: path.toString(), - art: art - ] - } - - @CompileDynamic - static String iso8601Now() { new Date().iso8601() } - - static String sha1(File file) { - MessageDigest md = MessageDigest.getInstance('SHA-1') - file.eachByte(4096) { byte[] bytes, int size -> - md.update(bytes, 0, size) - } - return md.digest().collect(this.&toHex).join('') - } - static String sha256(File file) { - MessageDigest md = MessageDigest.getInstance('SHA-256') - file.eachByte(4096) { byte[] bytes, int size -> - md.update(bytes, 0, size) - } - return md.digest().collect(this.&toHex).join('') - } - - @PackageScope static String toHex(byte bite) { - return String.format('%02x', bite) - } - - @CompileDynamic - static Provider> artifactTree(Project project, String artifact, boolean transitive = true) { - return MinimalResolvedArtifact.from(project, project.configurations.detachedConfiguration( - project.dependencies.create(artifact) - ).tap { it.transitive = transitive }).map { list -> - var map = new LinkedHashMap(list.size()) - for (var minimal in list) { - map.put(minimal.info().key(), minimal) - } - return map - } - } - - @CompileDynamic - @Deprecated(forRemoval = true) - private static Map artifactTreeOld(Project project, String artifact, boolean transitive = true) { - if (!project.ext.has('tree_resolver')) - project.ext.tree_resolver = 1 - def cfg = project.configurations.create('tree_resolver_' + project.ext.tree_resolver++) - cfg.transitive = transitive - def dep = project.dependencies.create(artifact) - cfg.dependencies.add(dep) - def files = cfg.resolve() - return getArtifacts(cfg) - } - - static boolean checkExists(String url) { - try { - return HTTP.send(HttpRequest.newBuilder(new URI(url)) - .method('HEAD', HttpRequest.BodyPublishers.noBody()).build(), HttpResponse.BodyHandlers.discarding() - ).statusCode() === 200 - } catch (Exception e) { - if (e.toString().contains('unable to find valid certification path to requested target')) - throw new RuntimeException("Failed to connect to $url: Missing certificate root authority, try updating Java") - throw e - } - } - - @CompileDynamic - static String getLatestForgeVersion(String mcVersion) { - final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8')) - final ver = json.promos["$mcVersion-latest"] - ver === null ? null : (mcVersion + '-' + ver) - } - - static void processClassNodes(File file, @ClosureParams(value = SimpleType, options = 'org.objectweb.asm.tree.ClassNode') Closure process) { - file.withInputStream { i -> - new ZipInputStream(i).withCloseable { zin -> - ZipEntry zein - while ((zein = zin.nextEntry) !== null) { - if (zein.name.endsWith('.class')) { - var node = new ClassNode(ASM_LEVEL) - new ClassReader(zin).accept(node, 0) - process(node) - } - } - } - } - } - - @CompileDynamic - static String asArtifactString(Object artifact) { - def value = SharedUtil.unpack(artifact) - if (!(value instanceof Dependency)) - throw new IllegalArgumentException("Cannot get non-dependency as artifact string! Found: $value.class") - - def classifier = value.hasProperty('classifier') ? ":$value.classifier" : '' - def extension = value.hasProperty('artifactType') ? "@$value.artifactType" : value.hasProperty('extension') ? "@$value.extension" : '' - classifier = classifier != ':null' ? classifier : '' - extension = extension != '@null' ? extension : '' - - "$value.group:$value.name:$value.version$classifier$extension".toString() - } - - static R finalize(Project project, R ret) { - ret.disallowChanges(); - ret.finalizeValueOnRead(); - return ret; - } - - static String capitalize(String s) { - return s.capitalize(); - } - - static String kebab(String s) { - s.replaceAll('([A-Z])', '-$1').toLowerCase() - } - - private static final Action DO_NOTHING = input -> {}; - @SuppressWarnings("unchecked") - static Action noop() { - return (Action)DO_NOTHING; - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/UtilExtensions.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/UtilExtensions.groovy deleted file mode 100644 index 6e7b4e2..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/UtilExtensions.groovy +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.json.JsonBuilder -import groovy.json.JsonSlurper -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.jetbrains.annotations.Nullable - -import java.security.MessageDigest -import java.text.SimpleDateFormat - -@CompileStatic -final class UtilExtensions { - private UtilExtensions() {} - - @PackageScope static void init() {} - - static { - initDynamic() - } - - @CompileDynamic - private static void initDynamic() { - File.metaClass.sha1 = { sha1(delegate) } - File.metaClass.getSha1 = { getSha1(delegate) } - File.metaClass.sha256 = { sha256(delegate) } - File.metaClass.getSha256 = { sha256(delegate) } - - File.metaClass.json = { json(delegate) } - File.metaClass.getJson = { getJson(delegate) } - File.metaClass.setJson = { json -> setJson(delegate, json) } - - Date.metaClass.iso8601 = { iso8601(delegate) } - - String.metaClass.rsplit = { String del, int limit = -1 -> rsplit(delegate, del, limit) } - } - - //region Hashing - static String sha1(File self) { - var md = MessageDigest.getInstance("SHA-1") - self.eachByte(4096) { byte[] bytes, int size -> - md.update(bytes, 0, size) - } - md.digest().collect(UtilExtensions.&toHex).join('') - } - - static @Nullable String getSha1(File self) { - !self.exists() ? null : sha1(self) - } - - static String sha256(File self) { - var md = MessageDigest.getInstance("SHA-256") - self.eachByte(4096) { byte[] bytes, int size -> - md.update(bytes, 0, size) - } - md.digest().collect(UtilExtensions.&toHex).join('') - } - - static @Nullable String getSha256(File self) { - !self.exists() ? null : sha256(self) - } - //endregion - - //region JSON - static Object json(File self) { - new JsonSlurper().parse(self) - } - - static def getJson(File self) { - self.exists() ? json(self) : [:] - } - - static void setJson(File self, def json) { - self.text = new JsonBuilder(json).toPrettyString() - } - //endregion - - //region Date - static String iso8601(Date self) { - var format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - var result = format.format(self) - return result[0..21] + ':' + result[22..-1] - } - //endregion - - //region Strings - static List rsplit(String self, String del, int limit = -1) { - var lst = new ArrayList() - int x = 0 - int idx - String tmp = self - while ((idx = tmp.lastIndexOf(del)) != -1 && (limit === -1 || x++ < limit)) { - lst.add(0, tmp.substring(idx + del.length(), tmp.length())) - tmp = tmp.substring(0, idx) - } - lst.add(0, tmp) - return lst - } - //endregion - - private static String toHex(byte bite) { - return '%02x'.formatted(bite) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/WriteManifest.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/WriteManifest.groovy deleted file mode 100644 index 276c9f8..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/WriteManifest.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.tasks - -import groovy.transform.CompileStatic -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.file.CopySpec -import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.java.archives.internal.ManifestInternal -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 org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar -import org.gradle.language.jvm.tasks.ProcessResources - -import javax.inject.Inject - -@CompileStatic -abstract class WriteManifest extends DefaultTask { - static TaskProvider register(Project project, TaskProvider jar) { - project.tasks.register('writeManifest', WriteManifest).tap { task -> - project.tasks.named('processResources', ProcessResources) { - it.dependsOn(task) - } - - project.afterEvaluate { - try (var os = new ByteArrayOutputStream()) { - // RATIONALE: ManifestInternal has not changed since Gradle 2.14 - // Due to the hacky nature of needing the proper manifest in the resources, this is the only good way of doing this - // The DefaultManifest object cannot be serialized into the Gradle cache, and the normal Manifest interface does not have this method - // This should be the only Gradle internals we need to use in all of ForgeDev, thankfully - (jar.get().manifest as ManifestInternal).writeTo(os) - task.get().inputBytes.set(os.toByteArray()) - } - } - } - } - - protected abstract @Input Property getInputBytes() - protected abstract @OutputFile RegularFileProperty getOutput() - - @Inject - WriteManifest(ProjectLayout layout) { - // The output name is ALWAYS "MANIFEST.MF", and output cannot be changed - this.output.value(layout.projectDirectory.file("src/main/resources/META-INF/MANIFEST.MF")).disallowChanges() - } - - @TaskAction - void exec() { - this.output.get().asFile.bytes = this.inputBytes.get() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LatestForgeVersion.groovy b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LatestForgeVersion.groovy deleted file mode 100644 index 627da7b..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LatestForgeVersion.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.values - -import groovy.json.JsonSlurper -import groovy.transform.CompileDynamic -import groovy.transform.CompileStatic -import net.minecraftforge.util.download.DownloadUtils -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.Logger -import org.gradle.api.logging.Logging -import org.gradle.api.provider.Property -import org.gradle.api.provider.ValueSource -import org.gradle.api.provider.ValueSourceParameters -import org.gradle.api.provider.ValueSourceSpec -import org.jetbrains.annotations.Nullable - -@CompileStatic -abstract class LatestForgeVersion implements ValueSource { - private static final Logger LOGGER = Logging.getLogger(LatestForgeVersion) - - private static final String PROMOTIONS_SLIM = 'https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json' - - static interface Parameters extends ValueSourceParameters { - Property getOffline(); - - RegularFileProperty getPromotions(); - - Property getMinecraftVersion(); - } - - static Action> parameters(Project project, String minecraftVersion) { - return { ValueSourceSpec spec -> - spec.parameters { parameters -> - parameters.offline.set(project.gradle.startParameter.offline) - parameters.promotions.set(project.layout.buildDirectory.file('promotions_slim.json')) - parameters.minecraftVersion.set(minecraftVersion) - } - } - } - - @Override - @Nullable String obtain() { - try { - final promotions = getParameters().getPromotions().getAsFile().get() - - // Always attempt to re-download promotions UNLESS we are offline - if (getParameters().getOffline().getOrElse(false)) { - if (!promotions.exists()) - throw new IllegalStateException('Cannot download Forge promotions while offline! Please build the project at least once while online.') - } else { - DownloadUtils.downloadFile(promotions, PROMOTIONS_SLIM) - } - - return parse(promotions) - } catch (Exception e) { - LOGGER.error('ERROR: Failed to get latest Forge version. Checks using this data will be skipped.', e) - return null - } - } - - @CompileDynamic - private String parse(File promotions) { - final mcVersion = parameters.minecraftVersion.get() - final json = new JsonSlurper().parseText(promotions.getText('UTF-8')) - final ver = json.promos["$mcVersion-latest"] - ver === null ? null : (mcVersion + '-' + ver) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfo.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfo.java index c01c7b7..21a564d 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfo.java +++ b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfo.java @@ -4,7 +4,8 @@ */ package net.minecraftforge.forgedev.legacy.values; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.Util; +import net.minecraftforge.util.hash.HashFunction; import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.Transformer; @@ -12,12 +13,10 @@ import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.AbstractArchiveTask; -import org.gradle.plugins.ide.eclipse.model.Library; import java.io.File; import java.io.Serializable; import java.util.Collection; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -55,7 +54,7 @@ public LibraryInfo(String name, String path, String url, String sha1, long size) } public LibraryInfo(MavenInfo info, File file, String url) { - this(info.name(), info.path(), url, Util.sha1(file), file.length()); + this(info.name(), info.path(), url, HashFunction.SHA1.sneakyHash(file), file.length()); } public LibraryInfo validateUrl(boolean offline) { @@ -81,7 +80,7 @@ public static LibraryInfo from(MinimalResolvedArtifact dependency) { url = "https://maven.minecraftforge.net/" + info.path(); var file = dependency.file(); - var sha1 = Util.sha1(dependency.file()); + var sha1 = HashFunction.SHA1.sneakyHash(dependency.file()); return new LibraryInfo( info.name(), diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfoSource.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfoSource.java deleted file mode 100644 index fdeb909..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/LibraryInfoSource.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.legacy.values; - -import net.minecraftforge.forgedev.legacy.tasks.Util; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.ValueSource; -import org.gradle.api.provider.ValueSourceParameters; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.Semaphore; - -public abstract class LibraryInfoSource implements ValueSource, LibraryInfoSource.Parameters> { - interface Parameters extends ValueSourceParameters { - ListProperty getDependencies(); - } - - @Override - public Map obtain() { - var dependencies = getParameters().getDependencies().get(); - - var ret = new HashMap(dependencies.size()); - var semaphore = new Semaphore(1, true); - dependencies.parallelStream().forEachOrdered(dependency -> { - var info = dependency.info(); - var url = "https://libraries.minecraft.net/" + info.path(); - if (!Util.checkExists(url)) - url = "https://maven.minecraftforge.net/" + info.path(); - - var file = dependency.file(); - var sha1 = Util.sha1(dependency.file()); - - try { - semaphore.acquire(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while trying to get library info for " + dependency.info(), e); - } - - ret.put(info.key(), new LibraryInfo( - info.name(), - info.path(), - url, - sha1, - file.length() - )); - - semaphore.release(); - }); - - return ret; - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MavenInfo.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MavenInfo.java index fbf6139..a8fc2aa 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MavenInfo.java +++ b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MavenInfo.java @@ -15,6 +15,7 @@ import org.gradle.internal.component.external.model.ModuleComponentArtifactIdentifier; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.Serializable; import java.text.MessageFormat; diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MinimalResolvedArtifact.java b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MinimalResolvedArtifact.java index a7343d5..7a2e256 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MinimalResolvedArtifact.java +++ b/src/main/groovy/net/minecraftforge/forgedev/legacy/values/MinimalResolvedArtifact.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.forgedev.legacy.values; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.Util; import org.gradle.api.Project; import org.gradle.api.Transformer; import org.gradle.api.artifacts.Configuration; diff --git a/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatches.java b/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatches.java new file mode 100644 index 0000000..50f402d --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatches.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.patches; + +import net.minecraftforge.forgedev.tasks.patching.binary.ApplyBinPatches; +import net.minecraftforge.forgedev.tasks.patching.binary.CreateBinPatches; +import org.gradle.api.Action; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskProvider; + +import java.io.File; + +public interface BinaryPatches { + void setClean(Provider value); + void setDirty(Provider value); + void setDirty(RegularFileProperty value); + + + TaskProvider getCreate(); + TaskProvider create(Action action); + TaskProvider getApply(); + TaskProvider apply(Action action); +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatchesImpl.java b/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatchesImpl.java new file mode 100644 index 0000000..49b7166 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/patches/BinaryPatchesImpl.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.patches; + +import net.minecraftforge.forgedev.ForgeDevExtension; +import net.minecraftforge.forgedev.ForgeDevPlugin; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.patching.binary.ApplyBinPatches; +import net.minecraftforge.forgedev.tasks.patching.binary.CreateBinPatches; +import org.gradle.api.Action; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.api.tasks.TaskProvider; + +import javax.inject.Inject; +import java.io.File; + +public abstract class BinaryPatchesImpl implements BinaryPatches { + private final String name; + private final ForgeDevExtension extension; + private final TaskProvider create; + private final TaskProvider apply; + + @Inject + public BinaryPatchesImpl(String name, ForgeDevExtension extension, TaskContainer tasks) { + this.name = name; + this.extension = extension; + this.create = tasks.register(name + "CreateBinPatches", CreateBinPatches.class); + this.apply = tasks.register(name + "ApplyBinPatches", ApplyBinPatches.class); + this.apply.configure(task -> { + task.getApply().setFrom(create.flatMap(SingleFileOutput::getOutput)); + task.getData().set(true); + task.getUnpatched().set(true); + }); + } + + public TaskProvider getCreate() { + return create; + } + public TaskProvider create(Action action) { + getCreate().configure(action); + return getCreate(); + } + public TaskProvider getApply() { + return apply; + } + public TaskProvider apply(Action action) { + getApply().configure(action); + return getApply(); + } + + + + @Override + public void setClean(Provider value) { + create(task -> task.getClean().setFrom(value)); + apply(task -> task.getClean().setFrom(value)); + } + + @Override + public void setDirty(Provider value) { + create(task -> task.getDirty().setFrom(value)); + } + + @Override + public void setDirty(RegularFileProperty value) { + create(task -> task.getDirty().setFrom(value)); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/patches/Patches.java b/src/main/groovy/net/minecraftforge/forgedev/patches/Patches.java new file mode 100644 index 0000000..8f3662c --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/patches/Patches.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.patches; + +import net.minecraftforge.forgedev.base.PatcherBase; +import net.minecraftforge.forgedev.tasks.patching.diff.ApplyPatches; +import net.minecraftforge.forgedev.tasks.patching.diff.GeneratePatches; +import org.gradle.api.Action; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.tasks.TaskProvider; + +public interface Patches { + PatcherBase getBase(); + void setBase(PatcherBase base); + + String getName(); + TaskProvider getApply(); + default void apply(Action action) { + getApply().configure(action); + } + + TaskProvider getMake(); + default void make(Action action) { + getMake().configure(action); + } + + DirectoryProperty getPatches(); + DirectoryProperty getPatched(); +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/patches/PatchesImpl.java b/src/main/groovy/net/minecraftforge/forgedev/patches/PatchesImpl.java new file mode 100644 index 0000000..cfa7ed2 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/patches/PatchesImpl.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.patches; + +import net.minecraftforge.forgedev.ForgeDevExtension; +import net.minecraftforge.forgedev.base.PatcherBase; +import net.minecraftforge.forgedev.tasks.patching.diff.ApplyPatches; +import net.minecraftforge.forgedev.tasks.patching.diff.GeneratePatches; +import org.gradle.api.Project; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.tasks.TaskProvider; + +import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.Files; + +public abstract class PatchesImpl implements Patches { + private final String name; + private final TaskProvider apply; + private final TaskProvider make; + + private PatcherBase base; + + protected abstract @Inject ObjectFactory getObjects(); + protected abstract @Inject ProviderFactory getProviders(); + + @Inject + public PatchesImpl(ForgeDevExtension extension, String name, Project project) { + this.name = name; + this.apply = project.getTasks().register("applyPatches", ApplyPatches.class); + this.make = project.getTasks().register("makePatches", GeneratePatches.class); + var buildDir = project.getLayout().getProjectDirectory(); + var updating = extension.getProblems().test("net.minecraftforge.forge.build.updating"); + + apply.configure(task -> { + task.getInput().setFrom(getBase().getNamedSources()); + task.getPatches().setFrom(getPatches()); + task.getOutputDirectory().set(getPatched()); + task.getFailOnError().set(false); + + if (updating) { + task.getMode().set("fuzzy"); + task.getRejects().setFrom(buildDir.dir("rejects")); + task.getArchiveRejects().unsetConvention(); + task.getFailOnError().set(false); + } + }); + + make.configure(task -> { + task.setOnlyIf(t -> getPatches().isPresent()); + task.getInput().setFrom(getBase().getNamedSources()); + task.getModified().setFrom(getPatched()); + task.getAutoHeader().set(true); + task.getLineEndings().convention("\n"); + task.getOutputDirectory().set(getPatches()); + }); + + // Is this needed? AfterEvaluate should be avoided + project.afterEvaluate(p -> { + // Automatically create the patches folder if it does not exist + try { + Files.createDirectories(getPatches().get().getAsFile().toPath()); + } catch (IOException e) { + throw new RuntimeException("Failed to create patches folder", e); + } + }); + } + + @Override + public PatcherBase getBase() { + return base; + } + @Override + public void setBase(PatcherBase base) { + this.base = base; + } + @Override + public String getName() { + return name; + } + @Override + public TaskProvider getApply() { + return apply; + } + @Override + public TaskProvider getMake() { + return make; + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/DeleteDirectoryTask.java b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/DeleteDirectoryTask.java new file mode 100644 index 0000000..6c8b815 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/DeleteDirectoryTask.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.publishvalidate; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; + +import java.io.File; +import java.util.Objects; + +public abstract class DeleteDirectoryTask extends DefaultTask { + @Internal + abstract RegularFileProperty getDirectory(); + + public DeleteDirectoryTask() { + } + + @TaskAction + public void exec() { + var dir = getDirectory().getAsFile().get(); + delete(dir); + } + + private void delete(File dir) { + var files = dir.listFiles(); + if (files != null) { + for (var file : files) { + if (file.isDirectory()) + delete(file); + else + file.delete(); + } + } + dir.delete(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidatePublish.java b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidatePublish.java new file mode 100644 index 0000000..69b5b9e --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidatePublish.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.publishvalidate; + +import net.minecraftforge.forgedev.ForgeDevExtension; +import net.minecraftforge.forgedev.Util; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.attributes.Usage; +import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.maven.MavenPublication; +import org.gradle.api.tasks.TaskProvider; + +import javax.inject.Inject; +import javax.naming.spi.ObjectFactory; + +public class ValidatePublish { + static final String NAME = "validate"; + static final String CONFIG_PRODUCER = NAME + "Producer"; + static final String CONFIG_CONSUMER = NAME + "Consumer"; + static final String TASK_NAME = NAME + "Published"; + + private static boolean enabled(Project project) { + return "true".equalsIgnoreCase(project.getProviders().gradleProperty("net.minecraftforge.forgedev.validate.publish").get()); + } + // Creates a consumable {NAME} configuration, with the usage of {NAME} + // and forces all publication tasks publish to a flat folder in our builder folder + public static void onApplyMavenPublish(Project project) { + if (!enabled(project)) + return; + + configurtion(project, true); + + var targetMaven = project.getLayout().getBuildDirectory().file("validate-publish"); + + var cleanupTask = project.getTasks().register("cleanupValidatePublish", DeleteDirectoryTask.class, task -> { + task.getDirectory().set(targetMaven); + task.getOutputs().upToDateWhen(t -> false); + }); + + project.getPluginManager().withPlugin("maven-publish", maven -> { + var publishing = project.getExtensions().getByType(PublishingExtension.class); + // Publish to our + publishing.getRepositories().maven(repo -> { + repo.setName(NAME); + repo.setUrl(targetMaven.get().getAsFile().toURI()); + }); + + // Add the output directory as an artifact of the ALL publish task + var publishAll = project.getTasks().named("publishAllPublicationsTo" + Util.capitalize(NAME) + "Repository"); + publishAll.configure(task -> task.setGroup(null)); + project.getArtifacts().add(CONFIG_PRODUCER, targetMaven, cfg -> cfg.builtBy(publishAll)); + + publishing.getPublications().configureEach(pub -> { + if (!(pub instanceof MavenPublication)) + return; + var publishToDir = project.getTasks().named("publish" + Util.capitalize(pub.getName()) + "PublicationTo" + Util.capitalize(NAME) + "Repository"); + publishToDir.configure(task -> { + task.setGroup(null); + // All publications share this output directory, having every task depend on the cleanup + // should make the cleanup run before any of them + task.dependsOn(cleanupTask); + task.getOutputs().upToDateWhen(t -> false); + }); + }); + }); + } + + public static Consumer registerConsumer(Project project, ForgeDevExtension extension) { + // Create consumer configuration + var cfg = configurtion(project, false); + + // Add all projects to consumer configuration + if (enabled(project)) { + project.allprojects(proj -> { + var dep = project.getDependencies().create(proj); + project.getDependencies().add(CONFIG_CONSUMER, dep); + }); + } + + var task = project.getTasks().register(TASK_NAME, ValidateTask.class, extension); + task.configure(t -> t.getFiles().setFrom(cfg)); + + return project.getObjects().newInstance(Consumer.class, cfg, task); + } + + private static Configuration configurtion(Project project, boolean producer) { + return project.getConfigurations().create(producer ? CONFIG_PRODUCER : CONFIG_CONSUMER, cfg -> { + cfg.setCanBeConsumed(producer); + cfg.setCanBeResolved(!producer); + cfg.attributes(attrs -> { + attrs.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, NAME)); + }); + }); + } + + public static abstract class Consumer { + private final Configuration config; + private final TaskProvider task; + + @Inject public abstract ObjectFactory getObjects(); + + @Inject + public Consumer(Configuration config, TaskProvider task) { + this.config = config; + this.task = task; + } + + public Configuration getConfig() { + return config; + } + public void config(Action action) { + action.execute(config); + } + + public TaskProvider getTask() { + return task; + } + public void task(Action action) { + //getTask().configure(action); + action.execute(task.get()); + } + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidateTask.java b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidateTask.java new file mode 100644 index 0000000..802bcba --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/publishvalidate/ValidateTask.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.publishvalidate; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import net.minecraftforge.forgedev.Constants; +import net.minecraftforge.forgedev.ForgeDevExtension; +import net.minecraftforge.forgedev.legacy.values.MavenInfo; +import net.minecraftforge.forgedev.values.MavenArtifact; +import net.minecraftforge.util.download.DownloadUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskAction; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class ValidateTask extends DefaultTask { + private static final Gson GSON = new GsonBuilder() + .disableHtmlEscaping() + .setPrettyPrinting() + .create(); + public abstract @InputFiles ConfigurableFileCollection getFiles(); + public abstract @Input MapProperty getBaseVersions(); + public abstract @Input Property getBaseBranch(); + public abstract @Optional @Input Property getVersionPrefix(); + public abstract @Internal DirectoryProperty getCacheDirectory(); + + protected abstract @Inject ObjectFactory getObjects(); + + private final ForgeDevExtension extension; + + @Inject + public ValidateTask(ForgeDevExtension extension) { + this.extension = extension; + this.getCacheDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName())); + } + + @TaskAction + public void exec() throws IOException { + getLogger().lifecycle("Executing ValidateTask"); + for (var dir : getFiles()) { + getLogger().lifecycle("Dir: {}", dir.getAbsoluteFile()); + var base = extension.gitVersion( parameters -> { + parameters.getCommit().set(getBaseBranch()); + // {project}/build/validate-publish -> {project} + parameters.getProjectDirectory().set(dir.getParentFile().getParentFile()); + }).get(); + if (getVersionPrefix().isPresent()) + base = getVersionPrefix().get() + '-' + base; + getLogger().lifecycle(" Base Version: {}", base); + + var files = new ArrayList(); + try (var stream = Files.walk(dir.toPath())) { + stream.filter(Files::isRegularFile).map(Path::toFile).forEach(files::add); + } + + // Gather all artifacts that this project has published. This **should** only be one artifact. But might as well make it generic + var artifacts = new HashMap>(); + for (var file : files) { + if (isMetadata(file)) + continue; + + var info = MavenArtifact.from(getObjects(), dir, file); + getLogger().lifecycle("File: {}", info.getDescriptor()); + var key = MavenArtifact.from(getObjects(), info.group() + ':' + info.name() + ':' + info.version()); + var suffix = (info.classifier() == null ? "" : '-' + info.classifier()) + '.' + info.extension(); + artifacts.computeIfAbsent(key, k -> new HashMap<>()).put(suffix, file); + } + + var cacheDir = getCacheDirectory().getAsFile().get(); + + for (var entry : artifacts.entrySet()) { + var artifact = entry.getKey(); + var baseVersion = artifact.withVersion(base); + var expected = getKnown(baseVersion); + getLogger().lifecycle(" Known: {}", entry.getValue().keySet()); + getLogger().lifecycle(" Expected: {}", expected); + + for (var e2 : entry.getValue().entrySet()) { + var suffix = e2.getKey(); + var file = e2.getValue(); + var path = baseVersion.getDirectory() + '/' + baseVersion.name() + '-' + baseVersion.version() + suffix; + var target = new File(cacheDir, path); + if (!target.exists()) + DownloadUtils.downloadFile(target, Constants.FORGE_MAVEN + path); + compare(file, artifact.version(), target, baseVersion.version()); + } + } + } + } + + private Set getKnown(MavenArtifact artifact) { + var url = Constants.FORGE_FILES + artifact.getDirectory() + "meta.json"; + var jsonStr = DownloadUtils.tryDownloadString(url); + if (jsonStr == null) { + getLogger().lifecycle(" No metadata file found at {}", url); + return Set.of(); + } + + var meta = GSON.>>>fromJson(jsonStr, new TypeToken<>(){}.getType()); + var ret = new HashSet(); + for (var e : meta.get("classifiers").entrySet()) { + for (var ext : e.getValue().keySet()) + ret.add((e.getKey().isEmpty() ? "" : '-' + e.getKey()) + '.' + ext); + } + return ret; + } + + private static final Set BLACKLIST = Set.of("md5", "sha1", "sha256", "sha512", "pom", "module"); + private static boolean isMetadata(File path) { + var name = path.getName(); + var ext = name.substring(name.lastIndexOf('.') + 1); + return BLACKLIST.contains(ext) || name.startsWith("maven-metadata.xml"); + } + + private void compare(File current, String currentVersion, File base, String baseVersion) { + + } + + private Map loadZip(File file) { + return null; + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/runs/Run.java b/src/main/groovy/net/minecraftforge/forgedev/runs/Run.java new file mode 100644 index 0000000..3637cdf --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/runs/Run.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.runs; + +import net.minecraftforge.forgedev.tasks.launcher.SlimeLauncherEclipseConfiguration; +import net.minecraftforge.forgedev.tasks.launcher.SlimeLauncherExec; +import net.minecraftforge.forgedev.tasks.launcher.SlimeLauncherOptions; +import net.minecraftforge.forgedev.tasks.launcher.SlimeLauncherOptionsImpl; +import org.gradle.api.Action; +import org.gradle.api.Named; +import org.gradle.api.Project; +import org.gradle.api.attributes.Usage; +import org.gradle.api.file.FileCollection; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; + +import javax.inject.Inject; +import java.io.File; + +public abstract class Run implements Named { + private final String name; + private final Project project; + private final SlimeLauncherOptionsImpl options; + private final TaskProvider eclipse; + private final TaskProvider eclipseTest; + private final TaskProvider run; + private final TaskProvider runTest; + + protected abstract @Inject ObjectFactory getObjects(); + + @Inject + public Run(String name, Project project, TaskProvider genEclipseRuns, String version) { + this.name = name; + this.project = project; + this.options = getObjects().newInstance(SlimeLauncherOptionsImpl.class, name); + var metadata = getDefaultMetadata(project, version); + + var java = project.getExtensions().getByType(JavaPluginExtension.class); + var main = java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).get(); + var test = java.getSourceSets().named(SourceSet.TEST_SOURCE_SET_NAME).get(); + + this.eclipse = SlimeLauncherExec.registerEclipse(project, main, options, version, metadata, genEclipseRuns); + this.eclipseTest = SlimeLauncherExec.registerEclipse(project, test, options, version, metadata, genEclipseRuns); + + this.run = SlimeLauncherExec.register(project, main, options, version, metadata); + this.runTest = SlimeLauncherExec.register(project, test, options, version, metadata); + } + + private FileCollection getDefaultMetadata(Project project, String version) { + // This is hardcoded and im not a fan of it, but requires a bunch of work to change + // I'm thinking we add 'metadata' to options which we fill with a PatcherBase + // For now just hardcode this as joined + var metadataDep = project.getDependencyFactory().create("net.minecraft", "joined", version, "metadata", "zip"); + var metadataAttr = getObjects().named(Usage.class, "metadata"); + var metadataConfiguration = project.getConfigurations().detachedConfiguration(metadataDep); + metadataConfiguration.setTransitive(false); + metadataConfiguration.attributes(a -> a.attribute(Usage.USAGE_ATTRIBUTE, metadataAttr)); + return metadataConfiguration; + } + + @Override + public String getName() { + return this.name; + } + + public void metadata(FileCollection metadata) { + eclipse(task -> task.getMetadata().setFrom(metadata)); + eclipseTest(task -> task.getMetadata().setFrom(metadata)); + run(task -> task.getMetadata().setFrom(metadata)); + runTest(task -> task.getMetadata().setFrom(metadata)); + } + + public void metadata(Provider metadata) { + metadata(this.project.files(metadata)); + } + + public SlimeLauncherOptionsImpl getOptions() { + return this.options; + } + public SlimeLauncherOptionsImpl options(Action action) { + action.execute(getOptions()); + return getOptions(); + } + public TaskProvider getEclipse() { + return this.eclipse; + } + public TaskProvider eclipse(Action action) { + getEclipse().configure(action); + return getEclipse(); + } + public TaskProvider getEclipseTest() { + return this.eclipseTest; + } + + public TaskProvider eclipseTest(Action action) { + getEclipseTest().configure(action); + return getEclipseTest(); + } + public TaskProvider getRun() { + return this.run; + } + public TaskProvider run(Action action) { + getRun().configure(action); + return getRun(); + } + public TaskProvider getRunTest() { + return this.runTest; + } + public TaskProvider runTest(Action action) { + getRunTest().configure(action); + return getRunTest(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/DownloadHashedFile.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/DownloadHashedFile.java new file mode 100644 index 0000000..93691ea --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/DownloadHashedFile.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks; + +import net.minecraftforge.forgedev.ForgeDevTask; +import net.minecraftforge.util.download.DownloadUtils; +import net.minecraftforge.util.hash.HashFunction; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +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; + +// This is a quick replacement for https://github.com/michel-kraemer/gradle-download-task +// Which we only use for downloading crowdin, could expand to any hashed file, but for now it's mainly just for that +public abstract class DownloadHashedFile extends DefaultTask implements ForgeDevTask, SingleFileOutput { + public abstract @Input Property getSrc(); + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public DownloadHashedFile() { + this.getOutput().convention(this.getSrc().map(src -> { + var tmp = src.replace('\\', '/'); + var idx = src.lastIndexOf('/'); + var name = idx == -1 ? tmp : src.substring(idx + 1); + return this.getDefaultOutputFile(name).get(); + })); + + getOutputs().upToDateWhen(task -> false); + + onlyIf(task -> { + final boolean isOffline = getProject().getGradle().getStartParameter().isOffline(); + if (!isOffline) + return true; + + var output = getOutput().getAsFile().get(); + if (!output.exists()) + throw new IllegalStateException("Unable to download file '" + output.getName() + "' in offline mode."); + + return false; + }); + } + + @TaskAction + public void exec() throws IOException { + var output = getOutput().getAsFile().get(); + if (output.exists()) { + var existing_hash = HashFunction.SHA1.hash(output); + var remote_hash = DownloadUtils.downloadString(getSrc().get() + ".sha1"); + if (remote_hash.equals(existing_hash)) { + getState().setDidWork(false); + return; + } + } + + DownloadUtils.downloadFile(output, getSrc().get()); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/MergeJars.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/MergeJars.java new file mode 100644 index 0000000..b425810 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/MergeJars.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks; + +import net.minecraftforge.forgedev.ForgeDevTask; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +// This **could** be a standard Copy task, but Gradle still extracts ZipTrees to temporary directories, instead of dealing with them in memory. +// So to save hard drives we do it this way +public abstract class MergeJars extends DefaultTask implements ForgeDevTask, SingleFileOutput { + public abstract @InputFiles ConfigurableFileCollection getInputJars(); + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public MergeJars() { + this.getOutput().convention(this.getDefaultOutputFile()); + } + + @TaskAction + public void run() throws IOException { + var output = getOutput().get().getAsFile(); + if (output.getParentFile() != null) + Files.createDirectories(output.getParentFile().toPath()); + + try (var zout = new ZipOutputStream(new FileOutputStream(output))) { + for (var jar : getInputJars().getFiles()) { + try (var zin = new ZipInputStream(new FileInputStream(jar))) { + for (var entry = zin.getNextEntry(); entry != null; entry = zin.getNextEntry()) { + ZipEntry _new = new ZipEntry(entry.getName()); + _new.setTime(0); //SHOULD be the same time as the main entry, but NOOOO _new.setTime(entry.getTime()) throws DateTimeException, so you get 0, screw you! + zout.putNextEntry(_new); + zin.transferTo(zout); + } + } + } + } + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.groovy deleted file mode 100644 index 20e6675..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.groovy +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevProblems -import net.minecraftforge.forgedev.ForgeDevTask -import net.minecraftforge.gradleutils.shared.Tool -import net.minecraftforge.gradleutils.shared.ToolExecBase - -import javax.inject.Inject - -@CompileStatic -abstract class ToolExec extends ToolExecBase implements ForgeDevTask { - @Inject - ToolExec(Tool tool) { - super(tool) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.java new file mode 100644 index 0000000..44b7468 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/ToolExec.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks; + +import net.minecraftforge.forgedev.ForgeDevProblems; +import net.minecraftforge.forgedev.ForgeDevTask; +import net.minecraftforge.gradleutils.shared.Tool; +import net.minecraftforge.gradleutils.shared.ToolExecBase; + +import javax.inject.Inject; + +public abstract class ToolExec extends ToolExecBase implements ForgeDevTask { + @Inject + public ToolExec(Tool tool) { + super(tool); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/WriteManifest.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/WriteManifest.java new file mode 100644 index 0000000..98b5a90 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/WriteManifest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks; + +import net.minecraftforge.forgedev.Util; +import org.gradle.api.DefaultTask; +import org.gradle.api.Project; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.java.archives.Manifest; +import org.gradle.api.java.archives.internal.ManifestInternal; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.file.Files; + +public abstract class WriteManifest extends DefaultTask implements SingleFileOutput { + public static TaskProvider register(Project project, TaskProvider jar) { + var write = project.getTasks().register("writeManifest", WriteManifest.class); + write.configure(task -> { + // We don't use a map/privider here because we explicitly are reading just the config of the task. + task.getInputBytes().set(getManifestBytes(jar.get().getManifest())); + }); + return write; + } + + private static byte[] getManifestBytes(Manifest manifest) { + try (var os = new ByteArrayOutputStream()) { + // RATIONALE: ManifestInternal has not changed since Gradle 2.14 + // Due to the hacky nature of needing the proper manifest in the resources, this is the only good way of doing this + // The DefaultManifest object cannot be serialized into the Gradle cache, and the normal Manifest interface does not have this method + // This should be the only Gradle internals we need to use in all of ForgeDev, thankfully + ((ManifestInternal)manifest).writeTo(os); + return os.toByteArray(); + } catch (IOException e) { + return Util.sneak(e); + } + } + + protected abstract @Input Property getInputBytes(); + // We can't use an output file because it causes task dependency hell. + // This **should** eventually die in the launcher re-write when we stop using the manifest for runtime version info. + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public WriteManifest() { + // The output name is ALWAYS "MANIFEST.MF", and output cannot be changed + this.getOutput().value(this.getProject().getLayout().getProjectDirectory().file("src/main/resources/META-INF/MANIFEST.MF")).disallowChanges(); + } + + @TaskAction + void exec() throws IOException{ + Files.write(this.getOutput().get().getAsFile().toPath(), getInputBytes().get()); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckATs.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckATs.java index 8ac1faf..4fc3dcc 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckATs.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckATs.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.forgedev.tasks.checks; -import net.minecraftforge.forgedev.legacy.tasks.InheritanceData; +import net.minecraftforge.forgedev.tasks.installertools.InheritanceData; import net.minecraftforge.srgutils.IMappingFile; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckExecs.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckExecs.java index a9ab3a4..1620b0c 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckExecs.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckExecs.java @@ -4,7 +4,6 @@ */ package net.minecraftforge.forgedev.tasks.checks; -import net.minecraftforge.forgedev.legacy.tasks.Util; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.tasks.InputFile; @@ -12,6 +11,7 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.io.FileInputStream; @@ -102,8 +102,9 @@ private Set collectKnown() throws IOException { return ret; } + // TODO: [ForgeDev] Move away from ASM with Java 25 and classfile's private static ClassVisitor visitor(Consumer known) { - return new ClassVisitor(Util.ASM_LEVEL) { + return new ClassVisitor(Opcodes.ASM9) { private String cls; @Override diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckSAS.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckSAS.java index 40ebed5..092407e 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckSAS.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/CheckSAS.java @@ -4,8 +4,8 @@ */ package net.minecraftforge.forgedev.tasks.checks; -import net.minecraftforge.forgedev.legacy.tasks.InheritanceData; -import net.minecraftforge.forgedev.legacy.tasks.InheritanceDataAnnotatable; +import net.minecraftforge.forgedev.tasks.installertools.InheritanceData; +import net.minecraftforge.forgedev.tasks.installertools.InheritanceDataAnnotatable; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.Property; diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/Checks.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/Checks.java index 2522666..28df989 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/Checks.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/checks/Checks.java @@ -4,9 +4,8 @@ */ package net.minecraftforge.forgedev.tasks.checks; -import net.minecraftforge.forgedev.LegacyMCPExtension; -import net.minecraftforge.forgedev.LegacyPatcherExtension; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.base.MCPBase; +import net.minecraftforge.forgedev.Util; import net.minecraftforge.forgedev.tasks.installertools.ExtractInheritance; import org.codehaus.groovy.runtime.StringGroovyMethods; import org.gradle.api.Action; @@ -17,25 +16,20 @@ import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.Jar; import org.gradle.language.base.plugins.LifecycleBasePlugin; +import org.jspecify.annotations.Nullable; import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; public abstract class Checks { private final TaskContainer tasks; - private final LegacyMCPExtension mcp; - private final LegacyPatcherExtension patcher; private final TaskProvider checkAndFix; + private MCPBase mcpBase; @Inject public abstract ObjectFactory getObjects(); @Inject public Checks(Project project) { this.tasks = project.getTasks(); - this.mcp = project.getExtensions().getByType(LegacyMCPExtension.class); - this.patcher = project.getExtensions().getByType(LegacyPatcherExtension.class); this.checkAndFix = tasks.register("checkAndFix", task -> { task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); }); @@ -48,28 +42,11 @@ public void fix(Action action) { this.checkAndFix.configure(action); } - private TaskProvider extractInheritance; - private TaskProvider getExtractInheritance() { - if (this.extractInheritance == null) { - this.extractInheritance = tasks.register("extractInheritance", ExtractInheritance.class, task -> { - task.getAdditionalArgs().add("--annotations"); - - // TODO: [ForgeDev] Support mapped AT/SAS files - task.getInput().set(mcp.getFiles().getJoinedSearge()); - task.getLibraries().from(mcp.getFiles().getLibraryList().map(libraries -> { - try { - return Files.readAllLines(libraries.getAsFile().toPath()).stream() - .map(line -> line.substring(3)) // remove -e= - .map(File::new) - .toList(); - } catch (IOException e) { - throw new RuntimeException(e); - } - })); - }); - - } - return this.extractInheritance; + private TaskProvider inheritance; + private TaskProvider getInheritance() { + if (this.inheritance == null) + this.inheritance = tasks.register("extractInheritance", ExtractInheritance.class); + return this.inheritance; } public Check check(String taskName, Class clazz) { @@ -79,16 +56,17 @@ public Check check(String taskName, Class clazz, Act taskName = StringGroovyMethods.capitalize(taskName); var check = tasks.register("check" + taskName, clazz, task -> { + task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); action.execute(task); task.getFix().set(false); }); tasks.named(LifecycleBasePlugin.CHECK_TASK_NAME, task -> task.dependsOn(check)); var fix = tasks.register("checkAndFix" + taskName, clazz, task -> { + task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); action.execute(task); task.getFix().set(true); }); - this.checkAndFix.configure(task -> task.dependsOn(fix)); @SuppressWarnings("unchecked") @@ -99,11 +77,9 @@ public Check check(String taskName, Class clazz, Act private Check ats; public Check getAts() { if (this.ats == null) { - var inheritance = getExtractInheritance().flatMap(ExtractInheritance::getOutput); - this.ats = check("Ats", CheckATs.class, task -> { - task.getAts().from(this.patcher.getAccessTransformers()); - task.getInheritance().set(inheritance); - }); + var inheritance = getInheritance().flatMap(ExtractInheritance::getOutput); + this.ats = check("Ats", CheckATs.class, task -> task.getInheritance().set(inheritance)); + configureAts(this.mcpBase); } return ats; } @@ -114,14 +90,17 @@ public Check ats(Action action) { ret.fix(action); return ret; } + private void configureAts(@Nullable MCPBase base) { + if (base != null && this.ats != null) + ats(task -> task.getAts().from(base.getAccessTransformers())); + } private Check execs; public Check getExecs() { if (this.execs == null) { - this.execs = check("Execs", CheckExecs.class, task -> { - task.getExcs().from(patcher.getExcs()); - task.getBinary().set(tasks.named(Jar.TASK_NAME, Jar.class).flatMap(Jar::getArchiveFile)); - }); + this.execs = check("Execs", CheckExecs.class, task -> + task.getBinary().set(tasks.named(Jar.TASK_NAME, Jar.class).flatMap(Jar::getArchiveFile)) + ); } return execs; } @@ -136,11 +115,10 @@ public Check execs(Action action) { private Check sas; public Check getSas() { if (this.sas == null) { - var inheritance = getExtractInheritance().flatMap(ExtractInheritance::getOutput); - this.sas = check("Sas", CheckSAS.class, task -> { - task.getInheritance().set(inheritance); - task.getSass().from(patcher.getSideAnnotationStrippers()); - }); + var inheritance = getInheritance().flatMap(ExtractInheritance::getOutput); + this.sas = check("Sas", CheckSAS.class, task -> task.getInheritance().set(inheritance)); + if (this.mcpBase != null) + sas(task -> task.getSass().from(mcpBase.getSideAnnotationStrippers())); } return sas; } @@ -150,6 +128,10 @@ public Check sas(Action action) { ret.fix(action); return ret; } + private void configureSas(@Nullable MCPBase base) { + if (base != null && this.sas != null) + sas(task -> task.getSass().from(base.getSideAnnotationStrippers())); + } private Check patches; public Check getPatches() { @@ -164,4 +146,17 @@ public Check patches(Action action) { ret.fix(action); return ret; } + + public void setBase(MCPBase base) { + // TODO: [ForgeDev] Support mapped AT/SAS files + getInheritance().configure(task -> { + task.getInput().fileProvider(base.getClassesRaw()); + task.getLibraries().setFrom(base.getDependencyConfiguration()); + }); + this.configureAts(base); + this.configureSas(base); + } + public void base(MCPBase base) { + setBase(base); + } } diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/CheckJarCompatibility.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/CheckJarCompatibility.java new file mode 100644 index 0000000..89c6c00 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/CheckJarCompatibility.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.compat; + +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.tasks.ToolExec; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +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.Optional; +import org.gradle.process.ExecResult; + +import javax.inject.Inject; +import java.io.IOException; + +public abstract class CheckJarCompatibility extends ToolExec { + abstract @InputFile RegularFileProperty getBaseJar(); + abstract @InputFile RegularFileProperty getInputJar(); + + abstract @InputFiles ConfigurableFileCollection getCommonLibraries(); + abstract @InputFiles ConfigurableFileCollection getBaseLibraries(); + abstract @InputFiles ConfigurableFileCollection getConcreteLibraries(); + + abstract @Input @Optional Property getBinary(); + abstract @Input @Optional Property getAnnotationCheckMode(); + + @Inject + public CheckJarCompatibility() { + super(Tools.JARCOMPATIBILITYCHECKER); + } + + @Override + protected ExecResult exec() throws IOException { + return super.exec().rethrowFailure().assertNormalExitValue(); + } + + @Override + protected void addArguments() { + super.addArguments(); + + this.args( + "--base-jar", this.getBaseJar(), + "--input-jar", this.getInputJar() + ); + + this.args("--lib", this.getCommonLibraries()); + this.args("--base-lib", this.getBaseLibraries()); + this.args("--concrete-lib", this.getConcreteLibraries()); + + this.args(this.getBinary().getOrElse(false) ? "--binary" : "--api"); + this.args("--annotation-check-mode", this.getAnnotationCheckMode()); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/ExtractUserdevBinPatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/ExtractUserdevBinPatches.java new file mode 100644 index 0000000..aca8cd4 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/ExtractUserdevBinPatches.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.compat; + + +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.util.data.json.JsonData; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.zip.ZipFile; + +abstract class ExtractUserdevBinPatches extends DefaultTask implements SingleFileOutput { + public abstract @InputFile RegularFileProperty getInput(); + + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public ExtractUserdevBinPatches() { + } + + @TaskAction + protected void exec() { + var file = getInput().getAsFile().get(); + try (var zip = new ZipFile(file)) { + var entry = zip.getEntry("config.json"); + if (entry == null) + throw new RuntimeException("No config.json found in " + file.getAbsolutePath()); + var cfg = JsonData.patcherConfig(zip.getInputStream(entry)); + + var patches = zip.getEntry(cfg.binpatches); + if (patches == null) + throw new RuntimeException("No binpatches found in " + file.getAbsolutePath()); + + Files.copy(zip.getInputStream(patches), getOutput().getAsFile().get().toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException("Failed to extract binary patches from userdev jar: " + file.getAbsolutePath(), e); + } + } +} \ No newline at end of file diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractExistingFiles.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractExistingFiles.groovy deleted file mode 100644 index 53b40ec..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractExistingFiles.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.compat - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevTask -import org.gradle.api.DefaultTask -import org.gradle.api.file.ArchiveOperations -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.file.FileSystemOperations -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputDirectories -import org.gradle.api.tasks.SkipWhenEmpty -import org.gradle.api.tasks.TaskAction - -import javax.inject.Inject - -@CompileStatic -abstract class LegacyExtractExistingFiles extends DefaultTask implements ForgeDevTask { - abstract @InputFile @SkipWhenEmpty RegularFileProperty getArchive() - abstract @OutputDirectories ConfigurableFileCollection getTargets() - - protected abstract @Inject FileSystemOperations getFileSystemOperations() - protected abstract @Inject ArchiveOperations getArchiveOperations() - - @Inject - LegacyExtractExistingFiles() { } - - @TaskAction - protected void exec() { - final zip = this.archiveOperations.zipTree(this.archive) - - for (var directory in this.targets) { - this.fileSystemOperations.copy(copy -> copy - .from(zip) - .into(directory) - .setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) - ) - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractZip.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractZip.groovy deleted file mode 100644 index c588e9a..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyExtractZip.groovy +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.compat - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevTask -import org.gradle.api.DefaultTask -import org.gradle.api.file.ArchiveOperations -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileSystemOperations -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction - -import javax.inject.Inject - -// TODO [ForgeDev] Consider using extraction as a tool in other tasks instead of this dedicated task -@CompileStatic -abstract class LegacyExtractZip extends DefaultTask implements ForgeDevTask { - abstract @InputFile RegularFileProperty getInput() - abstract @OutputDirectory DirectoryProperty getOutput() - - @Inject - LegacyExtractZip() { } - - protected abstract @Inject FileSystemOperations getFileSystemOperations() - protected abstract @Inject ArchiveOperations getArchiveOperations() - - @TaskAction - protected void exec() { - this.fileSystemOperations.copy(copy -> copy - .from(this.archiveOperations.zipTree(this.input)) - .into(this.output) - ) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyMergeFilesTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyMergeFilesTask.groovy deleted file mode 100644 index ed41376..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/LegacyMergeFilesTask.groovy +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.compat - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevTask -import org.gradle.api.DefaultTask -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.work.DisableCachingByDefault -import org.jetbrains.annotations.ApiStatus - -import javax.inject.Inject - -@CompileStatic -@Deprecated -@ApiStatus.ScheduledForRemoval -@DisableCachingByDefault( - because = 'Files that this task depends on cannot be cached' -) -abstract class LegacyMergeFilesTask extends DefaultTask implements ForgeDevTask { - abstract @InputFiles ConfigurableFileCollection getFilesToMerge() - protected abstract @Input @Optional Property getAdditionalData() - - abstract @Input Property getSeparationComment() - - void addAdditionalData(String entry) { - var existing = this.additionalData.present ? this.additionalData.get() + this.separationComment.get() : '' - this.additionalData.set(existing + entry) - } - - abstract @OutputFile RegularFileProperty getOutput() - - @Inject - LegacyMergeFilesTask() { - this.separationComment.convention('\n#============================================================\n') - - // TODO [ForgeDev] caching of this task (or delete it) - this.outputs.upToDateWhen { false } - } - - @TaskAction - protected void exec() { - var buffer = new StringBuilder() - for (var config in this.filesToMerge.files) { - var entries = config.text - - if (buffer.length() !== 0) - buffer.append('\n#============================================================\n') - buffer.append(entries) - } - - if (this.additionalData.present) - buffer.append(this.additionalData.get()) - - this.output.get().asFile.bytes = buffer.toString().bytes - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibility.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibility.java new file mode 100644 index 0000000..47594aa --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibility.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.compat; + +import org.gradle.api.Action; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; + +import java.io.File; + +public interface UserdevCompatibility { + Provider getBaseVersion(); + + TaskProvider getCheck(); + TaskProvider check(Action action); + + void setClean(RegularFileProperty clean); + void setClean(Provider clean); + + void setDirty(TaskProvider jarTask); +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibilityImpl.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibilityImpl.java new file mode 100644 index 0000000..dd96246 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/compat/UserdevCompatibilityImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.compat; + +import net.minecraftforge.forgedev.Constants; +import net.minecraftforge.forgedev.tasks.MergeJars; +import net.minecraftforge.forgedev.tasks.DownloadHashedFile; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.patching.binary.ApplyBinPatches; +import net.minecraftforge.forgedev.values.LatestForgeVersion; +import net.minecraftforge.forgedev.values.MavenArtifact; +import net.minecraftforge.util.data.json.JsonData; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.file.RegularFile; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.jvm.tasks.Jar; +import org.gradle.language.base.plugins.LifecycleBasePlugin; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.util.zip.ZipFile; + +public abstract class UserdevCompatibilityImpl implements UserdevCompatibility { + private final Provider baseForgeVersion; + private final TaskProvider apply; + private final TaskProvider check; + + protected abstract @Inject ProviderFactory getProviders(); + protected abstract @Inject ObjectFactory getObjects(); + + @Inject + public UserdevCompatibilityImpl(Project project, String minecraftVersion, boolean first) { + var group = "Compatibility"; + var TASK_NAME = "checkJarCompatibility" + (first ? "" : minecraftVersion.replace(".", "")); + var buildDir = project.getLayout().getBuildDirectory(); + + this.baseForgeVersion = project.getProviders().of(LatestForgeVersion.class, cfg -> { + cfg.parameters(p -> { + p.getOffline().set(project.getGradle().getStartParameter().isOffline()); + p.getCacheFile().set(project.getLayout().getBuildDirectory().file("promotions_slim.json")); + p.getMinecraftVersion().set(minecraftVersion); + }); + }); + + var downloadUserdev = project.getTasks().register(TASK_NAME + "DownloadUserdev", DownloadHashedFile.class, task -> { + task.setDescription("Sets up JAR compatibility checking by downloading the latest available UserDev."); + task.setGroup(group); + + task.getSrc().set(baseForgeVersion.map(ver -> "https://maven.minecraftforge.net/net/minecraftforge/forge/" + ver + "/forge-" + ver + "-userdev.jar")); + task.getOutput().set(baseForgeVersion.flatMap(ver -> buildDir.file(TASK_NAME + "/forge-" + ver + "-userdev.jar"))); + }); + + var extractBinPatches = project.getTasks().register(TASK_NAME + "ExtractBinPatches", ExtractUserdevBinPatches.class, task -> { + task.setDescription("Sets up JAR compatibility checking by extracting the binary patches from the latest available UserDev."); + task.setGroup(group); + + task.getInput().set(downloadUserdev.flatMap(DownloadHashedFile::getOutput)); + task.getOutput().set(baseForgeVersion.flatMap(ver -> buildDir.file(TASK_NAME + "/forge-" + ver + " -binpatches.lzma"))); + }); + + this.apply = project.getTasks().register(TASK_NAME + "ApplyBinPatches", ApplyBinPatches.class, task -> { + task.setDescription("Sets up JAR compatibility checking by applying the base jar's binary patches from the latest available UserDev."); + task.setGroup(group); + + //task.getClean().setFrom(task.getProject().getTasks().named("rawJoinedJarSrg", MavenizerRawArtifact.class).flatMap(MavenizerRawArtifact::getOutput)); + task.getApply().setFrom(extractBinPatches.flatMap(SingleFileOutput::getOutput)); + task.getOutput().set(baseForgeVersion.flatMap(ver -> buildDir.file(TASK_NAME + "/forge-" + ver + " -binpatched.jar"))); + }); + + var downloadBaseForgeUniversal = project.getTasks().register(TASK_NAME + "DownloadUniversal", DownloadHashedFile.class, task -> { + task.setDescription("Sets up JAR compatibility checking by downloading the latest available universal JAR."); + task.getSrc().set(downloadUserdev.flatMap(SingleFileOutput::getOutput).map(this::findUniversalUrl)); + task.getOutput().set(baseForgeVersion.flatMap(ver -> buildDir.file(TASK_NAME + "/forge-" + ver + "-universal.jar"))); + }); + + // Pulled out so the lambda doesn't capture 'this' + var applyOutput = apply.flatMap(ApplyBinPatches::getOutput); + var universal = downloadBaseForgeUniversal.flatMap(DownloadHashedFile::getOutput); + var mergeBaseForgeJar = project.getTasks().register(TASK_NAME + "Merge", MergeJars.class, task -> { + task.setDescription("Sets up JAR compatibility checking by merging the universal JAR with the binary patched JAR."); + task.setGroup(group); + + task.getInputJars().from(applyOutput, universal); + }); + + this.check = project.getTasks().register(TASK_NAME, CheckJarCompatibility.class, task -> { + var main = task.getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME); + var jar = task.getProject().getTasks().named("jar", Jar.class); + task.setGroup(LifecycleBasePlugin.VERIFICATION_GROUP); + task.setDescription("Checks the JAR compatibility between the built JAR and the latest available JAR."); + task.onlyIf(t -> baseForgeVersion.isPresent()); + + task.getBaseJar().set(mergeBaseForgeJar.flatMap(MergeJars::getOutput)); + task.getCommonLibraries().setFrom(main.get().getCompileClasspath()); + task.getInputJar().set(jar.flatMap(Jar::getArchiveFile)); + }); + + var providers = project.getProviders(); + var hasMaven = providers.environmentVariable("MAVEN_USER").isPresent() && providers.environmentVariable("MAVEN_PASSWORD").isPresent(); + var checkCompatibility = providers.gradleProperty("net.minecraftforge.forge.build.check.compatibility").map(Boolean::parseBoolean).getOrElse(false); + + if (!hasMaven && checkCompatibility) + project.getTasks().named("check", task -> task.dependsOn(check)); + } + + private String findUniversalUrl(RegularFile userdev) { + var file = userdev.getAsFile(); + try (var zip = new ZipFile(file)) { + var entry = zip.getEntry("config.json"); + if (entry == null) + throw new RuntimeException("No config.json found in " + file.getAbsolutePath()); + var cfg = JsonData.patcherConfig(zip.getInputStream(entry)); + + // Technically universal can be null, but that shouldn't ever happen + var universal = MavenArtifact.from(getObjects(), cfg.universal); + return Constants.FORGE_MAVEN + universal.getPath(); + } catch (IOException e) { + throw new RuntimeException("Failed to find universal url from userdev jar: " + file.getAbsolutePath(), e); + } + } + + @Override + public Provider getBaseVersion() { + return baseForgeVersion; + } + + @Override + public TaskProvider getCheck() { + return this.check; + } + @Override + public TaskProvider check(Action action) { + getCheck().configure(action); + return getCheck(); + } + + + @Override + public void setClean(RegularFileProperty clean) { + this.apply.configure(task -> task.getClean().setFrom(clean)); + } + @Override + public void setClean(Provider clean) { + this.apply.configure(task -> task.getClean().setFrom(clean)); + } + + @Override + public void setDirty(TaskProvider jarTask) { + this.check.configure(task -> task.getInputJar().set(jarTask.flatMap(AbstractArchiveTask::getArchiveFile))); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/filtering/LegacyFilterNewJar.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/filtering/LegacyFilterNewJar.groovy deleted file mode 100644 index bdad5ae..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/filtering/LegacyFilterNewJar.groovy +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.filtering - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevTask -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.srgutils.IMappingFile -import net.minecraftforge.util.file.FileUtils -import org.gradle.api.DefaultTask -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.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -import javax.inject.Inject -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream - -@CompileStatic -abstract class LegacyFilterNewJar extends DefaultTask implements ForgeDevTask { - abstract @InputFile RegularFileProperty getInput() - abstract @InputFile RegularFileProperty getSrg() - abstract @InputFiles ConfigurableFileCollection getBlacklist() - abstract @OutputFile RegularFileProperty getOutput() - - protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerClasspath() - protected abstract @Inject WorkerExecutor getWorkerExecutor() - - @Inject - LegacyFilterNewJar() { - this.output.convention(this.defaultOutputFile) - - this.workerClasspath.from(this.getTool(Tools.SRGUTILS)) - } - - @TaskAction - protected void exec() { - final work = this.workerExecutor.classLoaderIsolation { - it.classpath.from(this.workerClasspath) - } - - work.submit(Action) { - it.input.set(this.input) - it.srg.set(this.srg) - it.blacklist.setFrom(this.blacklist) - it.output.set(this.output) - } - - work.await() - } - - //We pack all inner classes in binpatches. So strip anything thats a vanilla class or inner class of one. - private static boolean isVanilla(Set classes, String cls) { - int idx = cls.indexOf('$') - if (idx != -1) { - return isVanilla(classes, cls.substring(0, idx)) - } - return classes.contains(cls) - } - - @CompileStatic - protected static abstract class Action implements WorkAction { - @CompileStatic - static interface Parameters extends WorkParameters { - RegularFileProperty getInput() - - RegularFileProperty getSrg() - - ConfigurableFileCollection getBlacklist() - - RegularFileProperty getOutput() - } - - @Override - void execute() { - var filter = new HashSet() - for (var file : this.parameters.blacklist.files) { - try (var zip = new ZipFile(file)) { - Iterable entries = zip.entries().&asIterator - for (var entry in entries) { - filter.add(entry.getName()) - } - } - } - - var classes = IMappingFile.load(this.parameters.srg.asFile.get()).classes.collect(new HashSet(), IMappingFile.IClass.&getMapped) - - try (var zin = new ZipFile(this.parameters.input.asFile.get()) - var out = new ZipOutputStream(new FileOutputStream(this.parameters.output.asFile.get()))) { - - Iterable entries = zin.entries().&asIterator - for (var entry in entries) { - if (entry.directory || filter.contains(entry.name) || - (entry.name.endsWith(".class") && isVanilla(classes, entry.name.substring(0, entry.name.length() - 6)))) { - continue - } - out.putNextEntry(FileUtils.getStableEntry(entry.name)) - zin.getInputStream(entry).transferTo(out) - out.closeEntry() - } - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/Installer.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/Installer.java index dfc8b21..4e21ba5 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/Installer.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/Installer.java @@ -7,15 +7,18 @@ import net.minecraftforge.forgedev.ForgeDevExtension; import net.minecraftforge.forgedev.Tools; import net.minecraftforge.forgedev.legacy.tasks.DownloadDependency; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.Util; import net.minecraftforge.forgedev.legacy.values.LibraryInfo; import net.minecraftforge.forgedev.legacy.values.MavenInfo; import net.minecraftforge.forgedev.legacy.values.MinimalResolvedArtifact; import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.values.MavenArtifact; import net.minecraftforge.gradleutils.shared.SharedUtil; import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; @@ -51,6 +54,8 @@ public abstract class Installer { @Input public abstract Property getOffline(); + protected abstract @Inject ObjectFactory getObjects(); + @Inject public Installer(Project project, String name, TaskProvider jar, TaskProvider jarConfig, TaskProvider json, TaskProvider launcherJson, TaskProvider base) { this.project = project; @@ -86,11 +91,19 @@ public void launcherJson(Action action) { getLauncherJson().configure(action); } - public Tool tool(Object dependency) { + public Tool tool(Provider dependency) { + return tool(dependency, true); + } + public Tool tool(Provider dependency, boolean transitive) { + return tool(MavenArtifact.from(getObjects(), dependency).get().getDescriptor(), transitive); + } + public Tool tool(Dependency dependency) { return tool(dependency, true); } - public Tool tool(Object dependency, boolean transitive) { - var gav = Util.asArtifactString(dependency); + public Tool tool(Dependency dependency, boolean transitive) { + return tool(MavenArtifact.from(getObjects(), dependency).getDescriptor(), transitive); + } + private Tool tool(String gav, boolean transitive) { var tree = MinimalResolvedArtifact.from(project, gav, transitive).get(); this.jarConfig.configure(task -> { for (var artifact : tree) @@ -163,7 +176,6 @@ public void launcherLibrary(Provider info, Action task.library(info, action)); } - /// Helper to set the base installer to Forge's this is meant to make it easy to do something like: /// installer { /// baseVersion = "1.0" diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/InstallerJson.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/InstallerJson.java index 7d69c04..e00c7b3 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/InstallerJson.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/InstallerJson.java @@ -6,15 +6,18 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.Util; import net.minecraftforge.forgedev.legacy.values.LibraryInfo; import net.minecraftforge.forgedev.legacy.values.MinimalResolvedArtifact; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; import net.minecraftforge.forgedev.tasks.installer.steps.Extract; import net.minecraftforge.forgedev.tasks.installer.steps.ExtractBundle; import net.minecraftforge.forgedev.tasks.installer.steps.Step; +import net.minecraftforge.util.hash.HashFunction; import org.gradle.api.Action; import org.gradle.api.DefaultTask; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFile; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; @@ -26,6 +29,7 @@ import org.gradle.api.tasks.bundling.AbstractArchiveTask; import javax.inject.Inject; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; @@ -119,11 +123,20 @@ public void data(String key, String client, String server) { this.getData().put(key, new Data(client, server)); } - public void data(String key, RegularFileProperty client, RegularFileProperty server) { - this.getData().put(key, client.zip(server, (l, r) -> new Data( - "'" + Util.sha1(client.get().getAsFile()) + "'", - "'" + Util.sha1(server.get().getAsFile()) + "'" - ))); + public void data(String key, Provider client, Provider server) { + this.getData().put(key, client.zip(server, (l, r) -> new Data(convert(l), convert(r)))); + } + + private static String convert(Object obj) { + if (obj instanceof String value) return value; + if (obj instanceof RegularFile value) return hash(value.getAsFile()); + if (obj instanceof File value) return hash(value); + if (obj instanceof SingleFileOutput value) return hash(value.getOutput().getAsFile().get()); + throw new IllegalArgumentException("Cannot convert object of type " + obj.getClass() + " to data entry: " + obj); + } + + private static String hash(File file) { + return '\'' + HashFunction.SHA1.sneakyHash(file) + '\''; } @TaskAction diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/LauncherJson.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/LauncherJson.java index 869d573..b3bb8c2 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/LauncherJson.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installer/LauncherJson.java @@ -6,7 +6,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import net.minecraftforge.forgedev.legacy.tasks.Util; +import net.minecraftforge.forgedev.Util; import net.minecraftforge.forgedev.legacy.values.LibraryInfo; import net.minecraftforge.forgedev.legacy.values.MavenInfo; import net.minecraftforge.forgedev.legacy.values.MinimalResolvedArtifact; diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/DownloadMappings.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/DownloadMappings.groovy deleted file mode 100644 index f16a160..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/DownloadMappings.groovy +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.installertools - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -abstract class DownloadMappings extends ToolExec { - abstract @Input Property getSide() - abstract @Input Property getVersion() - - abstract @OutputFile RegularFileProperty getOutput() - - @Inject - DownloadMappings() { - super(Tools.INSTALLERTOOLS) - - this.output.convention(this.getDefaultOutputFile('tsrg')) - this.standardOutputLogLevel.set(LogLevel.INFO) - } - - @Override - protected ExecResult exec() { - return super.exec().rethrowFailure().assertNormalExitValue() - } - - @Override - protected void addArguments() { - this.args( - '--task', 'DOWNLOAD_MOJMAPS', - '--sanitize', - '--version', this.version.get(), - '--side', this.side.get(), - '--output', this.output.asFile.get().absolutePath - ) - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.groovy deleted file mode 100644 index 3a8ff4c..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.groovy +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.installertools - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.tasks.SingleFileOutput -import net.minecraftforge.forgedev.tasks.ToolExec -import net.minecraftforge.gradleutils.shared.Tool -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.provider.ListProperty -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -abstract class ExtractInheritance extends ToolExec implements SingleFileOutput { - abstract @InputFile RegularFileProperty getInput(); - - abstract @InputFiles @Classpath ConfigurableFileCollection getLibraries(); - - abstract @OutputFile RegularFileProperty getOutput(); - - @Inject - ExtractInheritance() { - super(Tools.INSTALLERTOOLS) - this.output.convention(this.getDefaultOutputFile('json')) - this.preferToolchainJvm.convention(true) - this.standardOutputLogLevel.convention(LogLevel.INFO); - } - - @Override - protected ExecResult exec() throws IOException { - super.exec().assertNormalExitValue().rethrowFailure() - } - - @Override - protected void addArguments() { - this.args( - '--task', 'extract_inheritance', - '--input', this.input.asFile.get().absolutePath, - '--output', this.output.asFile.get().absolutePath - ) - - this.args('--lib', this.libraries) - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.java new file mode 100644 index 0000000..c52465e --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/ExtractInheritance.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.installertools; + +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.ToolExec; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.process.ExecResult; + +import javax.inject.Inject; +import java.io.IOException; + +public abstract class ExtractInheritance extends ToolExec implements SingleFileOutput { + public abstract @InputFile RegularFileProperty getInput(); + + public abstract @InputFiles @Classpath ConfigurableFileCollection getLibraries(); + + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public ExtractInheritance() { + super(Tools.INSTALLERTOOLS); + this.getOutput().convention(this.getDefaultOutputFile("json")); + this.getPreferToolchainJvm().convention(true); + this.getStandardOutputLogLevel().convention(LogLevel.INFO); + } + + @Override + protected ExecResult exec() throws IOException { + return super.exec().assertNormalExitValue().rethrowFailure(); + } + + @Override + protected void addArguments() { + this.args( + "--task", "extract_inheritance", + "--input", this.getInput().getAsFile().get().getAbsolutePath(), + "--output", this.getOutput().getAsFile().get().getAbsolutePath(), + "--annotations" + ); + + this.args("--lib", this.getLibraries()); + + super.addArguments(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceData.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceData.java similarity index 95% rename from src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceData.java rename to src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceData.java index 8fc3759..a6242ff 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceData.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceData.java @@ -2,7 +2,7 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev.legacy.tasks; +package net.minecraftforge.forgedev.tasks.installertools; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceDataAnnotatable.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceDataAnnotatable.java similarity index 79% rename from src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceDataAnnotatable.java rename to src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceDataAnnotatable.java index 604fd3b..a6c5339 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/tasks/InheritanceDataAnnotatable.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/installertools/InheritanceDataAnnotatable.java @@ -2,7 +2,7 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev.legacy.tasks; +package net.minecraftforge.forgedev.tasks.installertools; import java.util.List; diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/jarcompat/CheckJarCompatibility.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/jarcompat/CheckJarCompatibility.groovy deleted file mode 100644 index dfb31e2..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/jarcompat/CheckJarCompatibility.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.jarcompat - -import groovy.transform.CompileStatic; -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -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.Optional -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -abstract class CheckJarCompatibility extends ToolExec { - abstract @InputFile RegularFileProperty getBaseJar() - abstract @InputFile RegularFileProperty getInputJar() - - abstract @InputFiles ConfigurableFileCollection getCommonLibraries() - abstract @InputFiles ConfigurableFileCollection getBaseLibraries() - abstract @InputFiles ConfigurableFileCollection getConcreteLibraries() - - abstract @Input @Optional Property getBinary() - abstract @Input @Optional Property getAnnotationCheckMode() - - @Inject - CheckJarCompatibility() { - super(Tools.JARCOMPATIBILITYCHECKER) - } - - @Override - protected ExecResult exec() { - return super.exec().rethrowFailure().assertNormalExitValue() - } - - @Override - protected void addArguments() { - super.addArguments() - - this.args( - '--base-jar', baseJar, - '--input-jar', inputJar - ) - - this.args('--lib', commonLibraries) - this.args('--base-lib', baseLibraries) - this.args('--concrete-lib', concreteLibraries) - - this.args(binary.getOrElse(false) ? '--binary' : '--api') - this.args('--annotation-check-mode', annotationCheckMode) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherEclipseConfiguration.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherEclipseConfiguration.java index 2e559af..1ff4378 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherEclipseConfiguration.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherEclipseConfiguration.java @@ -28,7 +28,6 @@ import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; import org.gradle.jvm.toolchain.JavaLauncher; -import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.gradle.workers.WorkAction; import org.gradle.workers.WorkParameters; import org.gradle.workers.WorkerExecutor; @@ -53,7 +52,7 @@ import java.util.Map; // This is mostly taken from ForgeGradle 6 but slimmed down to what we need -abstract class SlimeLauncherEclipseConfiguration extends DefaultTask implements ForgeDevTask { +public abstract class SlimeLauncherEclipseConfiguration extends DefaultTask implements ForgeDevTask { protected abstract @OutputFile RegularFileProperty getOutputFile(); protected abstract @Input Property getProjectName(); @@ -76,7 +75,7 @@ abstract class SlimeLauncherEclipseConfiguration extends DefaultTask implements protected abstract @Internal DirectoryProperty getCacheDir(); - protected abstract @InputFiles ConfigurableFileCollection getMetadata(); + public abstract @InputFiles ConfigurableFileCollection getMetadata(); protected abstract @Inject ObjectFactory getObjects(); diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherExec.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherExec.java index d0fe640..5822569 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherExec.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherExec.java @@ -9,12 +9,10 @@ import net.minecraftforge.forgedev.Tools; import net.minecraftforge.forgedev.Util; import org.gradle.api.Project; -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.api.artifacts.ProjectDependency; import org.gradle.api.artifacts.component.ProjectComponentIdentifier; -import org.gradle.api.attributes.Usage; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.FileCollection; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.reflect.HasPublicType; @@ -27,10 +25,8 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; -import org.gradle.plugins.ide.eclipse.model.EclipseModel; import javax.inject.Inject; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; @@ -38,17 +34,9 @@ import java.util.Map; public abstract class SlimeLauncherExec extends JavaExec implements ForgeDevTask, HasPublicType { - public static TaskProvider register(Project project, SourceSet sourceSet, SlimeLauncherOptionsImpl options, ModuleIdentifier module, String version, File eclipseOutputDir) { - var metadataDep = project.getDependencyFactory().create(module.getGroup(), module.getName(), version, "metadata", "zip"); - var metadataAttr = project.getObjects().named(Usage.class, "metadata"); - var metadataConfiguration = project.getConfigurations().detachedConfiguration( - metadataDep - ); - metadataConfiguration.setTransitive(false); - metadataConfiguration.attributes(a -> a.attribute(Usage.USAGE_ATTRIBUTE, metadataAttr)); - - var runTaskName = sourceSet.getTaskName("run", options.getName()); + public static TaskProvider registerEclipse(Project project, SourceSet sourceSet, SlimeLauncherOptionsImpl options, String version, FileCollection metadata, TaskProvider genEclipseRuns) { var generateEclipseRunTaskName = sourceSet.getTaskName("genEclipseRun", options.getName()); + var runTaskName = sourceSet.getTaskName("run", options.getName()); var genEclipseRun = project.getTasks().register(generateEclipseRunTaskName, SlimeLauncherEclipseConfiguration.class, task -> { task.getRunName().set(options.getName()); @@ -79,13 +67,16 @@ public static TaskProvider register(Project project, SourceSe var caches = task.getObjects().directoryProperty().value(task.globalCaches().dir("slime-launcher/cache/%s".formatted(version))); task.getCacheDir().set(caches.map(task.problems.ensureFileLocation())); - task.getMetadata().setFrom(metadataConfiguration); + task.getMetadata().setFrom(metadata); task.getOptions().set(options); }); + genEclipseRuns.configure(task -> task.dependsOn(genEclipseRun)); + return genEclipseRun; + } - project.getTasks().named("genEclipseRuns", task -> task.dependsOn(genEclipseRun)); - + public static TaskProvider register(Project project, SourceSet sourceSet, SlimeLauncherOptionsImpl options, String version, FileCollection metadata) { + var runTaskName = sourceSet.getTaskName("run", options.getName()); return project.getTasks().register(runTaskName, SlimeLauncherExec.class, task -> { task.getRunName().set(options.getName()); task.getSourceSetName().set(sourceSet.getName()); @@ -95,7 +86,7 @@ public static TaskProvider register(Project project, SourceSe var caches = task.getObjectFactory().directoryProperty().value(task.globalCaches().dir("slime-launcher/cache/%s".formatted(version))); task.getCacheDir().set(caches.map(task.problems.ensureFileLocation())); - task.getMetadata().setFrom(metadataConfiguration); + task.getMetadata().setFrom(metadata); task.getOptions().set(options); }); @@ -109,7 +100,7 @@ public static TaskProvider register(Project project, SourceSe protected abstract @Internal DirectoryProperty getCacheDir(); - protected abstract @InputFiles ConfigurableFileCollection getMetadata(); + public abstract @InputFiles ConfigurableFileCollection getMetadata(); protected abstract @Input @Optional Property getClient(); diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherOptionsImpl.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherOptionsImpl.java index 6f4f5a7..1df386a 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherOptionsImpl.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/launcher/SlimeLauncherOptionsImpl.java @@ -235,7 +235,7 @@ public void systemProperties(Provider> properties) { @Override public void environment(String name, Object value) { - this.getSystemProperties().put(name, this.getProviders().provider(() -> Util.unpack(value).toString())); + this.getEnvironment().put(name, this.getProviders().provider(() -> Util.unpack(value).toString())); } @Override diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.groovy deleted file mode 100644 index 76b66ad..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.groovy +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mappings - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.ForgeDevTask -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.util.file.FileUtils -import org.gradle.api.DefaultTask -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.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -import javax.inject.Inject -import java.nio.charset.StandardCharsets -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream - -@CompileStatic -abstract class LegacyApplyMappings extends DefaultTask implements ForgeDevTask { - abstract @Input Property getJavadocs() - abstract @Input Property getLambdas() - - abstract @InputFile RegularFileProperty getInput() - abstract @InputFiles ConfigurableFileCollection getMappings() - abstract @OutputFile RegularFileProperty getOutput() - - @Inject - LegacyApplyMappings() { - this.workerClasspath.from(this.getTool(Tools.FASTCSV)) - - this.output.convention(this.defaultOutputFile) - - this.javadocs.convention(false) - this.lambdas.convention(false) - } - - protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerClasspath() - protected abstract @Inject WorkerExecutor getWorkerExecutor() - - @TaskAction - protected void exec() { - final work = this.workerExecutor.classLoaderIsolation { - it.classpath.from(this.workerClasspath) - } - - work.submit(Action) { - it.javadocs.set this.javadocs - it.lambdas.set this.lambdas - - it.input.set this.input - it.mappingsZip.set this.mappings.singleFile - it.output.set this.output - } - - work.await() - } - - @CompileStatic - @PackageScope static abstract class Action implements WorkAction { - @CompileStatic - static interface Parameters extends WorkParameters { - Property getJavadocs() - Property getLambdas() - - RegularFileProperty getInput() - RegularFileProperty getMappingsZip() - RegularFileProperty getOutput() - } - - @Inject - Action() {} - - @Override - void execute() { - final javadocs = this.parameters.javadocs.getOrElse(false) - final lambdas = this.parameters.lambdas.getOrElse(false) - - final input = this.parameters.input.get().asFile - final mappingsZip = this.parameters.mappingsZip.get().asFile - final output = this.parameters.output.get().asFile - - var names = MCPNames.load(mappingsZip) - try (var zin = new ZipFile(input)) { - try (var fos = new FileOutputStream(output) - var out = new ZipOutputStream(fos)) { - for (var entry : { zin.entries().asIterator() } as Iterable) { - out.putNextEntry(FileUtils.getStableEntry(entry.name)) - if (!entry.name.endsWith('.java')) { - zin.getInputStream(entry).transferTo(out) - } else { - out.write(names.rename(zin.getInputStream(entry), javadocs, lambdas).getBytes(StandardCharsets.UTF_8)) - } - out.closeEntry() - } - } - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.java new file mode 100644 index 0000000..7253c4a --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyApplyMappings.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.mappings; + +import net.minecraftforge.forgedev.ForgeDevTask; +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.util.file.FileUtils; +import org.gradle.api.DefaultTask; +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.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; +import org.gradle.workers.WorkAction; +import org.gradle.workers.WorkParameters; +import org.gradle.workers.WorkerExecutor; + +import javax.inject.Inject; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +// I have not exposed this in any way, this is basically just going to be used while upgrading old branches to +// the new toolchain so run only once. +public abstract class LegacyApplyMappings extends DefaultTask implements ForgeDevTask { + public abstract @Input Property getJavadocs(); + public abstract @Input Property getLambdas(); + + public abstract @InputFile RegularFileProperty getInput(); + public abstract @InputFiles ConfigurableFileCollection getMappings(); + public abstract @OutputFile RegularFileProperty getOutput(); + + @Inject + public LegacyApplyMappings() { + this.getWorkerClasspath().from(this.getTool(Tools.FASTCSV)); + this.getOutput().convention(this.getDefaultOutputFile()); + this.getJavadocs().convention(false); + this.getLambdas().convention(false); + } + + protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerClasspath(); + protected abstract @Inject WorkerExecutor getWorkerExecutor(); + + @TaskAction + protected void exec() { + final var work = this.getWorkerExecutor().classLoaderIsolation(cfg -> { + cfg.getClasspath().from(this.getWorkerClasspath()); + }); + + work.submit(Action.class, cfg -> { + cfg.getJavadocs().set(getJavadocs().get()); + cfg.getLambdas().set(getLambdas().get()); + cfg.getInput().set(getInput().get()); + cfg.getMappingsZip().set(getMappings().getSingleFile()); + cfg.getOutput().set(getOutput().get()); + }); + + work.await(); + } + + public static abstract class Action implements WorkAction { + interface Parameters extends WorkParameters { + Property getJavadocs(); + Property getLambdas(); + + RegularFileProperty getInput(); + RegularFileProperty getMappingsZip(); + RegularFileProperty getOutput(); + } + + @Inject + Action() {} + + @Override + public void execute() { + boolean javadocs = this.getParameters().getJavadocs().getOrElse(false); + boolean lambdas = this.getParameters().getLambdas().getOrElse(false); + + var input = this.getParameters().getInput().get().getAsFile(); + var mappingsZip = this.getParameters().getMappingsZip().get().getAsFile(); + var output = this.getParameters().getOutput().get().getAsFile(); + + try (var zin = new ZipFile(input)) { + var names = MCPNames.load(mappingsZip); + try (var fos = new FileOutputStream(output); + var out = new ZipOutputStream(fos)) { + for (var itr = zin.entries().asIterator(); itr.hasNext(); ) { + var entry = itr.next(); + out.putNextEntry(FileUtils.getStableEntry(entry.getName())); + if (!entry.getName().endsWith(".java")) { + zin.getInputStream(entry).transferTo(out); + } else { + out.write(names.rename(zin.getInputStream(entry), javadocs, lambdas).getBytes(StandardCharsets.UTF_8)); + } + out.closeEntry(); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyGenerateSRG.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyGenerateSRG.groovy deleted file mode 100644 index 36a3db8..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mappings/LegacyGenerateSRG.groovy +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mappings - - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.ForgeDevTask -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.srgutils.IMappingFile -import net.minecraftforge.srgutils.IMappingFile.INode -import net.minecraftforge.srgutils.IRenamer -import org.gradle.api.DefaultTask -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.InputFiles -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -import javax.inject.Inject - -@CompileStatic -abstract class LegacyGenerateSRG extends DefaultTask implements ForgeDevTask { - abstract @Input Property getFormat() - abstract @Input Property getNotch() - abstract @Input Property getReverse() - - abstract @InputFile RegularFileProperty getMcpSrgData() - abstract @InputFiles ConfigurableFileCollection getMappings() - abstract @OutputFile RegularFileProperty getOutput() - - protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerClasspath() - protected abstract @Inject WorkerExecutor getWorkerExecutor() - - LegacyGenerateSRG() { - this.workerClasspath.from( - this.getTool(Tools.SRGUTILS), - this.getTool(Tools.FASTCSV) - ) - - this.format.convention(IMappingFile.Format.TSRG2) - this.notch.convention(false) - this.reverse.convention(false) - - this.output.convention(this.getDefaultOutputFile('tsrg')) - } - - @TaskAction - protected void exec() { - final work = this.workerExecutor.classLoaderIsolation { - it.classpath.from(this.workerClasspath) - } - - work.submit(Action) { - it.format.set this.format - it.notch.set this.notch - it.reverse.set this.reverse - - it.mcpSrgData.set this.mcpSrgData - it.mappingsZip.set this.mappings.singleFile - it.output.set this.output - } - - work.await() - } - - @CompileStatic - @PackageScope static abstract class Action implements WorkAction { - @CompileStatic - static interface Parameters extends WorkParameters { - Property getFormat() - Property getNotch() - Property getReverse() - - RegularFileProperty getMcpSrgData() - RegularFileProperty getMappingsZip() - RegularFileProperty getOutput() - } - - @Inject - Action() {} - - @Override - void execute() { - // Obf -> Srg - var input = IMappingFile.load(this.parameters.mcpSrgData.get().asFile) - - // Srg->Obf->Srg = Srg->Srg - boolean notch = this.parameters.notch.getOrElse(false) - if (!notch) - input = input.reverse().chain(input) - - var map = MCPNames.load(this.parameters.mappingsZip.get().asFile) - // Srg->Mapped - var ret = input.rename(renamer(map)) - - ret.write( - this.parameters.output.get().asFile.toPath(), - this.parameters.format.get(), - this.parameters.reverse.getOrElse(false) - ) - } - - private static IRenamer renamer(MCPNames map) throws IOException { - new IRenamer() { - String rename(IMappingFile.INode value) { - final mapped = value.mapped - map.names().getOrDefault(mapped, mapped) - } - - @Override - String rename(IMappingFile.IPackage value) { - this.rename(value as INode) - } - - @Override - String rename(IMappingFile.IClass value) { - this.rename(value as INode) - } - - @Override - String rename(IMappingFile.IField value) { - this.rename(value as INode) - } - - @Override - String rename(IMappingFile.IMethod value) { - this.rename(value as INode) - } - - @Override - String rename(IMappingFile.IParameter value) { - this.rename(value as INode) - } - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MCPSetupFiles.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MCPSetupFiles.java deleted file mode 100644 index 3335e7f..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MCPSetupFiles.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp; - -public class MCPSetupFiles { - public String versionManifest; - public String versionJson; - public String clientRaw; - public String clientMappings; - public String serverRaw; - public String serverExtracted; - public String serverMappings; - public String librariesList; - public String joinedSrgMappings; -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerExec.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerExec.groovy deleted file mode 100644 index 3359c13..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerExec.groovy +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.ForgeDevPlugin -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.problems.Problems -import org.gradle.api.tasks.InputDirectory -import org.jetbrains.annotations.MustBeInvokedByOverriders - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class MavenizerExec extends ToolExec { - // Mavenizer - protected abstract @InputDirectory DirectoryProperty getCaches() - - @Inject - MavenizerExec() { - super(Tools.MAVENIZER) - - this.caches.convention(this.defaultToolDir.dir('cache').map(this.problems.ensureFileLocation())) - } - - protected void addArguments() { - //region Mavenizer - this.args( - '--cache', this.caches.get().asFile.absolutePath, - '--jdk-cache', this.caches.dir('jdks').get().asFile.absolutePath - ) - //endregion - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPDataTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPDataTask.groovy deleted file mode 100644 index fe60eae..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPDataTask.groovy +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult - -import javax.inject.Inject -import java.nio.file.Files - -@CompileStatic -abstract class MavenizerMCPDataTask extends MavenizerExec { - // MCP Task - abstract @Input Property getArtifact() // can also be just the version - abstract @OutputFile RegularFileProperty getOutput() - abstract @Input Property getOfficial() - abstract @Input @Optional Property getParchment() - abstract @Input Property getKey() - - // Legacy - abstract @Input Property getIsAllowEmpty() - - @Inject - MavenizerMCPDataTask() { - this.output.convention(this.defaultOutputFile) - this.official.convention(false) - this.key.convention('mappings') - - this.isAllowEmpty.convention(false) - } - - @Override - protected void addArguments() { - final official = this.official.getOrElse(false) - if (official && this.parchment.present) - throw new IllegalArgumentException('MavenizerMCPDataTask cannot use both official and parchment mappings') - - super.addArguments() - - //region MCP Data Task - var artifact = this.artifact.get() - this.args( - '--mcp-data', - artifact.contains(':') ? '--artifact' : '--version', artifact, - '--output', this.output.get().asFile.absolutePath, - ) - - if (official) - this.args('--mappings') - else if (this.parchment.present) - this.args('--parchment', this.parchment.get()) - - this.args('--key', this.key.get()) - //endregion - } - - @Override - protected ExecResult exec() { - var result = super.exec() - - if (!this.output.get().asFile.exists() && this.isAllowEmpty.getOrElse(false)) { - Files.createFile(this.output.get().asFile.toPath()) - } - - return result - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMaven.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMaven.groovy deleted file mode 100644 index 0f0e2be..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMaven.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevExtension -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal - -import javax.inject.Inject - -@CompileStatic -abstract class MavenizerMCPMaven extends MavenizerExec { - abstract @Input Property getArtifact() - abstract @Internal DirectoryProperty getOutput() - - @Inject - MavenizerMCPMaven() { - this.output.convention(this.project.extensions.getByType(ForgeDevExtension).mavenizerRepo) - } - - @Override - protected void addArguments() { - super.addArguments() - - this.args('--maven', '--global-auxiliary-variants', '--dependencies-only') - this.args('--output', output) - - if (!artifact.get().contains(':')) { - this.args('--artifact', 'net.minecraft:joined') - this.args('--version', artifact) - } else { - this.args('--artifact', artifact) - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMavenValue.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMavenValue.java deleted file mode 100644 index 913ba0e..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPMavenValue.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp; - -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; -import java.util.List; - -public abstract class MavenizerMCPMavenValue extends MavenizerValueSource { - public interface Parameters extends MavenizerValueSource.Parameters { - Property getArtifact(); - - DirectoryProperty getOutput(); - } - - @Override - protected void addArguments(List args) { - super.addArguments(args); - - var parameters = getParameters(); - args.addAll(List.of( - "--maven", "--global-auxiliary-variants", "--dependencies-only", - "--output", parameters.getOutput().get().getAsFile().getAbsolutePath() - )); - - var artifact = parameters.getArtifact().get(); - if (artifact.indexOf(':') < 0) { - args.addAll(List.of( - "--artifact", "net.minecraft:joined", - "--version", artifact - )); - } else { - args.addAll(List.of( - "--artifact", artifact - )); - } - } - - @Override - public Integer obtain() { - return this.exec(); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPSetup.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPSetup.groovy deleted file mode 100644 index 35f35a6..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPSetup.groovy +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevTask -import net.minecraftforge.util.data.json.JsonData -import org.gradle.api.file.ProjectLayout -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -abstract class MavenizerMCPSetup extends MavenizerMCPTask { - abstract @InputFile @Optional RegularFileProperty getAccessTransformerConfig() - abstract @InputFile @Optional RegularFileProperty getSideAnnotationStripperConfig() - abstract @Input Property getRename(); - abstract @Input @Optional Property getParchment() - - protected abstract @OutputFile RegularFileProperty getSetupFiles() - - abstract @OutputFile RegularFileProperty getVersionManifest() - abstract @OutputFile RegularFileProperty getVersionJson() - abstract @OutputFile RegularFileProperty getClientRaw() - abstract @OutputFile RegularFileProperty getClientMappings() - abstract @OutputFile RegularFileProperty getServerRaw() - abstract @OutputFile RegularFileProperty getServerExtracted() - abstract @OutputFile RegularFileProperty getServerMappings() - abstract @OutputFile RegularFileProperty getLibrariesList() - - @Inject - MavenizerMCPSetup() { - rename.convention(true) - - setupFiles.convention(this.defaultOutputDirectory.map { it.file('setup_files.json') }) - - versionManifest.convention(this.defaultOutputDirectory.map { it.file('manifest.json') }) - versionJson.convention(this.defaultOutputDirectory.map { it.file('version.json') }) - clientRaw.convention(this.defaultOutputDirectory.map { it.file('client.jar') }) - clientMappings.convention(this.defaultOutputDirectory.map { it.file('client_mappings.txt') }) - serverRaw.convention(this.defaultOutputDirectory.map { it.file('server_bundled.jar') }) - serverExtracted.convention(this.defaultOutputDirectory.map { it.file('server.jar') }) - serverMappings.convention(this.defaultOutputDirectory.map { it.file('server_mappings.txt') }) - librariesList.convention(this.defaultOutputDirectory.map { it.file('libraries.txt') }) - } - - @Override - protected void addArguments() { - super.addArguments() - - this.args('--mappings', this.rename) - - this.args('--at', this.accessTransformerConfig) - this.args('--sas', this.sideAnnotationStripperConfig) - this.args('--parchment', this.parchment) - - this.args('--output-files', this.setupFiles) - } - - @Override - protected ExecResult exec() { - var result = super.exec() - - var setupFiles = JsonData.fromJson(this.setupFiles.asFile.get(), MCPSetupFiles) - versionManifest.asFile.get().bytes = new File(setupFiles.versionManifest).bytes - versionJson.asFile.get().bytes = new File(setupFiles.versionJson).bytes - clientRaw.asFile.get().bytes = new File(setupFiles.clientRaw).bytes - clientMappings.asFile.get().bytes = new File(setupFiles.clientMappings).bytes - serverRaw.asFile.get().bytes = new File(setupFiles.serverRaw).bytes - serverExtracted.asFile.get().bytes = new File(setupFiles.serverExtracted).bytes - serverMappings.asFile.get().bytes = new File(setupFiles.serverMappings).bytes - librariesList.asFile.get().bytes = new File(setupFiles.librariesList).bytes - - return result - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPTask.groovy deleted file mode 100644 index 74ebaa1..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerMCPTask.groovy +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class MavenizerMCPTask extends MavenizerExec { - // MCP Task - abstract @Input Property getArtifact() // can also be just the version - abstract @OutputFile RegularFileProperty getOutput() - abstract @Input @Optional Property getPipeline() - - @Inject - MavenizerMCPTask() { - this.output.convention(this.defaultOutputFile) - } - - @Override - protected void addArguments() { - super.addArguments() - - //region MCP Task - var artifact = this.artifact.get() - this.args( - '--mcp', - artifact.contains(':') ? '--artifact' : '--version', artifact, - '--output', this.output.get().asFile.absolutePath, - ) - - if (this.pipeline.present) - this.args('--pipeline', this.pipeline.get()) - //endregion - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerRawArtifact.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerRawArtifact.groovy deleted file mode 100644 index 45844d8..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerRawArtifact.groovy +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import org.gradle.api.Project -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskProvider - -import javax.inject.Inject - -@CompileStatic -abstract class MavenizerRawArtifact extends MavenizerMCPTask { - static TaskProvider register(Project project, String pipeline, Provider artifact, Provider srgNames) { - project.tasks.register(getNameFor(pipeline, srgNames), MavenizerRawArtifact, pipeline, artifact, srgNames) - } - - private static String getNameFor(String pipeline, Provider srgNames) { - "raw${pipeline.capitalize()}Jar${srgNames.getOrElse(false) ? 'Srg' : ''}" - } - - abstract @Input Property getSrgNames() - - @Inject - MavenizerRawArtifact(String pipeline, Provider artifact, Provider srg) { - this.output.set(this.localCaches().file("${this.name}.jar")) - this.pipeline.set(pipeline) - this.artifact.set(artifact) - this.srgNames.set(srg) - } - - @Override - protected void addArguments() { - super.addArguments() - - this.args('--raw') - - if (this.srgNames.getOrElse(false)) - this.args('--searge') - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappings.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappings.groovy deleted file mode 100644 index 3965676..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappings.groovy +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.ForgeDevExtension -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional - -import javax.inject.Inject - -@CompileStatic -abstract class MavenizerSyncMappings extends MavenizerExec { - // MCP Task - abstract @Input Property getVersion() - abstract @Input @Optional Property getParchmentVersion() - protected abstract @Internal DirectoryProperty getOutput() - - @Inject - MavenizerSyncMappings() { - this.output.convention(this.project.extensions.getByType(ForgeDevExtension).mavenizerRepo) - } - - //net.minecraft:mappings_channel:version@zip - //--maven --artifact net.minecraft:mappings --version 1.21.7 - @Override - protected void addArguments() { - super.addArguments() - - this.args( - '--maven', - '--output', this.output.locationOnly.get().asFile.absolutePath, - '--artifact', 'net.minecraft:mappings', - '--version', this.version.get() - ) - if (this.parchmentVersion.present) { - this.args('--parchment', this.parchmentVersion.get()) - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappingsValue.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappingsValue.java deleted file mode 100644 index 405a89d..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerSyncMappingsValue.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp; - -import net.minecraftforge.gradleutils.shared.Tool; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; -import java.util.List; - -public abstract class MavenizerSyncMappingsValue extends MavenizerValueSource { - public interface Parameters extends MavenizerValueSource.Parameters { - Property getVersion(); - - Property getParchmentVersion(); - - DirectoryProperty getOutput(); - } - - //net.minecraft:mappings_channel:version@zip - //--maven --artifact net.minecraft:mappings --version 1.21.7 - @Override - protected void addArguments(List args) { - super.addArguments(args); - - var parameters = getParameters(); - args.addAll(List.of( - "--maven", - "--output", parameters.getOutput().get().getAsFile().getAbsolutePath(), - "--artifact", "net.minecraft:mappings", - "--version", parameters.getVersion().get() - )); - - if (parameters.getParchmentVersion().isPresent()) { - args.add("--parchment"); - args.add(parameters.getParchmentVersion().get()); - } - } - - @Override - public Integer obtain() { - return this.exec(); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerValueSource.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerValueSource.java deleted file mode 100644 index c109663..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/mcp/MavenizerValueSource.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.mcp; - -import net.minecraftforge.forgedev.ForgeDevPlugin; -import net.minecraftforge.forgedev.ForgeDevProblems; -import net.minecraftforge.forgedev.Tools; -import net.minecraftforge.gradleutils.shared.Tool; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.ValueSource; -import org.gradle.api.provider.ValueSourceParameters; -import org.gradle.process.ExecOperations; - -import javax.inject.Inject; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -abstract class MavenizerValueSource implements ValueSource { - interface Parameters extends ValueSourceParameters { - default void init(ForgeDevPlugin plugin, ForgeDevProblems problems) { - var tool = plugin.getTool(Tools.MAVENIZER); - - getClasspath().convention(tool.getClasspath()); - getJavaLauncher().convention(tool.getJavaLauncher().map(j -> j.getExecutablePath().getAsFile().getAbsolutePath())); - getCaches().convention(plugin.globalCaches().dir(tool.getName().toLowerCase(Locale.ENGLISH) + "/cache").map(problems.ensureFileLocation())); - } - - Property getName(); - - ConfigurableFileCollection getClasspath(); - - Property getJavaLauncher(); - - DirectoryProperty getCaches(); - } - - private static final Logger LOGGER = Logging.getLogger(MavenizerValueSource.class); - - protected abstract @Inject ExecOperations getExecOperations(); - - @Inject - public MavenizerValueSource() { } - - protected int exec() { - return this.getExecOperations().javaexec(spec -> { - var params = this.getParameters(); - spec.setClasspath(params.getClasspath()); - spec.setExecutable(params.getJavaLauncher().get()); - { - var args = new ArrayList(); - this.addArguments(args); - spec.setArgs(args); - } - - LOGGER.info("Executing Mavenizer: "); - var itr = params.getClasspath().iterator(); - LOGGER.info(" Classpath: {}", itr.next().getAbsolutePath()); - while (itr.hasNext()) - LOGGER.info(" {}", itr.next().getAbsolutePath()); - - LOGGER.info(" Java: {}", params.getJavaLauncher().get()); - var args = spec.getArgs(); - var prefix = " Arguments: "; - for (int x = 0; x < args.size(); x++) { - var current = args.get(x); - var next = args.size() > x + 1 ? args.get(x + 1) : null; - var line = current; - if (current.startsWith("--") && next != null && !next.startsWith("--")) { - x++; - line += ' ' + next; - } - LOGGER.info("{}{}", prefix, line); - prefix = " "; - } - }).rethrowFailure().assertNormalExitValue().getExitValue(); - } - - protected void addArguments(List args) { - var parameters = getParameters(); - args.addAll(List.of( - "--cache", parameters.getCaches().get().getAsFile().getAbsolutePath(), - "--jdk-cache", parameters.getCaches().dir("jdks").get().getAsFile().getAbsolutePath() - )); - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyRenameJar.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyRenameJar.groovy deleted file mode 100644 index 772b517..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyRenameJar.groovy +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.obfuscation - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.Util -import net.minecraftforge.forgedev.tasks.ToolExec -import net.minecraftforge.srgutils.IMappingFile -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.logging.Logger -import org.gradle.api.logging.Logging -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -import javax.inject.Inject -import java.nio.charset.StandardCharsets -import java.nio.file.Files - -@CompileStatic -@Deprecated(forRemoval = true) -@SuppressWarnings('GrDeprecatedAPIUsage') -abstract class LegacyRenameJar extends ToolExec { - private static final Logger LOGGER = Logging.getLogger(LegacyRenameJar) - - protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerActionClasspath() - - abstract @InputFile RegularFileProperty getMappings() - abstract @InputFiles @Optional ConfigurableFileCollection getExtraMappings() - protected abstract @Internal RegularFileProperty getTemporaryMappings() - - abstract @InputFile RegularFileProperty getInput() - abstract @OutputFile RegularFileProperty getOutput() - abstract @InputFiles @Classpath @Optional ConfigurableFileCollection getLibraries() - - protected abstract @Inject WorkerExecutor getWorkerExecutor() - - @Inject - LegacyRenameJar() { - super(Tools.RENAMER) - - this.preferToolchainJvm.set(true) - - this.workerActionClasspath.from( - this.getTool(Tools.SRGUTILS).classpath - ) - - this.temporaryMappings.convention(this.defaultOutputDirectory.map { it.file('mappings_temp.tsrg') }) - this.standardOutputLogLevel.set(LogLevel.INFO) - this.standardErrorLogLevel.set(LogLevel.INFO) - } - - @Override - protected void addArguments() { - super.addArguments() - - var argsList = [ - '--input', this.input.asFile.get().absolutePath, - '--names', this.temporaryMappings.asFile.get().absolutePath, - '--output', this.output.asFile.get().absolutePath - ] - - for (var library in this.libraries.files) { - argsList.add('--lib') - argsList.add(library.absolutePath) - } - - var argsFile = new File(this.temporaryDir, 'args.txt') - Files.write(argsFile.toPath(), argsList, StandardCharsets.UTF_8) - - this.args('--cfg', argsFile.absolutePath) - } - - @Override - protected ExecResult exec() { - final work = this.workerExecutor.classLoaderIsolation { - it.classpath.from(this.workerActionClasspath) - } - - work.submit(Action) { - it.mappings.set(this.mappings) - it.extraMappings.setFrom(this.extraMappings) - it.temporaryMappings.set(this.temporaryMappings) - } - - work.await() - - return super.exec() - } - - @CompileStatic - protected static abstract class Action implements WorkAction { - @CompileStatic - static interface Parameters extends WorkParameters { - RegularFileProperty getMappings() - ConfigurableFileCollection getExtraMappings() - RegularFileProperty getTemporaryMappings() - } - - @Inject - Action() {} - - @Override - void execute() { - var tempMappings = this.parameters.temporaryMappings.asFile.get() - if (tempMappings.getParentFile() != null && !tempMappings.getParentFile().exists() && !tempMappings.getParentFile().mkdirs()) { - LOGGER.warn("WARNING: Could not create parent directories for temp dir '{}'", tempMappings.getAbsolutePath()) - } - - if (tempMappings.exists() && !tempMappings.delete()) { - throw new IllegalStateException("Could not delete temp mappings file: " + tempMappings.getAbsolutePath()) - } else { - var mappings = IMappingFile.load(this.parameters.mappings.asFile.get()) - - for(var file : this.parameters.extraMappings.files) { - mappings = mappings.merge(IMappingFile.load(file)) - } - - mappings.write(tempMappings.toPath(), IMappingFile.Format.TSRG2, false) - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyReobfuscateJar.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyReobfuscateJar.groovy deleted file mode 100644 index de158aa..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/obfuscation/LegacyReobfuscateJar.groovy +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.obfuscation - -import groovy.transform.CompileStatic -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.Util -import net.minecraftforge.forgedev.tasks.ToolExec -import net.minecraftforge.srgutils.IMappingFile -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.logging.Logger -import org.gradle.api.logging.Logging -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.InputFiles -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -import javax.inject.Inject -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.util.zip.ZipEntry -import java.util.zip.ZipFile -import java.util.zip.ZipOutputStream - -@CompileStatic -@Deprecated(forRemoval = true) -@SuppressWarnings('GrDeprecatedAPIUsage') -abstract class LegacyReobfuscateJar extends ToolExec { - private static final Logger LOGGER = Logging.getLogger(LegacyReobfuscateJar) - - protected abstract @InputFiles @Classpath ConfigurableFileCollection getWorkerActionClasspath() - - abstract @InputFile RegularFileProperty getInput() - abstract @InputFile RegularFileProperty getSrg() - abstract @OutputFile RegularFileProperty getOutput() - abstract @InputFiles @Classpath @Optional ConfigurableFileCollection getLibraries() - - abstract @Input Property getKeepPackages() - abstract @Input Property getKeepData() - - protected abstract @Inject WorkerExecutor getWorkerExecutor() - - @Inject - LegacyReobfuscateJar() { - super(Tools.RENAMER) - - this.preferToolchainJvm.set(true) - - this.workerActionClasspath.from( - this.getTool(Tools.SRGUTILS).classpath - ) - - this.standardOutputLogLevel.set(LogLevel.INFO) - - this.keepPackages.convention(false) - this.keepData.convention(false) - } - - private File getTemporaryOutput() { - return new File(this.temporaryDir, 'output.jar') - } - - @Override - protected void addArguments() { - super.addArguments() - - var argsList = [ - '--input', this.input.get().asFile.absolutePath, - '--map', this.srg.get().asFile.absolutePath, - '--output', this.temporaryOutput.absolutePath - ] - - for (var library in this.libraries.files) { - argsList.add('--lib') - argsList.add(library.absolutePath) - } - - var argsFile = new File(this.temporaryDir, 'args.txt') - Files.write(argsFile.toPath(), argsList, StandardCharsets.UTF_8) - - this.args('--cfg', argsFile.absolutePath) - } - - @Override - protected ExecResult exec() { - var result = super.exec().rethrowFailure().assertNormalExitValue() - - final work = this.workerExecutor.classLoaderIsolation { - it.classpath.from(this.workerActionClasspath) - } - - work.submit(Action) { - it.logLevel.set(standardOutputLogLevel) - - it.srg.set(this.srg) - it.output.set(this.output) - it.temporaryOutput.set(this.temporaryOutput) - - it.keepPackages.set(this.keepPackages) - it.keepData.set(this.keepData) - } - - work.await() - - return result - } - - @CompileStatic - protected static abstract class Action implements WorkAction { - @CompileStatic - static interface Parameters extends WorkParameters { - Property getLogLevel() - - RegularFileProperty getSrg() - RegularFileProperty getOutput() - RegularFileProperty getTemporaryOutput() - - Property getKeepPackages() - Property getKeepData() - } - - @Inject - Action() {} - - @Override - void execute() { - var packages = new HashSet() - var srgMappings = IMappingFile.load(parameters.srg.asFile.get()) - for (IMappingFile.IClass srgClass : srgMappings.getClasses()) { - String named = srgClass.getOriginal() - int idx = named.lastIndexOf('/') - if (idx != -1) { - packages.add(named.substring(0, idx + 1) + "package-info.class") - } - } - - var temporaryOutput = parameters.temporaryOutput.asFile.get() - try (ZipFile zin = new ZipFile(temporaryOutput) - ZipOutputStream out = new ZipOutputStream(new FileOutputStream(parameters.output.asFile.get()))) { - for (Enumeration enu = zin.entries(); enu.hasMoreElements(); ) { - ZipEntry entry = enu.nextElement() - boolean filter = entry.isDirectory() || entry.getName().startsWith("mcp/") //Directories and MCP's annotations - if (!parameters.keepPackages.get()) filter |= packages.contains(entry.getName()) - if (!parameters.keepData.get()) filter |= !entry.getName().endsWith(".class") - - if (filter) { - LOGGER.log(parameters.logLevel.get(), "Filtered: {}", entry.getName()) - continue - } - out.putNextEntry(entry) - zin.getInputStream(entry).transferTo(out) - out.closeEntry() - } - } - - if (!temporaryOutput.delete()) - LOGGER.warn('WARNING: Failed to delete temporary output: {}', temporaryOutput.absolutePath) - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.groovy deleted file mode 100644 index e6339a2..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.groovy +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.binary - -import groovy.transform.CompileStatic -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles - -import javax.inject.Inject - -@CompileStatic -abstract class ApplyBinPatches extends BinaryPatcherExec { - // Create - abstract @InputFiles ConfigurableFileCollection getApply() - abstract @Input Property getData() - abstract @Input Property getUnpatched() - - @Inject - ApplyBinPatches() { - this.data.convention(false) - this.unpatched.convention(false) - } - - @Override - protected void addArguments() { - super.addArguments() - - if (!this.apply.empty) { - this.apply.forEach { - this.args('--apply', it.absolutePath) - } - } else { - throw new IllegalArgumentException('No apply!') - } - - if (this.data.getOrElse(false)) - this.args('--data') - - if (this.unpatched.getOrElse(false)) - this.args('--unpatched') - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.java new file mode 100644 index 0000000..8626407 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/ApplyBinPatches.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.binary; + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; + +import javax.inject.Inject; + +public abstract class ApplyBinPatches extends BinaryPatcherExec { + // Create + public abstract @InputFiles ConfigurableFileCollection getApply(); + public abstract @Input Property getData(); + public abstract @Input Property getUnpatched(); + public abstract @Input Property getStore(); + public abstract @Input @Optional Property getMarker(); + + @Inject + public ApplyBinPatches() { + this.getData().convention(false); + this.getUnpatched().convention(false); + this.getStore().convention(false); + } + + @Override + protected void addArguments() { + super.addArguments(); + + if (!this.getApply().isEmpty()) { + this.getApply().forEach(file -> + this.args("--apply", file.getAbsolutePath()) + ); + } else { + throw new IllegalArgumentException("No apply!"); + } + + if (this.getData().getOrElse(false)) + this.args("--data"); + + if (this.getUnpatched().getOrElse(false)) + this.args("--unpatched"); + + if (this.getStore().getOrElse(false)) + this.args("--store"); + + if (this.getMarker().isPresent()) + this.args("--marker", this.getMarker().get()); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.groovy deleted file mode 100644 index f18662b..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.groovy +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.binary - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.Util -import net.minecraftforge.forgedev.tasks.SingleFileOutput -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.problems.Problems -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.Optional -import org.gradle.api.tasks.OutputFile - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class BinaryPatcherExec extends ToolExec implements SingleFileOutput { - // Shared - abstract @InputFiles ConfigurableFileCollection getClean() - @Override - abstract @OutputFile RegularFileProperty getOutput() - abstract @Input @Optional ListProperty getPrefix() - abstract @Input Property getPack200() - abstract @Deprecated @Input Property getLegacy() - - @Inject - @SuppressWarnings('GrDeprecatedAPIUsage') // setting convention "false" for legacy - BinaryPatcherExec() { - super(Tools.BINPATCH) - - this.output.convention(this.defaultOutputFile) - this.pack200.convention(false) - this.legacy.convention(false) - - this.standardOutputLogLevel.set(LogLevel.INFO) - } - - @Override - protected void addArguments() { - if (!this.clean.empty) { - this.clean.forEach { - this.args('--clean', it.absolutePath) - } - } else { - throw new IllegalArgumentException('no clean!') - } - - this.args('--output', this.output.get().asFile.absolutePath) - - if (this.prefix.present) { - this.prefix.get().forEach { - this.args('--prefix', it) - } - } - - if (this.pack200.getOrElse(false)) - this.args('--pack200') - - //noinspection GrDeprecatedAPIUsage - if (this.legacy.getOrElse(false)) - this.args('--legacy') - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.java new file mode 100644 index 0000000..d2f4319 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/BinaryPatcherExec.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.binary; + +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.ToolExec; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.LogLevel; +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.Optional; +import org.gradle.api.tasks.OutputFile; + +import javax.inject.Inject; + +abstract class BinaryPatcherExec extends ToolExec implements SingleFileOutput { + // Shared + public abstract @InputFiles ConfigurableFileCollection getClean(); + public abstract @Input @Optional ListProperty getPrefix(); + public abstract @Input Property getPack200(); + public abstract @Deprecated @Input Property getLegacy(); + + public abstract @Override @OutputFile RegularFileProperty getOutput(); + + @Inject + public BinaryPatcherExec() { + super(Tools.BINPATCH); + + this.getOutput().convention(this.getDefaultOutputFile()); + this.getPack200().convention(false); + this.getLegacy().convention(false); + + this.getStandardOutputLogLevel().set(LogLevel.INFO); + } + + @Override + protected void addArguments() { + if (!this.getClean().isEmpty()) { + this.getClean().forEach( file -> + this.args("--clean", file.getAbsolutePath()) + ); + } else { + throw new IllegalArgumentException("no clean!"); + } + + this.args("--output", this.getOutput().getAsFile().get().getAbsolutePath()); + + if (this.getPrefix().isPresent()) { + this.getPrefix().get().forEach( prefix -> + this.args("--prefix", prefix) + ); + } + + if (this.getPack200().getOrElse(false)) + this.args("--pack200"); + + if (this.getLegacy().getOrElse(false)) + this.args("--legacy"); + + super.addArguments(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.groovy deleted file mode 100644 index 26365c0..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.groovy +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.binary - -import groovy.transform.CompileStatic -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.problems.Problems -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional - -import javax.inject.Inject - -@CompileStatic -abstract class CreateBinPatches extends BinaryPatcherExec { - // Create - abstract @InputFiles ConfigurableFileCollection getCreate() - abstract @InputFiles @Optional ConfigurableFileCollection getPatches() - abstract @InputFiles @Optional ConfigurableFileCollection getSrg() - - @Inject - CreateBinPatches() { } - - @Override - protected void addArguments() { - super.addArguments() - - if (!this.create.empty) { - this.create.forEach { - this.args('--create', it.absolutePath) - } - } else { - throw new IllegalArgumentException('No create!') - } - - this.patches.forEach { - this.args('--patches', it.absolutePath) - } - - this.srg.forEach { - this.args('--srg', it.absolutePath) - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.java new file mode 100644 index 0000000..6fcf79b --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/binary/CreateBinPatches.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.binary; + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; + +import javax.inject.Inject; + +public abstract class CreateBinPatches extends BinaryPatcherExec { + // Create + public abstract @InputFiles ConfigurableFileCollection getDirty(); + public abstract @InputFiles @Optional ConfigurableFileCollection getPatches(); + public abstract @InputFiles @Optional ConfigurableFileCollection getSrg(); + public abstract @InputFiles @Optional ConfigurableFileCollection getSas(); + public abstract @Input Property getReverseSrg(); + + @Inject + public CreateBinPatches() { + getReverseSrg().convention(false); + } + + @Override + protected void addArguments() { + super.addArguments(); + + if (!this.getDirty().isEmpty()) { + this.getDirty().forEach(file -> + this.args("--create", file.getAbsolutePath()) + ); + } else { + throw new IllegalArgumentException("No create!"); + } + + this.getPatches().forEach(file -> + this.args("--patches", file.getAbsolutePath()) + ); + + if (this.getReverseSrg().getOrElse(false)) { + this.args("--reverse-srg"); + } + + this.getSrg().forEach(file -> + this.args("--srg", file.getAbsolutePath()) + ); + + this.getSas().forEach(file -> + this.args("--sas", file.getAbsolutePath()) + ); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.groovy deleted file mode 100644 index 6ee7d76..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -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.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -abstract class ApplyPatches extends BasePatchTask { - abstract @Input Property getFailOnError() - - private final RegularFileProperty input = this.objects.fileProperty() - private final DirectoryProperty patches = this.objects.directoryProperty() - private final RegularFileProperty output = this.objects.fileProperty() - - @InputFile RegularFileProperty getInput() { this.input } - @InputDirectory @Optional DirectoryProperty getPatches() { this.patches } - @OutputFile RegularFileProperty getOutput() { this.output } - - @Inject - ApplyPatches() { - this.failOnError.convention(true) - - this.logLevel.convention('warn') - } - - @Override - protected ExecResult exec() { - if (!this.patches.isPresent()) { - this.output.get().asFile.bytes = this.input.get().asFile.bytes - return - } - - var result = super.exec() - var exitValue = result.exitValue - if (exitValue !== 0) { - // patches failed - if (exitValue !== 1) - result.rethrowFailure() - - // some other error - if (this.failOnError.get()) - result.assertNormalExitValue() - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.java new file mode 100644 index 0000000..d6a0a61 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/ApplyPatches.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.diff; + +import net.minecraftforge.forgedev.Util; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputFiles; +import org.gradle.process.ExecResult; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +public abstract class ApplyPatches extends BaseDiffPatchExec { + public abstract @InputFiles ConfigurableFileCollection getPatches(); + public abstract @OutputFiles @Optional ConfigurableFileCollection getRejects(); + + // Patch specific + public abstract @Input Property getFailOnError(); + public abstract @Input @Optional Property getArchiveRejects(); + public abstract @Input @Optional Property getFuzz(); + public abstract @Input @Optional Property getOffset(); + public abstract @Input @Optional Property getMode(); + public abstract @Input @Optional Property getArchivePatches(); + + // Patch shared + public abstract @Input @Optional Property getPrefix(); + + @Inject + public ApplyPatches() { + this.getStandardOutputLogLevel().convention(LogLevel.WARN); + this.getFailOnError().convention(true); + this.getLogLevel().convention("warn"); + this.getMode().convention("access"); + } + + @Override + protected void addArguments() { + super.addArguments(); + + //region Patch specific + if (!this.getRejects().isEmpty()) + this.args("--reject", getRejects().getSingleFile()); + if (this.getArchiveRejects().isPresent()) + this.args("--archive-rejects", this.getArchiveRejects().get()); + if (this.getFuzz().isPresent()) + this.args("--fuzz", this.getFuzz().get()); + if (this.getOffset().isPresent()) + this.args("--offset", this.getOffset().get()); + if (this.getMode().isPresent()) + this.args("--mode", this.getMode().get()); + if (this.getArchivePatches().isPresent()) + this.args("--archive-patches", this.getArchivePatches().get()); + //endregion + + //region Patch shared + if (this.getPrefix().isPresent()) + this.args("--prefix", this.getPrefix().get()); + //endregion + + //region Patch Task + // https://github.com/TheCBProject/DiffPatch/blob/204d393ee23f5cd4298f771c7b9157ee21eb3b62/src/main/java/io/codechicken/diffpatch/cli/DiffPatchCli.java#L191 + // --patch {base} {patches} + this.args( + "--patch", + getInput().getSingleFile(), + getPatches().getSingleFile() + ); + //endregion + } + + @Override + protected @Nullable ExecResult exec() throws IOException { + ExecResult result = null; + var output = getOutput().getAsFile().get(); + + // No patches, not sure when this would ever come up, but isn't hard to support + if (this.getPatches().isEmpty()) { + var input = getInput().getSingleFile(); + if (output.getParent() != null) + Files.createDirectories(output.getParentFile().toPath()); + Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING); + } else { + result = super.exec(); + + var exitValue = result.getExitValue(); + if (exitValue != 0) { + // patches failed + if (exitValue != 1) + result.rethrowFailure(); + + // some other error + if (this.getFailOnError().get()) + result.assertNormalExitValue(); + } + } + if (this.getOutputDirectory().isPresent()) + Util.extractZip(output, this.getOutputDirectory().getAsFile().get(), true); + return result; + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BakePatches.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BakePatches.groovy deleted file mode 100644 index a9a197f..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BakePatches.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.process.ExecResult - -import javax.inject.Inject -import java.util.zip.ZipOutputStream - -@CompileStatic -abstract class BakePatches extends BasePatchBakingTask { - private final DirectoryProperty input = this.objects.directoryProperty() - private final RegularFileProperty output = this.objects.fileProperty() - - @InputDirectory @Optional DirectoryProperty getInput() { this.input } - @OutputFile RegularFileProperty getOutput() { this.output } - - @Inject - BakePatches() { } - - @Override - protected ExecResult exec() { - if (!this.input.present) { - // create empty zip for output - new ZipOutputStream(new FileOutputStream(this.output.get().asFile)).close() - return null - } - - super.exec() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.groovy deleted file mode 100644 index 591d6b0..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.groovy +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileSystemLocation -import org.gradle.api.file.FileSystemLocationProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Console -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class BaseDiffPatchExec extends ToolExec { - /* CLI FLAGS - See io.codechicken.diffpatch.cli.DiffPatchCli#mainI, or run --help on the fat jar */ - - // Utility - abstract @Input @Console Property getVerbose() - abstract @Input @Optional @Console Property getLogLevel() - abstract @Input @Console Property getSummary() - - // Shared - FileSystemLocationProperty getInput() { - throw new IllegalStateException('Must be overridden with RegularFileProperty or DirectoryProperty') - } - FileSystemLocationProperty getOutput() { - throw new IllegalStateException('Must be overridden with RegularFileProperty or DirectoryProperty') - } - abstract @Input @Optional Property getArchive() - abstract @Input @Optional Property getArchiveBase() - abstract @Input @Optional Property getBasePathPrefix() - abstract @Input @Optional Property getModifiedPathPrefix() - abstract @Input @Optional Property getLineEndings() - - @Inject - BaseDiffPatchExec() { - super(Tools.DIFFPATCH) - - if (this.input instanceof DirectoryProperty) - this.archiveBase.unset().disallowChanges() - if (this.output instanceof DirectoryProperty) - this.archive.unset().disallowChanges() - - this.verbose.convention(false) - this.summary.convention(false) - } - - protected void addArguments() { - //region Utility - if (this.verbose.get()) - this.args('--verbose') - if (this.logLevel.present) - this.args('--log-level', this.logLevel.get()) - if (this.summary.get()) - this.args('--summary') - //endregion - - //region Shared - if (this.output.present) - this.args('--output', this.output.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath) - if (this.archive.present) - this.args('--archive', this.archive.get()) - if (this.archiveBase.present) - this.args('--archive-base', this.archiveBase.get()) - if (this.basePathPrefix.present) - this.args('--base-path-prefix', this.basePathPrefix.get()) - if (this.modifiedPathPrefix.present) - this.args('--modified-path-prefix', this.modifiedPathPrefix.get()) - if (this.lineEndings.present) - this.args('--line-endings', this.lineEndings.map { - switch (it) { - case '\r': - return 'CR' - case '\n': - return 'LF' - case '\r\n': - return 'CRLF' - default: - return it - } - }.get()) - //endregion - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.java new file mode 100644 index 0000000..d21cd83 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffPatchExec.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.diff; + +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.ToolExec; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Console; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.OutputFile; + +import javax.inject.Inject; + +public abstract class BaseDiffPatchExec extends ToolExec implements SingleFileOutput { + /* CLI FLAGS - See io.codechicken.diffpatch.cli.DiffPatchCli#mainI, or run --help on the fat jar */ + + // Utility + public abstract @Input @Console Property getVerbose(); + public abstract @Input @Optional @Console Property getLogLevel(); + public abstract @Input @Console Property getSummary(); + + public abstract @InputFiles ConfigurableFileCollection getInput(); + + public abstract @OutputFile RegularFileProperty getOutput(); + public abstract @Optional @OutputDirectory DirectoryProperty getOutputDirectory(); + + public abstract @Input @Optional Property getArchive(); + public abstract @Input @Optional Property getArchiveBase(); + public abstract @Input @Optional Property getBasePathPrefix(); + public abstract @Input @Optional Property getModifiedPathPrefix(); + public abstract @Input @Optional Property getLineEndings(); + + @Inject + protected BaseDiffPatchExec() { + super(Tools.DIFFPATCH); + this.getOutput().convention(this.getDefaultOutputFile()); + this.getVerbose().convention(false); + this.getSummary().convention(false); + } + + protected void addArguments() { + //region Utility + if (this.getVerbose().get()) + this.args("--verbose"); + if (this.getLogLevel().isPresent()) + this.args("--log-level", this.getLogLevel().get()); + if (this.getSummary().get()) + this.args("--summary"); + //endregion + + //region Shared + this.args("--output", this.getOutput().getAsFile().get()); + if (this.getArchive().isPresent()) + this.args("--archive", this.getArchive().get()); + if (this.getArchiveBase().isPresent()) + this.args("--archive-base", this.getArchiveBase().get()); + if (this.getBasePathPrefix().isPresent()) + this.args("--base-path-prefix", this.getBasePathPrefix().get()); + if (this.getModifiedPathPrefix().isPresent()) + this.args("--modified-path-prefix", this.getModifiedPathPrefix().get()); + if (this.getLineEndings().isPresent()) { + this.args("--line-endings", this.getLineEndings().map(val -> switch (val) { + case "\r" -> "CR"; + case "\n" -> "LF"; + case "\r\n" -> "CRLF"; + default -> val; + }).get()); + } + //endregion + + super.addArguments(); + } + + /* + protected File resolve(String name, ConfigurableFileCollection cfg) { + getLogger().lifecycle("Resolving " + name); + for (var from : cfg.getFrom()) { + getLogger().lifecycle(" From: " + from); + } + for (var file : cfg.getFiles()) { + getLogger().lifecycle(" File: " + file); + } + var itr = cfg.iterator(); + if (!itr.hasNext()) + throw new IllegalStateException("Can not find Files for " + name + " no values specified"); + + var ret = itr.next(); + var absolute = ret.getAbsolutePath(); + if (itr.hasNext()) { + // If there are extra files then assume we are a directory and try and find the common root. + if (!ret.isDirectory()) + throw new IllegalStateException("Can not find Files for " + name + " first entry not a directory: " + ret); + var next = itr.next().getAbsolutePath(); + if (!next.startsWith(absolute)) + throw new IllegalStateException("Could not find shared directory for " + name + " child was not a sub-entry: " + next); + } + getLogger().lifecycle(" Resolved: " + ret); + return ret; + } + */ +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffTask.groovy deleted file mode 100644 index 824afe6..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BaseDiffTask.groovy +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileSystemLocation -import org.gradle.api.file.FileSystemLocationProperty -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class BaseDiffTask extends BaseDiffPatchExec { - FileSystemLocationProperty getModified() { - throw new IllegalStateException('Must be overridden with RegularFileProperty or DirectoryProperty') - } - - // Diff specific - abstract @Input Property getDiff() - abstract @Input Property getAutoHeader() - abstract @Input @Optional Property getContext() - abstract @Input @Optional Property getArchiveModified() - - @Inject - BaseDiffTask() { - if (this.modified instanceof DirectoryProperty) - this.archiveModified.unset().disallowChanges() - - this.diff.convention(false) - this.autoHeader.convention(false) - } - - @Override - protected void addArguments() { - super.addArguments() - - //region Diff specific - if (this.autoHeader.get()) - this.args('--auto-header') - if (this.context.present) - this.args('--context', this.context.get()) - if (this.archiveModified.present) - this.args('--archive-modified', this.archiveModified.get()) - //endregion - - // https://github.com/TheCBProject/DiffPatch/blob/204d393ee23f5cd4298f771c7b9157ee21eb3b62/src/main/java/io/codechicken/diffpatch/cli/DiffPatchCli.java#L155 - // --diff {base} {modified} - this.args( - '--diff', - this.input.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath, - this.modified.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath - ) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchBakingTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchBakingTask.groovy deleted file mode 100644 index dc60c3d..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchBakingTask.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class BasePatchBakingTask extends BaseDiffPatchExec { - // Patch shared - abstract @Input @Optional Property getPrefix() - - @Inject - BasePatchBakingTask() { } - - @Override - protected void addArguments() { - super.addArguments() - - //region Patch shared - if (this.prefix.present) - this.args('--prefix', this.prefix.get()) - //endregion - - // https://github.com/TheCBProject/DiffPatch/blob/204d393ee23f5cd4298f771c7b9157ee21eb3b62/src/main/java/io/codechicken/diffpatch/cli/DiffPatchCli.java#L235 - // --bake {input_patches} - this.args( - '--bake', - this.input.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath - ) - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchTask.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchTask.groovy deleted file mode 100644 index 540cfbb..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/BasePatchTask.groovy +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.FileSystemLocation -import org.gradle.api.file.FileSystemLocationProperty -import org.gradle.api.file.RegularFile -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity -import org.gradle.work.DisableCachingByDefault - -import javax.inject.Inject -import java.nio.file.Files - -@DisableCachingByDefault(because = 'Abstract super-class, not to be instantiated directly') -@CompileStatic -@PackageScope abstract class BasePatchTask extends BaseDiffPatchExec { - FileSystemLocationProperty getPatches() { - throw new IllegalStateException('Must be overridden with RegularFileProperty or DirectoryProperty') - } - - // Patch specific - @Internal Property getRejects() { this.rejects } - abstract @Input @Optional Property getArchiveRejects() - abstract @Input @Optional Property getFuzz() - abstract @Input @Optional Property getOffset() - abstract @Input @Optional Property getMode() - abstract @Input @Optional Property getArchivePatches() - - // Patch shared - abstract @Input @Optional Property getPrefix() - - void setRejects(Provider rejects) { - final value = rejects.get() - - if (value instanceof RegularFile) - this.setRejects(value) - else if (value instanceof Directory) - this.setRejects(value) - else - this.rejects.set((File) rejects) - } - - void setRejects(RegularFile rejects) { - this.rejects.set(this.providers.provider { - rejects.asFile - }) - } - - void setRejects(Directory rejects) { - this.rejects.set(this.providers.provider { - rejects.asFile.tap { - Files.createDirectories(it.toPath()) - } - }) - } - - private final Property rejects = this.objects.property(File) - - @Inject - BasePatchTask() { - if (this.rejects instanceof DirectoryProperty) - this.archiveRejects.unset().disallowChanges() - if (this.patches instanceof DirectoryProperty) - this.archivePatches.unset().disallowChanges() - } - - @Override - protected void addArguments() { - super.addArguments() - - //region Patch specific - if (this.rejects.present) - this.args('--reject', this.rejects.get().absolutePath) - if (this.archiveRejects.present) - this.args('--archive-rejects', this.archiveRejects.get()) - if (this.fuzz.present) - this.args('--fuzz', this.fuzz.get()) - if (this.offset.present) - this.args('--offset', this.offset.get()) - if (this.mode.present) - this.args('--mode', this.mode.get()) - if (this.archivePatches.present) - this.args('--archive-patches', this.archivePatches.get()) - //endregion - - //region Patch shared - if (this.prefix.present) - this.args('--prefix', this.prefix.get()) - //endregion - - //region Patch Task - // https://github.com/TheCBProject/DiffPatch/blob/204d393ee23f5cd4298f771c7b9157ee21eb3b62/src/main/java/io/codechicken/diffpatch/cli/DiffPatchCli.java#L191 - // --patch {base} {patches} - this.args( - '--patch', - this.input.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath, - this.patches.locationOnly.map(this.problems.ensureFileLocation()).get().asFile.absolutePath - ) - //endregion - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.groovy deleted file mode 100644 index 3c21250..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.groovy +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.patching.diff - -import groovy.transform.CompileStatic -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputDirectory - -@CompileStatic -abstract class GeneratePatches extends BaseDiffTask { - private final RegularFileProperty input = this.objects.fileProperty() - private final RegularFileProperty modified = this.objects.fileProperty() - private final DirectoryProperty output = this.objects.directoryProperty() - - @InputFile RegularFileProperty getInput() { this.input } - @InputFile RegularFileProperty getModified() { this.modified } - @OutputDirectory DirectoryProperty getOutput() { this.output } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.java new file mode 100644 index 0000000..fa0f815 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/patching/diff/GeneratePatches.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.patching.diff; + +import net.minecraftforge.forgedev.Util; +import org.gradle.api.file.ConfigurableFileCollection; +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.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.OutputFile; +import org.gradle.process.ExecResult; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.IOException; + +public abstract class GeneratePatches extends BaseDiffPatchExec { + public abstract @InputFiles ConfigurableFileCollection getModified(); + public abstract @OutputFile RegularFileProperty getOutput(); + public abstract @Optional @OutputDirectory DirectoryProperty getOutputDirectory(); + + // Diff specific + public abstract @Input Property getDiff(); + public abstract @Input Property getAutoHeader(); + public abstract @Input @Optional Property getContext(); + public abstract @Input @Optional Property getArchiveModified(); + + @Inject + public GeneratePatches() { + this.getDiff().convention(false); + this.getAutoHeader().convention(false); + this.getOutput().convention(this.getDefaultOutputFile()); + } + + @Override + protected void addArguments() { + super.addArguments(); + + //region Diff specific + if (this.getAutoHeader().get()) + this.args("--auto-header"); + if (this.getContext().isPresent()) + this.args("--context", this.getContext().get()); + if (this.getArchiveModified().isPresent()) + this.args("--archive-modified", this.getArchiveModified().get()); + //endregion + + // https://github.com/TheCBProject/DiffPatch/blob/204d393ee23f5cd4298f771c7b9157ee21eb3b62/src/main/java/io/codechicken/diffpatch/cli/DiffPatchCli.java#L155 + // --diff {base} {modified} + this.args( + "--diff", + this.getInput().getSingleFile(), + this.getModified().getSingleFile() + ); + } + + @Override + protected @Nullable ExecResult exec() throws IOException { + var result = super.exec().rethrowFailure(); + var output = getOutput().getAsFile().get(); + if (this.getOutputDirectory().isPresent()) + Util.extractZip(output, this.getOutputDirectory().getAsFile().get(), true); + return result; + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/sas/CreateFakeSASPatches.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/sas/CreateFakeSASPatches.java deleted file mode 100644 index 1904d44..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/sas/CreateFakeSASPatches.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.sas; - -import net.minecraftforge.forgedev.ForgeDevTask; -import org.codehaus.groovy.runtime.ResourceGroovyMethods; -import org.gradle.api.DefaultTask; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.OutputDirectory; -import org.gradle.api.tasks.TaskAction; -import org.gradle.workers.WorkAction; -import org.gradle.workers.WorkParameters; -import org.gradle.workers.WorkerExecutor; - -import javax.inject.Inject; -import java.io.File; -import java.io.IOException; - -// TODO [ForgeDev] This does not account for incremental changes. -public abstract class CreateFakeSASPatches extends DefaultTask implements ForgeDevTask { - public abstract @InputFiles ConfigurableFileCollection getFiles(); - - public abstract @OutputDirectory DirectoryProperty getOutput(); - - protected abstract @Inject WorkerExecutor getWorkerExecutor(); - - public CreateFakeSASPatches() { - getOutput().convention(getDefaultOutputDirectory().map(d -> d.dir("patches"))); - } - - @TaskAction - public void apply() throws IOException { - { - var output = getOutput().get().getAsFile(); - if (!output.exists() && !output.mkdirs()) - throw new IllegalStateException("Failed to make directory: " + output); - } - - var queue = getWorkerExecutor().noIsolation(); - - for (var file : getFiles()) { - for (var line : ResourceGroovyMethods.readLines(file)) { - if (line.isBlank()) continue; - - queue.submit(Action.class, parameters -> { - parameters.getOutput().set(this.getOutput()); - parameters.getLine().set(line); - }); - } - } - - queue.await(); - } - - static abstract class Action implements WorkAction { - interface Parameters extends WorkParameters { - DirectoryProperty getOutput(); - - Property getLine(); - } - - @Inject - public Action() { } - - @Override - public void execute() { - var output = getParameters().getOutput().getAsFile().get(); - var line = getParameters().getLine().get(); - - int idx = line.indexOf('#'); - if (idx == 0) return; - - if (idx != -1) line = line.substring(0, idx - 1); - if (line.charAt(0) == '\t') line = line.substring(1); - var cls = (line.trim() + " ").split(" ", -1)[0]; - var patch = new File(output, cls + ".java.patch"); - if (!patch.getParentFile().exists() && !patch.getParentFile().mkdirs()) - throw new IllegalStateException("Failed to make directory: " + patch.getParentFile()); - - try { - patch.createNewFile(); - } catch (IOException e) { - throw new IllegalStateException("Failed to create empty file: " + patch, e); - } - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/Shim.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/Shim.java index 853183b..2f7ce26 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/Shim.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/Shim.java @@ -7,15 +7,18 @@ import net.minecraftforge.forgedev.ForgeDevExtension; import net.minecraftforge.forgedev.Tools; import net.minecraftforge.forgedev.legacy.tasks.DownloadDependency; +import net.minecraftforge.forgedev.legacy.values.MavenInfo; import net.minecraftforge.gradleutils.shared.SharedUtil; import org.gradle.api.Action; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.bundling.Jar; import org.gradle.language.base.plugins.LifecycleBasePlugin; import org.jetbrains.annotations.ApiStatus; import javax.inject.Inject; +import java.util.ArrayList; public abstract class Shim { public static final String DEFAULT_NAME = "serverShim"; @@ -70,6 +73,17 @@ public void setBase(String artifact) { base.configure(task -> task.setArtifact(artifact) ); } + public void bootstrapClasspath(Configuration cfg) { + jar(task -> { + var deps = new ArrayList(); + for (var dep : cfg.getIncoming().getArtifacts()) { + var info = MavenInfo.from(project, dep); + deps.add("libraries/" + info.path()); + } + task.getManifest().getAttributes().put("Class-Path", String.join(" ", deps)); + }); + } + @ApiStatus.Internal public static Shim register(Project project, ForgeDevExtension ext, String name) { var base = DownloadDependency.register(project, name + "DownloadBase", Tools.SHIM.getModule().toString()); diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimClasspath.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimClasspath.java index fd37557..bd34b93 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimClasspath.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimClasspath.java @@ -5,10 +5,10 @@ package net.minecraftforge.forgedev.tasks.shim; import net.minecraftforge.forgedev.ForgeDevTask; -import net.minecraftforge.forgedev.legacy.tasks.Util; import net.minecraftforge.forgedev.legacy.values.MavenInfo; import net.minecraftforge.forgedev.legacy.values.MinimalResolvedArtifact; import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.util.hash.HashFunction; import org.gradle.api.DefaultTask; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; @@ -66,7 +66,7 @@ public void run() throws IOException { var key = info.group() + ':' + info.name(); if (info.classifier() != null) key += ':' + info.classifier(); - entries.put(key, Util.sha256(lib.file()) + '\t' + lib.info().name() + '\t' + lib.info().path()); + entries.put(key, HashFunction.SHA256.hash(lib.file()) + '\t' + lib.info().name() + '\t' + lib.info().path()); } // Copy from vanilla if we don't overwrite the version diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimConfig.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimConfig.java index daf5f16..773d68a 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimConfig.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/shim/ShimConfig.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.forgedev.tasks.shim; -import net.minecraftforge.forgedev.legacy.tasks.CleanProperties; +import net.minecraftforge.forgedev.CleanProperties; import net.minecraftforge.forgedev.tasks.SingleFileOutput; import org.gradle.api.DefaultTask; import org.gradle.api.file.ProjectLayout; diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ApplyRangeMap.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ApplyRangeMap.groovy deleted file mode 100644 index b0f2173..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ApplyRangeMap.groovy +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.srg2source - -import groovy.transform.CompileStatic -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.problems.Problems -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.Optional -import org.gradle.api.tasks.OutputFile - -import javax.inject.Inject - -@CompileStatic -abstract class ApplyRangeMap extends S2SExec { - abstract @InputFiles ConfigurableFileCollection getSources() - abstract @OutputFile RegularFileProperty getOutput() - abstract @InputFiles @Optional ConfigurableFileCollection getExcFiles() - abstract @InputFiles ConfigurableFileCollection getSrgFiles() - - abstract @InputFile RegularFileProperty getRangeMap() - abstract @Input @Optional Property getKeepImports() - abstract @Input @Optional @Deprecated Property getAnnotate() - - abstract @Input @Optional Property getSortImports() - abstract @Input @Optional Property getGuessLambdas() - abstract @Input @Optional Property getGuessLocals() - - @Inject - ApplyRangeMap() { - this.output.convention(this.defaultOutputFile) - } - - @Override - protected void addArguments() { - this.args('--apply') - - this.args('--in', this.sources) - this.args('--out', this.output) - this.args('--exc', this.excFiles) - this.args('--srg', this.srgFiles) - - this.args('--range', this.rangeMap) - this.args('--keepImports', this.keepImports) - //this.args('--annotate', this.annotate) - - this.args('--sortImports', this.sortImports) - this.args('--guessLambdas', this.guessLambdas) - this.args('--guessLocals', this.guessLocals) - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ExtractRangeMap.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ExtractRangeMap.groovy deleted file mode 100644 index b14c2c3..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/ExtractRangeMap.groovy +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.srg2source - -import groovy.transform.CompileStatic -import org.gradle.api.JavaVersion -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.problems.Problems -import org.gradle.api.provider.Property -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.jvm.toolchain.JavaLanguageVersion - -import javax.inject.Inject - -@CompileStatic -abstract class ExtractRangeMap extends S2SExec { - abstract @InputFiles @Optional ConfigurableFileCollection getDependencies() - abstract @InputFiles ConfigurableFileCollection getSources() - abstract @OutputFile RegularFileProperty getOutput() - - abstract @Input @Optional Property getBatch() - abstract @Input @Optional Property getMixins() - abstract @Input @Optional Property getMixinsFatal() - - private final Property sourceCompatiblityProp = this.objects.property(JavaVersion) - - @Input @Optional Property getSourceCompatibility() { - this.sourceCompatiblityProp - } - - void setSourceCompatibility(JavaLanguageVersion javaVersion) { - this.sourceCompatibility.set(JavaVersion.toVersion(javaVersion)) - } - - void setSourceCompatibility(Provider javaVersion) { - this.sourceCompatibility.set(javaVersion.map(JavaVersion.&toVersion)) - } - - @Inject - ExtractRangeMap() { - this.output.convention(this.getDefaultOutputFile('txt')) - - this.sourceCompatibility.convention( - this.project.extensions.findByType(JavaPluginExtension).toolchain.languageVersion.map(JavaVersion.&toVersion) - ) - } - - @Override - protected void addArguments() { - this.args('--extract') - - this.args('--lib', this.dependencies) - this.args('--in', this.sources) - this.args('--out', this.output) - - this.args('--batch', this.batch) - this.args('--mixins', this.mixins) - this.args('--fatalmixins', this.mixinsFatal) - - this.args('--source-compatibility', this.sourceCompatibility.map(this.&parseSourceCompatibility)) - - super.addArguments() - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/S2SExec.groovy b/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/S2SExec.groovy deleted file mode 100644 index 35a6656..0000000 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/srg2source/S2SExec.groovy +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.forgedev.tasks.srg2source - -import groovy.transform.CompileStatic -import groovy.transform.PackageScope -import net.minecraftforge.forgedev.Tools -import net.minecraftforge.forgedev.Util -import net.minecraftforge.forgedev.tasks.ToolExec -import org.gradle.api.JavaVersion -import org.gradle.api.logging.LogLevel -import org.gradle.api.problems.Problems -import org.gradle.process.ExecResult - -import javax.inject.Inject - -@CompileStatic -@PackageScope abstract class S2SExec extends ToolExec { - // NOTE: check net.minecraftforge.srg2source.api.SourceVersion for compatible ranges - private static final int SRC_COMPAT_MIN = 6 - private static final String SRC_COMPAT_MIN_STR = '1.6' - private static final int SRC_COMPAT_MAX = 23 - private static final String SRC_COMPAT_MAX_STR = '23' - - @Inject - S2SExec() { - super(Tools.SRG2SRC) - - this.preferToolchainJvm.set(true) - - this.standardOutputLogLevel.set(LogLevel.INFO) - } - - @Override - protected ExecResult exec() { - super.exec().rethrowFailure().assertNormalExitValue() - } - - protected final String parseSourceCompatibility(JavaVersion javaVersion) { - int version = javaVersion.ordinal() + 1 - - if (version < SRC_COMPAT_MIN) { - this.logger.warn('WARNING: {} source compatibility {} is lower than minimum of {}', this.identityPath, version, SRC_COMPAT_MIN_STR) - return SRC_COMPAT_MIN_STR - } else if (version.toInteger() > SRC_COMPAT_MAX) { - this.logger.warn('WARNING: {} source compatibility {} is higher than the maximum of {}', this.identityPath, version, SRC_COMPAT_MAX_STR) - return SRC_COMPAT_MAX_STR - } else { - return version - } - } -} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserDev.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserDev.java new file mode 100644 index 0000000..cc63b24 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserDev.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.tasks.userdev; + +import net.minecraftforge.forgedev.ForgeDevExtension; +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.base.MCPBase; +import net.minecraftforge.forgedev.base.PatcherBase; +import net.minecraftforge.forgedev.tasks.SingleFileOutput; +import net.minecraftforge.forgedev.tasks.patching.binary.CreateBinPatches; +import net.minecraftforge.forgedev.tasks.patching.diff.GeneratePatches; +import org.gradle.api.Action; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.Jar; +import org.gradle.language.base.plugins.LifecycleBasePlugin; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; + +public abstract class UserDev { + private final String name; + private final Project project; + private final ForgeDevExtension extension; + private final TaskProvider jar; + + private PatcherBase patcherBase; + + private @Nullable TaskProvider config; + private @Nullable TaskProvider binaryPatches; + + @Inject + public UserDev(String name, Project project, ForgeDevExtension extension) { + this.name = name; + this.project = project; + this.extension = extension; + this.jar = project.getTasks().register(this.name + "Jar", Jar.class); + this.jar.configure(task -> { + task.setGroup(LifecycleBasePlugin.BUILD_GROUP); + task.getArchiveClassifier().set("userdev"); + }); + project.getTasks().named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME, task -> task.dependsOn(jar)); + } + + /*UserDev: + * config.json + * joined.lzma + * sources.jar + * patches/ + * net/minecraft/item/Item.java.patch + * ats/ + * at1.cfg + * at2.cfg + */ + public TaskProvider getJar() { + return jar; + } + public TaskProvider jar(Action action) { + getJar().configure(action); + return getJar(); + } + public TaskProvider getConfig() { + if (config == null) { + this.config = this.project.getTasks().register(this.name + "Config", UserdevConfig.class); + jar(task -> task.from(this.config.flatMap(UserdevConfig::getOutput), e -> e.rename(f -> "config.json"))); + } + return this.config; + } + public TaskProvider config(Action action) { + getConfig().configure(action); + return getConfig(); + } + public TaskProvider getBinaryPatches() { + if (this.binaryPatches == null) { + var tool = this.extension.getPlugin().getTool(Tools.BINPATCH); + + getConfig().configure(task -> { + task.getBinpatcherVersion().convention(tool.getModule().toString()); + task.getBinpatcherArguments().addAll("--clean", "{clean}", "--output", "{output}", "--apply", "{patch}"); + }); + + var genBinPatches = this.project.getTasks().register(this.name + "BinaryPatches", CreateBinPatches.class); + if (this.patcherBase != null) + genBinPatches.configure(this::configureBinaryPatches); + + getJar().configure(task -> { + task.from(genBinPatches.flatMap(CreateBinPatches::getOutput), e -> e.rename(f -> "joined.lzma")); + }); + + this.binaryPatches = genBinPatches; + } + return this.binaryPatches; + } + public TaskProvider binaryPatches(Action action) { + getBinaryPatches().configure(action); + return getBinaryPatches(); + } + + public void base(PatcherBase base) { + getJar().configure(task -> { + task.from(base.getAccessTransformers(), cfg -> cfg.into("ats/")); + task.from(base.getSideAnnotationStrippers(), cfg -> cfg.into("sas/")); + }); + getConfig().configure(task -> { + task.getATs().from(base.getAccessTransformers()); + task.getSASs().from(base.getSideAnnotationStrippers()); + }); + if (base instanceof MCPBase mcp) { + getConfig().configure(task -> task.getMCPConfig().set(mcp.getMcpArtifact())); + if (this.binaryPatches != null) + this.binaryPatches.configure(this::configureBinaryPatches); + } + this.patcherBase = base; + } + + private void configureBinaryPatches(CreateBinPatches task) { + if (this.patcherBase instanceof MCPBase mcp) { + task.getClean().setFrom(mcp.getClasses()); + task.getSrg().setFrom(mcp.getMap2Srg()); + task.getReverseSrg().set(true); + task.getSas().setFrom(mcp.getSideAnnotationStrippers()); + } + } + + public void patches(TaskProvider make) { + make.configure(task -> task.getAutoHeader().set(false)); + config(task -> { + task.getPatchesOriginalPrefix().set(make.flatMap(GeneratePatches::getBasePathPrefix)); + task.getPatchesModifiedPrefix().set(make.flatMap(GeneratePatches::getModifiedPathPrefix)); + }); + jar(task -> { + task.from(project.zipTree(make.flatMap(SingleFileOutput::getOutput)), e -> e.into("patches/")); + }); + } + + // TODO: [ForgeDev][UserDev] CLIENT EXTRA? + /* TODO: [ForgeDev][UserDev] Extra SRG Mappings + if (!legacyPatcher.getExtraMappings().isEmpty()) { + for (var extraMapping : legacyPatcher.getExtraMappings()) { + if (extraMapping instanceof File e) { + userdevJar.configure(t -> t.from(e, c -> c.into("srgs/"))); + userdevConfig.configure(t -> t.getSRGs().from(e)); + } else if (extraMapping instanceof String e) { + userdevConfig.configure(t -> t.getSRGLines().add(e)); + } + } + } + */ +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/tasks/generation/GeneratePatcherConfigV2.java b/src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserdevConfig.java similarity index 90% rename from src/main/groovy/net/minecraftforge/forgedev/tasks/generation/GeneratePatcherConfigV2.java rename to src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserdevConfig.java index cde9b2b..973a5e0 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/tasks/generation/GeneratePatcherConfigV2.java +++ b/src/main/groovy/net/minecraftforge/forgedev/tasks/userdev/UserdevConfig.java @@ -2,10 +2,11 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev.tasks.generation; +package net.minecraftforge.forgedev.tasks.userdev; import net.minecraftforge.forgedev.ForgeDevTask; import net.minecraftforge.forgedev.Util; +import net.minecraftforge.forgedev.values.MavenArtifact; import net.minecraftforge.gradleutils.shared.Closures; import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.data.json.PatcherConfig; @@ -15,7 +16,9 @@ import org.gradle.api.DefaultTask; import org.gradle.api.Named; import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.ProjectDependency; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.model.ObjectFactory; @@ -29,6 +32,8 @@ import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; import javax.inject.Inject; import java.io.File; @@ -40,7 +45,7 @@ import java.util.Arrays; import java.util.HashMap; -public abstract class GeneratePatcherConfigV2 extends DefaultTask implements ForgeDevTask { +public abstract class UserdevConfig extends DefaultTask implements ForgeDevTask { private static final String DEFAULT_PATCHES_PREFIX_ORIGINAL = "a/"; private static final String DEFAULT_PATCHES_PREFIX_MODIFIED = "b/"; @@ -78,7 +83,7 @@ public abstract class GeneratePatcherConfigV2 extends DefaultTask implements For protected abstract @Inject ObjectFactory getObjects(); @Inject - public GeneratePatcherConfigV2() { + public UserdevConfig() { this.getOutput().convention(this.getDefaultOutputFile("json")); this.getPatchesOriginalPrefix().convention(DEFAULT_PATCHES_PREFIX_ORIGINAL); @@ -143,6 +148,13 @@ private boolean isV2() { || !"b/".equals(getPatchesModifiedPrefix().getOrNull()); } + public void universal(TaskProvider task) { + this.getUniversal().set(MavenArtifact.from(task).map(MavenArtifact::getFullDescriptor)); + } + + public void sources(TaskProvider task) { + this.getSource().set(MavenArtifact.from(task).map(MavenArtifact::getFullDescriptor)); + } private static String depToString(Dependency value) { String group = value.getGroup(); @@ -156,6 +168,15 @@ private static String depToString(Dependency value) { return group + ':' + value.getName() + ':' + version; } + public final void addLibraries(Configuration config) { + for (var dep : config.getAllDependencies()) { + if (dep instanceof ProjectDependency project) + getLibraries().add(project.getGroup() + ':' + project.getName() + ':' + project.getVersion()); + else + getLibraries().add(dep.toString()); + } + } + public final void addCompileDependency(Dependency value) { this.getExtraCompileDeps().add(depToString(value)); } diff --git a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/CIRuntime.java b/src/main/groovy/net/minecraftforge/forgedev/values/CIRuntime.java similarity index 90% rename from src/main/groovy/net/minecraftforge/forgedev/legacy/values/CIRuntime.java rename to src/main/groovy/net/minecraftforge/forgedev/values/CIRuntime.java index f5abeae..d6d4c59 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/legacy/values/CIRuntime.java +++ b/src/main/groovy/net/minecraftforge/forgedev/values/CIRuntime.java @@ -2,7 +2,7 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev.legacy.values; +package net.minecraftforge.forgedev.values; import org.gradle.api.provider.ValueSource; import org.gradle.api.provider.ValueSourceParameters; diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/GitVersionValueSource.java b/src/main/groovy/net/minecraftforge/forgedev/values/GitVersionValueSource.java new file mode 100644 index 0000000..d876a4c --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/GitVersionValueSource.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.forgedev.Util; +import net.minecraftforge.gradleutils.shared.EnhancedPlugin; +import org.gradle.api.Action; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.api.provider.ValueSource; +import org.gradle.api.provider.ValueSourceParameters; +import org.gradle.jvm.toolchain.JavaLauncher; +import org.gradle.process.ExecOperations; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +public abstract class GitVersionValueSource implements ValueSource { + public static Provider provider(ProviderFactory providers, EnhancedPlugin plugin, Action config) { + return providers.of(GitVersionValueSource.class, spec -> { + spec.parameters(params -> { + var tool = plugin.getTool(Tools.GITVERSION); + params.getClasspath().setFrom(tool.getClasspath()); + params.getJavaLauncher().set(tool.getJavaLauncher().map(JavaLauncher::getExecutablePath)); + params.getRootDirectory().set(plugin.rootProjectDirectory()); + params.getProjectDirectory().set(plugin.workingProjectDirectory()); + params.getCommit().set("HEAD"); + config.execute(params); + }); + }); + } + + public interface Parameters extends ValueSourceParameters { + ConfigurableFileCollection getClasspath(); + RegularFileProperty getJavaLauncher(); + DirectoryProperty getRootDirectory(); + DirectoryProperty getProjectDirectory(); + Property getCommit(); + } + private final ExecOperations execOps; + private static final Logger LOGGER = Logging.getLogger(GitVersionValueSource.class); + + @Inject + public GitVersionValueSource(ExecOperations execOps) { + this.execOps = execOps; + } + + @Override + @Nullable + public String obtain() { + var output = new ByteArrayOutputStream(); + this.execOps.javaexec(spec -> { + var params = this.getParameters(); + spec.setClasspath(params.getClasspath()); + spec.setExecutable(params.getJavaLauncher().get()); + spec.args("--root-dir", params.getRootDirectory().get().getAsFile().getAbsolutePath()); + spec.args("--project-dir", params.getProjectDirectory().get().getAsFile().getAbsolutePath()); + spec.args("--commit", params.getCommit().get()); + + LOGGER.info("Executing GitVersion: "); + Util.logFiles(LOGGER, " Classpath", params.getClasspath().getFiles()); + LOGGER.info(" Java: {}", params.getJavaLauncher().get().getAsFile().getAbsolutePath()); + Util.logArgs(LOGGER, " Arguments", spec.getArgs()); + spec.setStandardOutput(output); + }).rethrowFailure().assertNormalExitValue(); // If something fails, this should throw an exception + + var lines = output.toString(StandardCharsets.UTF_8).split(System.lineSeparator()); + // We don't want the new line characters, just the first line. + return lines[0]; + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/LatestForgeVersion.java b/src/main/groovy/net/minecraftforge/forgedev/values/LatestForgeVersion.java new file mode 100644 index 0000000..13cfabe --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/LatestForgeVersion.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import net.minecraftforge.util.download.DownloadUtils; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.ValueSource; +import org.gradle.api.provider.ValueSourceParameters; +import org.jspecify.annotations.Nullable; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Map; + +public abstract class LatestForgeVersion implements ValueSource { + private static final Logger LOGGER = Logging.getLogger(LatestForgeVersion.class); + + private static final String PROMOTIONS_SLIM = "https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"; + private static final Gson GSON = new Gson(); + + public interface Parameters extends ValueSourceParameters { + Property getOffline(); + + RegularFileProperty getCacheFile(); + + Property getMinecraftVersion(); + } + + @Override + @Nullable + public String obtain() { + try { + var file = getParameters().getCacheFile().getAsFile().get(); + + // Always attempt to re-download promotions UNLESS we are offline + if (getParameters().getOffline().getOrElse(false)) { + if (!file.exists()) + throw new IllegalStateException("Cannot download Forge promotions while offline! Please build the project at least once while online."); + } else { + DownloadUtils.downloadFile(file, PROMOTIONS_SLIM); + } + + var mcVersion = getParameters().getMinecraftVersion().get(); + var json = GSON.fromJson(Files.readString(file.toPath(), StandardCharsets.UTF_8), new TypeToken>(){}); + return json.get(mcVersion + "-latest"); + } catch (Exception e) { + LOGGER.error("ERROR: Failed to get latest Forge version. Checks using this data will be skipped.", e); + return null; + } + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/MCPData.java b/src/main/groovy/net/minecraftforge/forgedev/values/MCPData.java new file mode 100644 index 0000000..2fc1b51 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/MCPData.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import net.minecraftforge.forgedev.ForgeDevPlugin; +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A wrapper around Mavenzier's --mcp task. + * It provides access to the decompild code, as well as any mappings data as needed. + */ +public abstract class MCPData extends MavenizerData { + private final String name; + + public abstract Property<@NotNull String> getMcpVersion(); + public abstract Property<@NotNull String> getMcpPipeline(); + public abstract Property<@NotNull String> getMappingChannelInput(); + public abstract Property<@NotNull String> getMappingVersionInput(); + public abstract ConfigurableFileCollection getAccessTransformers(); + public abstract ConfigurableFileCollection getSideAnnotationStrippers(); + + @Inject + public MCPData(final Project project, final ForgeDevPlugin plugin, String name) { + super(project, plugin); + this.name = name; + } + + protected String getTaskName() { + return this.name + "/mcp-data-" + this.getMcpVersion().get() + '-' + this.getMcpPipeline().get(); + } + + @Override + protected @Nullable File getOutputDir() { + return this.plugin.localCaches().dir(getTaskName()).get().getAsFile(); + } + @Override + public List getArgs() { + var ret = new ArrayList<>(List.of( + "--mcp", + "--pipeline", this.getMcpPipeline().get() + )); + + // Version or fully qualified artifact + var version = this.getMcpVersion().get(); + ret.add(version.indexOf(':') != -1 ? "--artifact" : "--version"); + ret.add(version); + + if (this.getMappingChannelInput().isPresent()) { + ret.add("--mappings"); + if (this.getMappingVersionInput().isPresent()) + ret.add(this.getMappingChannelInput().get() + ':' + this.getMappingVersionInput().get()); + else + ret.add(this.getMappingChannelInput().get()); + } + + for (var file : this.getAccessTransformers()) { + ret.add("--at"); + ret.add(file.getAbsolutePath()); + } + + for (var file : this.getSideAnnotationStrippers()) { + ret.add("--sas"); + ret.add(file.getAbsolutePath()); + } + return ret; + } + + public Provider getConfig() { + return get("config"); + } + public Provider getPipeline() { + return get("pipeline"); + } + public Provider getOutput() { + return getFile("output"); + } + public Provider getExtra() { + return getFile("extra"); + } + + public Provider getMappingChannel() { + return get("mappings.channel"); + } + public Provider getMappingVersion() { + return get("mappings.version"); + } + public Provider getMappingZip() { + return getFile("mappings.zip"); + } + public Provider getObf2Srg() { + return getFile("mappings.obf2srg"); + } + public Provider getMap2Obf() { + return getFile("mappings.map2obf"); + } + public Provider getMap2Srg() { + return getFile("mappings.map2srg"); + } + + public Provider getClasses() { + return getFile("classes.srg"); + } + public Provider getClassesRaw() { + return getFile("classes.raw"); + } + public Provider getSources() { + return getFile("sources.named"); + } + public Provider getSourcesSrg() { + return getFile("sources.srg"); + } + + public Provider> getDependencies() { + return get("dependencies").map(value -> value.isBlank() ? List.of() : List.of(value.split(","))); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/MavenArtifact.java b/src/main/groovy/net/minecraftforge/forgedev/values/MavenArtifact.java new file mode 100644 index 0000000..6edfca1 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/MavenArtifact.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import groovy.lang.MissingPropertyException; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.Serializable; +import java.io.File; +import java.util.Objects; + +public abstract class MavenArtifact implements Serializable { + private final String group; + private final String name; + private final @Nullable String version; + private final @Nullable String classifier; + private final String extension; + + protected abstract @Inject ObjectFactory getObjects(); + + @Inject + public MavenArtifact(String group, String name, @Nullable String version, @Nullable String classifier, String extension) { + this.group = group; + this.name = name; + this.version = version == null || version.isEmpty() ? null : version; + this.classifier = classifier == null || classifier.isEmpty() ? null : classifier; + this.extension = extension; + } + + public static MavenArtifact from(ObjectFactory objects, String descriptor) { + var pts = descriptor.split(":"); + if (pts.length < 2) + throw new IllegalArgumentException("Descriptor is missing group and name: " + descriptor); + + String extension = ""; + int last = pts.length - 1; + int idx = pts[last].indexOf('@'); + if (idx != -1) { + extension = pts[last].substring(idx + 1); + pts[last] = pts[last].substring(0, idx); + } + + var version = pts.length > 2 ? pts[2] : ""; + var classifier = pts.length > 3 ? pts[3] : ""; + + return objects.newInstance(MavenArtifact.class, pts[0], pts[1], version, classifier, extension); + } + + private static String nonull(@Nullable String str) { + return str == null ? "" : str; + } + + public static MavenArtifact from(Project project) { + return project.getObjects().newInstance(MavenArtifact.class, project.getGroup().toString(), project.getName(), project.getVersion().toString(), "", "jar"); + } + + public static MavenArtifact from(AbstractArchiveTask task) { + var project = task.getProject(); + return project.getObjects().newInstance(MavenArtifact.class, project.getGroup().toString(), project.getName(), project.getVersion().toString(), task.getArchiveClassifier().getOrElse(""), task.getArchiveExtension().get()); + } + + @SuppressWarnings("unchecked") + private static @Nullable R getProperty(Object object, String property) { + try { + return (R) InvokerHelper.getProperty(object, property); + } catch (MissingPropertyException e) { + return null; + } + } + private static ObjectFactory getObjects(Object value) { + if (value instanceof ObjectFactory factory) + return factory; + return getProperty(value, "objectFactory"); + } + public static MavenArtifact from(Dependency dependency) { + return from(getObjects(dependency), dependency); + } + public static MavenArtifact from(ObjectFactory objects, Dependency dependency) { + String version = dependency.getVersion() == null ? "" : dependency.getVersion(); + String classifier = getProperty(dependency, "classifier"); + if (classifier == null) + classifier = ""; + String extension = getProperty(dependency, "artifactType"); + if (extension == null) + extension = getProperty(dependency, "extension"); + if (extension == null) + extension = "jar"; + return objects.newInstance(MavenArtifact.class, Objects.requireNonNull(dependency.getGroup()), dependency.getName(), version, classifier, extension); + } + + public static Provider from(TaskProvider provider) { + return provider.map(MavenArtifact::from); + } + + public static Provider from(Provider provider) { + return provider.map(value -> { + if (value instanceof MavenArtifact object) + return object; + if (value instanceof Project project) + return from(project); + if (value instanceof AbstractArchiveTask task) + return from(task); + if (value instanceof Dependency dependency) + return from(dependency); + throw new IllegalArgumentException("Cannot create MavenArtifact from " + provider + ", must provide ObjectFactory"); + }); + } + + public static Provider from(ObjectFactory objects, Provider provider) { + return provider.map(value -> { + if (value instanceof MavenArtifact object) + return object; + if (value instanceof Project project) + return from(project); + if (value instanceof AbstractArchiveTask task) + return from(task); + if (value instanceof Dependency dependency) + return from(objects, dependency); + if (value instanceof String string) + return from(objects, string); + throw new IllegalArgumentException("Cannot create MavenArtifact from " + provider + ", Unknown type"); + }); + } + + public static MavenArtifact from(ObjectFactory objects, File base, File file) { + + // Parse the file path as a maven coordinate: {group}/{name}/{version}/{name}-{version}[-{classifier}.{ext} + var basePath = base.getAbsolutePath(); + if (!basePath.endsWith(File.separator)) + basePath += File.separator; + if (!file.getAbsolutePath().startsWith(basePath)) + throw new IllegalArgumentException("Cannot get maven info for " + file.getAbsolutePath() + " it is not a subfile of " + base.getAbsolutePath()); + + var filename = file.getName(); + var versionDir = file.getParentFile(); + var version = versionDir.getName(); + var nameDir = versionDir.getParentFile(); + var name = nameDir.getName(); + + var groupDir = nameDir.getParentFile(); + var group = groupDir.getAbsolutePath().substring(basePath.length()).replace('\\', '/').replace('/', '.'); + + var classifier = ""; + var idx = name.length() + 1 + version.length(); + if (idx > filename.length()) + throw new IllegalArgumentException("Could not determine maven coordinates from: " + file.getAbsoluteFile()); + + var chr = filename.charAt(idx); + if (chr == '-') { + // Find the next . which should be the extension. + // This does mean that clssifiers cant have . in them. But fuck it + var dot = filename.indexOf('.', idx + 1); + classifier = filename.substring(idx + 1, dot); + idx = dot; + chr = filename.charAt(idx); + } + + if (chr != '.') + throw new IllegalArgumentException("Could not determine maven coordinates from: " + file.getAbsoluteFile()); + var extension = filename.substring(idx + 1); + + return objects.newInstance(MavenArtifact.class, group, name, version, classifier, extension); + } + + public String group() { + return this.group; + } + + public MavenArtifact withGroup(String group) { + return getObjects().newInstance(MavenArtifact.class, group, name, nonull(version), nonull(classifier), extension); + } + + public String name() { + return this.name; + } + + public MavenArtifact withName(String name) { + return getObjects().newInstance(MavenArtifact.class, group, name, nonull(version), nonull(classifier), extension); + } + + public @Nullable String version() { + return this.version; + } + + public MavenArtifact withVersion(@Nullable String version) { + return getObjects().newInstance(MavenArtifact.class, group, name, nonull(version), nonull(classifier), extension); + } + + public @Nullable String classifier() { + return this.classifier; + } + + public MavenArtifact withClassifier(@Nullable String classifier) { + return getObjects().newInstance(MavenArtifact.class, group, name, nonull(version), nonull(classifier), extension); + } + + public String extension() { + return this.extension; + } + + public MavenArtifact withExtension(String extension) { + return getObjects().newInstance(MavenArtifact.class, group, name, nonull(version), nonull(classifier), extension); + } + + public String getDescriptor() { + return getDescriptor(false); + } + public String getFullDescriptor() { + return getDescriptor(true); + } + private String getDescriptor(boolean full) { + var ret = new StringBuilder(); + ret.append(this.group).append(':').append(this.name); + if (this.version != null) { + ret.append(':').append(this.version); + if (this.classifier != null) + ret.append(':').append(this.classifier); + } + if (full || !"jar".equals(this.extension)) + ret.append('@').append(this.extension); + return ret.toString(); + } + + public String getDirectory() { + var ret = new StringBuilder(); + ret.append(this.group.replace('.', '/')).append('/').append(this.name).append('/'); + if (this.version != null) + ret.append(this.version).append('/'); + return ret.toString(); + } + + public String getFileName() { + var ret = new StringBuilder(); + ret.append(this.name); + if (this.version != null) { + ret.append('-').append(this.version); + if (this.classifier != null) + ret.append('-').append(this.classifier); + } + ret.append('.').append(this.extension); + return ret.toString(); + } + + public String getPath() { + return getDirectory() + getFileName(); + } + + @Override + public int hashCode() { + return getDescriptor().hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MavenArtifact o && this.getDescriptor().equals(o.getDescriptor()); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/MavenizerData.java b/src/main/groovy/net/minecraftforge/forgedev/values/MavenizerData.java new file mode 100644 index 0000000..32feb2a --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/MavenizerData.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import com.google.gson.reflect.TypeToken; +import net.minecraftforge.forgedev.ForgeDevPlugin; +import net.minecraftforge.forgedev.Tools; +import net.minecraftforge.util.data.json.JsonData; +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Provider; +import org.gradle.api.provider.ProviderFactory; +import org.gradle.internal.impldep.com.google.common.io.Files; +import org.gradle.jvm.toolchain.JavaLauncher; +import org.jspecify.annotations.Nullable; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * A wrapper around Mavenzier's tasks that output a json map + * This is intended to be extended by classes that expose providers for the returned data. + */ +public abstract class MavenizerData { + protected static final Logger LOGGER = Logging.getLogger(MavenizerData.class); + protected final Project project; + protected final ForgeDevPlugin plugin; + private final MapProperty data; + + protected abstract @Inject ObjectFactory getObjects(); + protected abstract @Inject ProviderFactory getProviders(); + + @Inject + public MavenizerData(final Project project, final ForgeDevPlugin plugin) { + this.project = project; + this.plugin = plugin; + + this.data = getObjects().mapProperty(String.class, String.class) + .convention(getProviders().provider(this::mavenizer)); + this.data.finalizeValueOnRead(); + } + + protected abstract String getTaskName(); + protected abstract List getArgs(); + protected @Nullable File getOutputDir() { + return null; + } + + protected Provider get(String key) { + return this.data.getting(key).orElse(getProviders().provider(() -> { + throw new IllegalStateException("Mavenizer did not output expected json data " + key); + })); + } + + protected Provider get(String key, @Nullable String _default, String requiredVersion) { + return this.data.getting(key).orElse(getProviders().provider(() -> { + // This should only happen when someone hardcodes their tool version, warn them + var message = "Mavenizer did not output expected json data " + key +", Make sure you're using Mavenizer >= " + requiredVersion; + if (_default != null) { + LOGGER.warn(message); + return _default; + } + throw new IllegalStateException("Mavenizer did not output expected json data " + key +", Make sure you're using Mavenizer >= " + requiredVersion); + })); + } + + protected Provider getFile(String key) { + return get(key).map(this::file); + } + private File file(String path) { + return getOutputDir() == null ? project.file(path) : new File(getOutputDir(), path); + } + + protected List loadList(File source) { + var ret = new ArrayList(); + try { + for (var line : Files.readLines(source, StandardCharsets.UTF_8)) + ret.add(file(line)); + } catch (IOException e) { + throw new RuntimeException("Could not read file: " + source, e); + } + return ret; + } + + private Map mavenizer() { + var outputJson = plugin.localCaches().file(getTaskName() + ".json").get().getAsFile(); + return getProviders().of(MavenizerValueSource.class, spec -> { + spec.parameters(params -> { + var tool = plugin.getTool(Tools.MAVENIZER); + params.getClasspath().setFrom(tool.getClasspath()); + params.getJavaLauncher().set(tool.getJavaLauncher().map(JavaLauncher::getExecutablePath)); + + var toolCache = plugin.globalCaches() + .dir(tool.getName().toLowerCase(Locale.ENGLISH)) + .map(plugin.getExtension().getProblems().ensureFileLocation()); + var cache = toolCache.get().dir("caches").getAsFile(); + + params.getArguments().set(getProviders().provider(() -> { + var ret = new ArrayList<>(List.of( + "--cache", cache.getAbsolutePath(), + "--output", outputJson.getAbsolutePath() + )); + if (getOutputDir() != null) { + ret.add("--output-dir"); + ret.add(getOutputDir().getAbsolutePath()); + } + ret.addAll(getArgs()); + return ret; + })); + }); + }) + .map(v -> JsonData.fromJson(outputJson, new TypeToken>(){})).get(); + } +} diff --git a/src/main/groovy/net/minecraftforge/forgedev/MavenizerValueSource.java b/src/main/groovy/net/minecraftforge/forgedev/values/MavenizerValueSource.java similarity index 59% rename from src/main/groovy/net/minecraftforge/forgedev/MavenizerValueSource.java rename to src/main/groovy/net/minecraftforge/forgedev/values/MavenizerValueSource.java index a8f9d7f..c35817a 100644 --- a/src/main/groovy/net/minecraftforge/forgedev/MavenizerValueSource.java +++ b/src/main/groovy/net/minecraftforge/forgedev/values/MavenizerValueSource.java @@ -2,8 +2,9 @@ * Copyright (c) Forge Development LLC and contributors * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.forgedev; +package net.minecraftforge.forgedev.values; +import net.minecraftforge.forgedev.Util; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.logging.Logger; @@ -16,8 +17,8 @@ import javax.inject.Inject; -abstract class MavenizerValueSource implements ValueSource { - interface Parameters extends ValueSourceParameters { +public abstract class MavenizerValueSource implements ValueSource { + public interface Parameters extends ValueSourceParameters { ConfigurableFileCollection getClasspath(); RegularFileProperty getJavaLauncher(); ListProperty getArguments(); @@ -41,25 +42,9 @@ public Boolean obtain() { spec.setArgs(params.getArguments().get()); LOGGER.info("Executing Mavenizer: "); - var itr = params.getClasspath().iterator(); - LOGGER.info(" Classpath: {}", itr.next().getAbsolutePath()); - while (itr.hasNext()) - LOGGER.info(" {}", itr.next().getAbsolutePath()); - + Util.logFiles(LOGGER, " Classpath", params.getClasspath().getFiles()); LOGGER.info(" Java: {}", params.getJavaLauncher().get().getAsFile().getAbsolutePath()); - var args = params.getArguments().get(); - var prefix = " Arguments: "; - for (int x = 0; x < args.size(); x++) { - var current = args.get(x); - var next = args.size() > x + 1 ? args.get(x + 1) : null; - var line = current; - if (current.startsWith("--") && next != null && !next.startsWith("--")) { - x++; - line += ' ' + next; - } - LOGGER.info("{}{}", prefix, line); - prefix = " "; - } + Util.logArgs(LOGGER, " Arguments", spec.getArgs()); }).rethrowFailure().assertNormalExitValue(); return false; } diff --git a/src/main/groovy/net/minecraftforge/forgedev/values/MinecraftFiles.java b/src/main/groovy/net/minecraftforge/forgedev/values/MinecraftFiles.java new file mode 100644 index 0000000..733e062 --- /dev/null +++ b/src/main/groovy/net/minecraftforge/forgedev/values/MinecraftFiles.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.forgedev.values; + +import net.minecraftforge.forgedev.ForgeDevPlugin; +import org.gradle.api.Project; +import org.gradle.api.provider.Provider; + +import javax.inject.Inject; +import java.io.File; +import java.util.List; + +/** + * A wrapper around Mavenzier's --minecraft-files task. + * This exposes a lot of vanilla files with no modifications. + * Basically, I had the tasks written in Mavenizer and don't want to duplicate them here. + */ +public abstract class MinecraftFiles extends MavenizerData { + private final String version; + + @Inject + public MinecraftFiles(final Project project, final ForgeDevPlugin plugin, String version) { + super(project, plugin); + this.version = version; + } + + @Override + protected String getTaskName() { + return "minecraft-" + version; + } + + @Override + public List getArgs() { + return List.of( + "--minecraft-files", + "--version", this.version + ); + } + + public Provider getId() { + return get("id"); + } + public Provider getVersion() { + return getFile("version"); + } + public Provider getClient() { + return getFile("client"); + } + public Provider getServer() { + return getFile("server"); + } + public Provider> getClientLibraries() { + return getFile("client.libraries").map(this::loadList); + } + public Provider> getServerLibraries() { + return getFile("server.libraries").map(this::loadList); + } + public Provider getServerExtracted() { + return getFile("server.extracted"); + } + // This file is optional, only available for 1.14->1.21.11, will throw an exception if requested outside that + public Provider getClientMappings() { + return getFile("client.mappings"); + } + // This file is optional, only available for 1.14->1.21.11, will throw an exception if requested outside that + public Provider getServerMappings() { + return getFile("server.mappings"); + } +}