From 5cc3605307d64bed04a7f0c4af4af8d660185acf Mon Sep 17 00:00:00 2001 From: Jonathing Date: Mon, 20 Oct 2025 08:21:26 -0400 Subject: [PATCH] 0.4 - Java 25, Complex OS Rules, Log Cache Misses, and Java Provisioner 2.0 (#6) --- build.gradle | 3 +- gradle/wrapper/gradle-wrapper.jar | Bin 43705 -> 45633 bytes gradlew | 7 +- gradlew.bat | 3 +- settings.gradle | 23 +++--- .../mcmaven/cli/MCPDataTask.java | 32 +++---- .../minecraftforge/mcmaven/cli/MCPTask.java | 66 ++++++++------- .../net/minecraftforge/mcmaven/cli/Main.java | 22 ++--- .../minecraftforge/mcmaven/cli/MavenTask.java | 16 ++-- .../{GlobalOptions.java => Mavenizer.java} | 11 ++- .../mcmaven/impl/MinecraftMaven.java | 22 ++--- .../mcmaven/impl/cache/JDKCache.java | 59 +++++++------ .../mcmaven/impl/cache/MavenCache.java | 21 +++-- .../impl/cache/MinecraftMavenCache.java | 3 +- .../mcmaven/impl/data/MCPSetupFiles.java | 20 ++--- .../mcmaven/impl/mappings/Mappings.java | 13 +-- .../mcmaven/impl/repo/Repo.java | 43 +++++++--- .../mcmaven/impl/repo/forge/ForgeRepo.java | 19 +++-- .../mcmaven/impl/repo/forge/InjectTask.java | 11 ++- .../mcmaven/impl/repo/forge/Patcher.java | 53 ++++++------ .../impl/repo/mcpconfig/MCPConfigRepo.java | 14 ++-- .../impl/repo/mcpconfig/MCPTaskFactory.java | 68 ++++++++------- .../impl/repo/mcpconfig/MinecraftTasks.java | 18 ++-- .../mcmaven/impl/tasks/MCPNames.java | 4 +- .../mcmaven/impl/tasks/RecompileTask.java | 13 +-- .../mcmaven/impl/tasks/RenameTask.java | 4 +- .../mcmaven/impl/util/Arch.java | 78 ------------------ .../mcmaven/impl/util/Artifact.java | 34 ++++++-- .../mcmaven/impl/util/GradleAttributes.java | 9 +- .../mcmaven/impl/util/ProcessUtils.java | 22 ++--- .../mcmaven/impl/util/Task.java | 12 +-- 31 files changed, 352 insertions(+), 371 deletions(-) rename src/main/java/net/minecraftforge/mcmaven/impl/{GlobalOptions.java => Mavenizer.java} (79%) delete mode 100644 src/main/java/net/minecraftforge/mcmaven/impl/util/Arch.java diff --git a/build.gradle b/build.gradle index b6026b2..53793a1 100644 --- a/build.gradle +++ b/build.gradle @@ -19,8 +19,7 @@ version = gitversion.tagOffset println "Version: $version" -// TODO [Mavenizer] Update to Java 25 once ForgeGradle and ForgeDev target Gradle 9.1.0 -java.toolchain.languageVersion = JavaLanguageVersion.of(21) +java.toolchain.languageVersion = JavaLanguageVersion.of(25) dependencies { compileOnly libs.nulls diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 9bbc975c742b298b441bfb90dbc124400a3751b9..f8e1ee3125fe0768e9a76ee977ac089eb657005e 100644 GIT binary patch delta 37435 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 delta 35540 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%0A!3{ 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 D0|+Ag diff --git a/gradlew b/gradlew index faf9300..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,8 +210,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a21..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle index 767da1c..f601175 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,17 +20,17 @@ gradle.beforeProject { Project project -> //formatter:off dependencyResolutionManagement.versionCatalogs.register('libs') { plugin 'licenser', 'net.minecraftforge.licenser' version '1.2.0' - plugin 'gradleutils', 'net.minecraftforge.gradleutils' version '3.3.18' - plugin 'gitversion', 'net.minecraftforge.gitversion' version '3.1.1' - plugin 'changelog', 'net.minecraftforge.changelog' version '3.1.2' + plugin 'gradleutils', 'net.minecraftforge.gradleutils' version '3.3.21' + plugin 'gitversion', 'net.minecraftforge.gitversion' version '3.1.6' + plugin 'changelog', 'net.minecraftforge.changelog' version '3.1.3' plugin 'shadow', 'com.gradleup.shadow' version '9.2.2' // Static Analysis - library 'nulls', 'org.jetbrains', 'annotations' version '26.0.2' + library 'nulls', 'org.jetbrains', 'annotations' version '26.0.2-1' library 'gson', 'com.google.code.gson', 'gson' version '2.10.1' library 'jopt', 'net.sf.jopt-simple', 'jopt-simple' version '6.0-alpha-3' - library 'jver', 'net.minecraftforge', 'java-provisioner' version '1.0.10' + library 'jver', 'net.minecraftforge', 'java-provisioner' version '2.0.0' library 'srgutils', 'net.minecraftforge', 'srgutils' version '0.5.14' library 'diff', 'io.codechicken', 'DiffPatch' version '2.0.0.36' // Fuzzy patching @@ -38,11 +38,12 @@ dependencyResolutionManagement.versionCatalogs.register('libs') { library 'commonsio', 'commons-io', 'commons-io' version '2.18.0' // Utilities - library 'utils-download', 'net.minecraftforge', 'download-utils' version '0.3.1' - library 'utils-files', 'net.minecraftforge', 'file-utils' version '0.3.1' - library 'utils-hash', 'net.minecraftforge', 'hash-utils' version '0.1.9' - library 'utils-data', 'net.minecraftforge', 'json-data-utils' version '0.2.3' - library 'utils-logging', 'net.minecraftforge', 'log-utils' version '0.3.1' - bundle 'utils', ['utils-download', 'utils-files', 'utils-hash', 'utils-data', 'utils-logging'] + library 'utils-download', 'net.minecraftforge', 'download-utils' version '0.4.0' + library 'utils-files', 'net.minecraftforge', 'file-utils' version '0.3.2' + library 'utils-hash', 'net.minecraftforge', 'hash-utils' version '0.1.12' + library 'utils-data', 'net.minecraftforge', 'json-data-utils' version '0.4.0' + library 'utils-logging', 'net.minecraftforge', 'log-utils' version '0.5.0' + library 'utils-os', 'net.minecraftforge', 'os-utils' version '0.1.0' + bundle 'utils', ['utils-download', 'utils-files', 'utils-hash', 'utils-data', 'utils-logging', 'utils-os'] } //formatter:on diff --git a/src/main/java/net/minecraftforge/mcmaven/cli/MCPDataTask.java b/src/main/java/net/minecraftforge/mcmaven/cli/MCPDataTask.java index 32c9a3c..c2859cf 100644 --- a/src/main/java/net/minecraftforge/mcmaven/cli/MCPDataTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/cli/MCPDataTask.java @@ -20,7 +20,9 @@ import net.minecraftforge.srgutils.IMappingFile.IField; import net.minecraftforge.srgutils.IMappingFile.IMethod; import net.minecraftforge.srgutils.IMappingFile.IParameter; -import net.minecraftforge.util.logging.Log; +import net.minecraftforge.util.logging.Logger; + +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; // TODO [Mavenizer][MCPDataTask] This is a copy of FG6's ExtractMCPData task. // its not the best, but I dont want to re-wrok INSTALLER_TOOLS to put the tsrg in the mappings zip @@ -28,14 +30,14 @@ public class MCPDataTask { public static void run(String[] args) throws Exception { int ret = runI(args); if (ret != 0) { - Log.release(); + LOGGER.release(); //System.exit(ret); } } private static int runI(String[] args) throws Exception { // TODO [MCMavenizer] Make this into a --log [level] option - Log.enabled = Log.Level.INFO; + LOGGER.setEnabled(Logger.Level.INFO); var parser = new OptionParser(); parser.allowsUnrecognizedOptions(); @@ -87,7 +89,7 @@ private static int runI(String[] args) throws Exception { var options = parser.parse(args); if (options.has(helpO)) { - parser.printHelpOn(Log.INFO); + parser.printHelpOn(LOGGER.getInfo()); return -1; } @@ -103,7 +105,7 @@ private static int runI(String[] args) throws Exception { null; if (artifact == null) { - Log.error("Missing mcp --version or --artifact"); + LOGGER.error("Missing mcp --version or --artifact"); return -2; } @@ -118,19 +120,19 @@ else if (options.has(parchmentO)) var key = options.valueOf(keyO); if (key == null) { - Log.error("Missing --key option"); + LOGGER.error("Missing --key option"); return -3; } var repo = new MCPConfigRepo(new Cache(cacheRoot, jdkCacheRoot)); - Log.info(" Output: " + output.getAbsolutePath()); - Log.info(" Cache: " + cacheRoot.getAbsolutePath()); - Log.info(" JDK Cache: " + jdkCacheRoot.getAbsolutePath()); - Log.info(" Artifact: " + artifact); - Log.info(" Key: " + key); + LOGGER.info(" Output: " + output.getAbsolutePath()); + LOGGER.info(" Cache: " + cacheRoot.getAbsolutePath()); + LOGGER.info(" JDK Cache: " + jdkCacheRoot.getAbsolutePath()); + LOGGER.info(" Artifact: " + artifact); + LOGGER.info(" Key: " + key); if (mappings != null) - Log.info(" Mappings: " + mappings); - Log.info(); + LOGGER.info(" Mappings: " + mappings); + LOGGER.info(); var mcp = repo.get(artifact); var side = mcp.getSide("joined"); @@ -141,14 +143,14 @@ else if (options.has(parchmentO)) path = "config/static_methods.txt"; if (path == null) { - Log.error("Could not find data entry for '%s'".formatted(key)); + LOGGER.error("Could not find data entry for '%s'".formatted(key)); return -4; } try (ZipFile zip = new ZipFile(mcp.getData())) { var entry = zip.getEntry(path); if (entry == null) { - Log.error("Invalid config zip, missing file: " + path); + LOGGER.error("Invalid config zip, missing file: " + path); return -5; } diff --git a/src/main/java/net/minecraftforge/mcmaven/cli/MCPTask.java b/src/main/java/net/minecraftforge/mcmaven/cli/MCPTask.java index 6051101..a033a3e 100644 --- a/src/main/java/net/minecraftforge/mcmaven/cli/MCPTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/cli/MCPTask.java @@ -24,14 +24,16 @@ import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.hash.HashFunction; import net.minecraftforge.util.hash.HashStore; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; + +import net.minecraftforge.util.logging.Logger; import org.jetbrains.annotations.Nullable; // TODO [Mavenizer][MCPTask] Cleanup. Works well but is a mess. public class MCPTask { public static void run(String[] args) throws Exception { // TODO [MCMavenizer] Make this into a --log [level] option - Log.enabled = Log.Level.INFO; + LOGGER.setEnabled(Logger.Level.INFO); var parser = new OptionParser(); parser.allowsUnrecognizedOptions(); @@ -100,8 +102,8 @@ public static void run(String[] args) throws Exception { var options = parser.parse(args); if (options.has(helpO)) { - parser.printHelpOn(Log.INFO); - Log.release(); + parser.printHelpOn(LOGGER.getInfo()); + LOGGER.release(); return; } @@ -122,24 +124,24 @@ public static void run(String[] args) throws Exception { var sas = options.has(sasO) ? options.valueOf(sasO) : null; if (artifact == null) { - Log.error("Missing mcp --version or --artifact"); - Log.release(); + LOGGER.error("Missing mcp --version or --artifact"); + LOGGER.release(); return; } var repo = new MCPConfigRepo(new Cache(cacheRoot, jdkCacheRoot)); - Log.info(" Output: " + output.getAbsolutePath()); - Log.info(" Cache: " + cacheRoot.getAbsolutePath()); - Log.info(" JDK Cache: " + jdkCacheRoot.getAbsolutePath()); - Log.info(" Artifact: " + artifact); - Log.info(" Pipeline: " + pipeline); + LOGGER.info(" Output: " + output.getAbsolutePath()); + LOGGER.info(" Cache: " + cacheRoot.getAbsolutePath()); + LOGGER.info(" JDK Cache: " + jdkCacheRoot.getAbsolutePath()); + LOGGER.info(" Artifact: " + artifact); + LOGGER.info(" Pipeline: " + pipeline); if (options.has(rawO)) { - Log.info(" Raw Names: " + (options.has(seargeO) ? "Searge" : "Notch")); + LOGGER.info(" Raw Names: " + (options.has(seargeO) ? "Searge" : "Notch")); } else { - Log.info(" Access: " + (ats == null ? null : ats.getAbsolutePath())); - Log.info(" SAS: " + (sas == null ? null : sas.getAbsolutePath())); + LOGGER.info(" Access: " + (ats == null ? null : ats.getAbsolutePath())); + LOGGER.info(" SAS: " + (sas == null ? null : sas.getAbsolutePath())); } - Log.info(); + LOGGER.info(); var mcp = repo.get(artifact); var side = mcp.getSide(pipeline); @@ -152,13 +154,13 @@ public static void run(String[] args) throws Exception { Task rawTask = searge ? side.getTasks().getSrgJar() : side.getTasks().getRawJar(); File raw; - Log.info("Creating Raw Jar"); - var indent = Log.push(); + LOGGER.info("Creating Raw Jar"); + var indent = LOGGER.push(); try { raw = rawTask.execute(); cache.add("raw", raw); } finally { - Log.pop(indent); + LOGGER.pop(indent); } if (!output.exists() || !cache.isSame()) { @@ -198,12 +200,12 @@ public static void run(String[] args) throws Exception { File sources = null; { - Log.info("Creating MCP Source Jar"); - var indent = Log.push(); + LOGGER.info("Creating MCP Source Jar"); + var indent = LOGGER.push(); try { sources = sourcesTask.execute(); } finally { - Log.pop(indent); + LOGGER.pop(indent); } } @@ -211,8 +213,8 @@ public static void run(String[] args) throws Exception { .add("sources", sources); if (options.has(mappingsO)) { - Log.info("Renaming MCP Source Jar"); - var indent = Log.push(); + LOGGER.info("Renaming MCP Source Jar"); + var indent = LOGGER.push(); try { var mappings = options.has(parchmentO) ? new ParchmentMappings(options.valueOf(parchmentO)) @@ -221,7 +223,7 @@ public static void run(String[] args) throws Exception { var renameTask = new RenameTask(side.getBuildFolder(), pipeline, side, sourcesTask, mappings, false); sources = renameTask.execute(); } finally { - Log.pop(indent); + LOGGER.pop(indent); } cache.add("renamed", sources); @@ -242,14 +244,14 @@ public static void run(String[] args) throws Exception { // TODO [Mavenizer][Extra MCPTask Files] do this better private static void writeFiles(MCPTaskFactory mcpTaskFactory, File output) { var files = new MCPSetupFiles(); - files.versionManifest = mcpTaskFactory.findStep("downloadManifest").execute(); - files.versionJson = mcpTaskFactory.findStep("downloadJson").execute(); - files.clientRaw = mcpTaskFactory.findStep("downloadClient").execute(); - files.serverRaw = mcpTaskFactory.findStep("downloadServer").execute(); - files.serverExtracted = mcpTaskFactory.findStep("extractServer").execute(); - files.clientMappings = mcpTaskFactory.findStep("downloadClientMappings").execute(); - files.serverMappings = mcpTaskFactory.findStep("downloadServerMappings").execute(); - files.librariesList = mcpTaskFactory.findStep("listLibraries").execute(); + files.versionManifest = mcpTaskFactory.findStep("downloadManifest").execute().getAbsolutePath(); + files.versionJson = mcpTaskFactory.findStep("downloadJson").execute().getAbsolutePath(); + files.clientRaw = mcpTaskFactory.findStep("downloadClient").execute().getAbsolutePath(); + files.serverRaw = mcpTaskFactory.findStep("downloadServer").execute().getAbsolutePath(); + files.serverExtracted = mcpTaskFactory.findStep("extractServer").execute().getAbsolutePath(); + files.clientMappings = mcpTaskFactory.downloadClientMappings().execute().getAbsolutePath(); + files.serverMappings = mcpTaskFactory.downloadServerMappings().execute().getAbsolutePath(); + files.librariesList = mcpTaskFactory.findStep("listLibraries").execute().getAbsolutePath(); try { JsonData.toJson(files, output); diff --git a/src/main/java/net/minecraftforge/mcmaven/cli/Main.java b/src/main/java/net/minecraftforge/mcmaven/cli/Main.java index 92a2a1d..d089e45 100644 --- a/src/main/java/net/minecraftforge/mcmaven/cli/Main.java +++ b/src/main/java/net/minecraftforge/mcmaven/cli/Main.java @@ -6,7 +6,7 @@ import joptsimple.OptionParser; import joptsimple.OptionSpecBuilder; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import java.time.Duration; import java.util.ArrayList; @@ -16,22 +16,22 @@ public class Main { public static void main(String[] args) throws Exception { var start = System.nanoTime(); try { - Log.capture(); - Log.info(JarVersionInfo.of(DISPLAY_NAME, Main.class).implementation()); + LOGGER.capture(); + LOGGER.info(JarVersionInfo.of(DISPLAY_NAME, Main.class).implementation()); run(args); } catch (Throwable e) { - Log.release(); + LOGGER.release(); throw e; } var time = Duration.ofNanos(System.nanoTime() - start); - if (Log.isCapturing()) { - Log.drop(); - Log.INFO.print("Minecraft Maven is up-to-date"); + if (LOGGER.isCapturing()) { + LOGGER.drop(); + LOGGER.getInfo().print("Minecraft Maven is up-to-date"); } else { - Log.INFO.print("Minecraft Maven has finished"); + LOGGER.getInfo().print("Minecraft Maven has finished"); } - Log.INFO.println(String.format(", took %d:%02d.%03d", time.toMinutesPart(), time.toSecondsPart(), time.toMillisPart())); + LOGGER.getInfo().printf(", took %d:%02d.%03d%n", time.toMinutesPart(), time.toSecondsPart(), time.toMillisPart()); } private static void run(String[] args) throws Exception { @@ -59,7 +59,7 @@ private static void run(String[] args) throws Exception { } } - parser.printHelpOn(Log.INFO); - Log.release(); + parser.printHelpOn(LOGGER.getInfo()); + LOGGER.release(); } } diff --git a/src/main/java/net/minecraftforge/mcmaven/cli/MavenTask.java b/src/main/java/net/minecraftforge/mcmaven/cli/MavenTask.java index 884eb52..df425b5 100644 --- a/src/main/java/net/minecraftforge/mcmaven/cli/MavenTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/cli/MavenTask.java @@ -10,18 +10,20 @@ import joptsimple.OptionParser; import joptsimple.OptionSpecBuilder; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.MinecraftMaven; import net.minecraftforge.mcmaven.impl.mappings.Mappings; import net.minecraftforge.mcmaven.impl.mappings.ParchmentMappings; import net.minecraftforge.mcmaven.impl.util.Artifact; import net.minecraftforge.mcmaven.impl.util.Constants; -import net.minecraftforge.util.logging.Log; +import net.minecraftforge.util.logging.Logger; + +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; class MavenTask { static void run(String[] args) throws Exception { // TODO [MCMavenizer] Make this into a --log [level] option - Log.enabled = Log.Level.INFO; + LOGGER.setEnabled(Logger.Level.INFO); var parser = new OptionParser(); parser.allowsUnrecognizedOptions(); @@ -111,16 +113,16 @@ static void run(String[] args) throws Exception { var options = parser.parse(args); if (options.has(helpO)) { - parser.printHelpOn(Log.INFO); - Log.release(); + parser.printHelpOn(LOGGER.getInfo()); + LOGGER.release(); return; } // global options if (options.has(offlineO)) - GlobalOptions.setOffline(); + Mavenizer.setOffline(); if (options.has(cacheOnlyO)) - GlobalOptions.setCacheOnly(); + Mavenizer.setCacheOnly(); var output = options.valueOf(outputO); var cache = options.valueOf(cacheO); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/GlobalOptions.java b/src/main/java/net/minecraftforge/mcmaven/impl/Mavenizer.java similarity index 79% rename from src/main/java/net/minecraftforge/mcmaven/impl/GlobalOptions.java rename to src/main/java/net/minecraftforge/mcmaven/impl/Mavenizer.java index a518cc8..5c4d994 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/GlobalOptions.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/Mavenizer.java @@ -4,9 +4,11 @@ */ package net.minecraftforge.mcmaven.impl; -import net.minecraftforge.util.logging.Log; +import net.minecraftforge.util.logging.Logger; + +public final class Mavenizer { + public static final Logger LOGGER = Logger.create(); -public final class GlobalOptions { private static boolean offline = false; private static boolean cacheOnly = false; @@ -37,10 +39,11 @@ public static void assertNotCacheOnly() { if (cacheOnly) { throw new IllegalArgumentException("Cache is out of date! Please run without --cache-only"); } else if (!cacheMiss) { + LOGGER.warn("Cache miss!", new Exception("Cache miss!")); cacheMiss = true; - Log.release(); + LOGGER.release(); } } - private GlobalOptions() { } + private Mavenizer() { } } diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/MinecraftMaven.java b/src/main/java/net/minecraftforge/mcmaven/impl/MinecraftMaven.java index d66ff06..0259c52 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/MinecraftMaven.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/MinecraftMaven.java @@ -17,7 +17,7 @@ import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.hash.HashStore; import net.minecraftforge.util.hash.HashUtils; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import java.io.File; import java.util.ArrayList; @@ -37,22 +37,22 @@ public MinecraftMaven(File output, File cacheRoot, File jdkCacheRoot, Mappings m } public MinecraftMaven { - Log.info(" Output: " + output.getAbsolutePath()); - Log.info(" Cache: " + cache.root().getAbsolutePath()); - Log.info(" JDK Cache: " + cache.jdks().root().getAbsolutePath()); - Log.info(" Offline: " + GlobalOptions.isOffline()); - Log.info(" Cache Only: " + GlobalOptions.isCacheOnly()); - Log.info(" Mappings: " + mappings); + LOGGER.info(" Output: " + output.getAbsolutePath()); + LOGGER.info(" Cache: " + cache.root().getAbsolutePath()); + LOGGER.info(" JDK Cache: " + cache.jdks().root().getAbsolutePath()); + LOGGER.info(" Offline: " + Mavenizer.isOffline()); + LOGGER.info(" Cache Only: " + Mavenizer.isCacheOnly()); + LOGGER.info(" Mappings: " + mappings); if (!foreignRepositories.isEmpty()) - Log.info(" Foreign Repos: [" + String.join(", ", foreignRepositories.values()) + ']'); - Log.info(" GradleVariantHack: " + globalAuxiliaryVariants); - Log.info(); + LOGGER.info(" Foreign Repos: [" + String.join(", ", foreignRepositories.values()) + ']'); + LOGGER.info(" GradleVariantHack: " + globalAuxiliaryVariants); + LOGGER.info(); } public void run(Artifact artifact) { var module = artifact.getGroup() + ':' + artifact.getName(); var version = artifact.getVersion(); - Log.info("Processing Minecraft dependency: %s:%s".formatted(module, version)); + LOGGER.info("Processing Minecraft dependency: %s:%s".formatted(module, version)); var mcprepo = new MCPConfigRepo(this.cache); if (Constants.FORGE_GROUP.equals(artifact.getGroup()) && Constants.FORGE_NAME.equals(artifact.getName())) { diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/cache/JDKCache.java b/src/main/java/net/minecraftforge/mcmaven/impl/cache/JDKCache.java index c896f9e..e74f25b 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/cache/JDKCache.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/cache/JDKCache.java @@ -12,20 +12,21 @@ import java.util.List; import java.util.Map; -import net.minecraftforge.java_provisioner.api.IJavaInstall; -import net.minecraftforge.java_provisioner.api.IJavaLocator; -import net.minecraftforge.mcmaven.impl.GlobalOptions; -import net.minecraftforge.util.logging.Log; -import net.minecraftforge.util.logging.Log.Level; +import net.minecraftforge.java_provisioner.api.JavaInstall; +import net.minecraftforge.java_provisioner.api.JavaLocator; +import net.minecraftforge.java_provisioner.api.JavaProvisioner; +import net.minecraftforge.mcmaven.impl.Mavenizer; +import net.minecraftforge.util.logging.Logger; -import org.jetbrains.annotations.Nullable; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; /** Represents the JDK cache for this tool. */ public final class JDKCache { private boolean attemptedLocate = false; + private final List attemptedLocateErrors = new ArrayList<>(); private final File root; private final Map jdks = new HashMap<>(); - private final IJavaLocator disco; + private final JavaProvisioner disco; /** * Initializes the JDK cache with the given cache directory. @@ -34,7 +35,7 @@ public final class JDKCache { */ public JDKCache(File cache) { this.root = cache; - this.disco = IJavaLocator.disco(cache, GlobalOptions.isOffline()); + this.disco = JavaLocator.disco(cache, Mavenizer.isOffline()); } public File root() { @@ -48,7 +49,7 @@ public File root() { * @param version The version to get * @return The JDK, or {@code null} if it could not be found or downloaded */ - public @Nullable File get(int version) { + public File get(int version) throws Exception { if (!attemptedLocate) attemptLocate(); @@ -57,26 +58,19 @@ public File root() { if (ret != null) return ret; try { - var downloaded = disco.provision(version); // Implementation detail, we only download jdks, so no need to check here - if (downloaded == null) { - Log.error("Failed to find JDK for " + version); - for (var line : disco.logOutput()) - Log.error(" " + line); - return null; - } - ret = downloaded.home(); + ret = disco.provision(version).home(); // Implementation detail, we only download jdks, so no need to check here } catch (Exception e) { - Log.error("Failed to provision JDK " + version); - e.printStackTrace(Log.getLog(Level.ERROR)); - return null; + LOGGER.error("Failed to provision JDK " + version); + e.printStackTrace(LOGGER.getLog(Logger.Level.ERROR)); + throw e; } // not sure how this would ever hit. but just in case... var old = jdks.putIfAbsent(version, ret); if (old != null) { - Log.error("JDKCache: Downloaded JDK " + version + " is replacing an existing download! It was probably downloaded by another thread."); - Log.error("JDKCache: Old JDK: " + old); - // TODO Throw exception here + LOGGER.error("JDKCache: Downloaded JDK " + version + " is replacing an existing download! It was probably downloaded by another thread."); + LOGGER.error("JDKCache: Old JDK: " + old); + // TODO [Mavenizer][Provisioner] Properly account for parallel Mavenizer instances } return ret; @@ -86,16 +80,19 @@ private void attemptLocate() { if (attemptedLocate) return; attemptedLocate = true; - List locators = new ArrayList<>(); - locators.add(IJavaLocator.home()); - locators.add(IJavaLocator.gradle()); + List locators = new ArrayList<>(); + locators.add(JavaLocator.home()); + locators.add(JavaLocator.gradle()); locators.add(this.disco); - List installs = new ArrayList<>(); + List installs = new ArrayList<>(); - for (IJavaLocator locator : locators) { - List found = locator.findAll(); - installs.addAll(found); + for (JavaLocator locator : locators) { + try { + installs.addAll(locator.findAll()); + } catch (Exception e) { + attemptedLocateErrors.add(e); + } } // Remove duplicates @@ -103,7 +100,7 @@ private void attemptLocate() { installs.removeIf(install -> !seen.add(install.home())); Collections.sort(installs); - for (IJavaInstall install : installs) { + for (JavaInstall install : installs) { if (!install.isJdk() || install.majorVersion() <= 0) continue; this.jdks.putIfAbsent(install.majorVersion(), install.home()); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/cache/MavenCache.java b/src/main/java/net/minecraftforge/mcmaven/impl/cache/MavenCache.java index 938872f..5dd7980 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/cache/MavenCache.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/cache/MavenCache.java @@ -4,13 +4,13 @@ */ package net.minecraftforge.mcmaven.impl.cache; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.util.Artifact; import net.minecraftforge.util.download.DownloadUtils; import net.minecraftforge.util.hash.HashFunction; import net.minecraftforge.mcmaven.impl.util.Util; import net.minecraftforge.util.hash.HashUtils; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import org.jetbrains.annotations.ApiStatus; import org.w3c.dom.Document; import org.w3c.dom.NodeList; @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -213,18 +212,18 @@ protected File download(boolean changing, String path) throws IOException { if (!invalidHash && changing) { for (var func : knownHashes) { - if (GlobalOptions.isOffline()) continue; + if (Mavenizer.isOffline()) continue; - var rhash = DownloadUtils.tryDownloadString(true, repo + path + '.' + func.extension()); + var rhash = DownloadUtils.tryDownloadString(repo + path + '.' + func.extension()); if (rhash == null) continue; try { var chash = func.hash(target); if (!chash.equals(rhash)) { - Log.error("Outdated cached file: " + target.getAbsolutePath()); - Log.error("Expected: " + rhash); - Log.error("Actual: " + chash); + LOGGER.error("Outdated cached file: " + target.getAbsolutePath()); + LOGGER.error("Expected: " + rhash); + LOGGER.error("Actual: " + chash); invalidHash = true; } } catch (IOException e) { @@ -239,12 +238,12 @@ protected File download(boolean changing, String path) throws IOException { if (!invalidHash) return target; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); target.delete(); } - GlobalOptions.assertNotCacheOnly(); - GlobalOptions.assertOnline(); + Mavenizer.assertNotCacheOnly(); + Mavenizer.assertOnline(); downloadFile(target, path); HashUtils.updateHash(target, knownHashes); return target; diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/cache/MinecraftMavenCache.java b/src/main/java/net/minecraftforge/mcmaven/impl/cache/MinecraftMavenCache.java index 28abfc6..d5bcc7e 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/cache/MinecraftMavenCache.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/cache/MinecraftMavenCache.java @@ -14,6 +14,7 @@ import net.minecraftforge.util.file.FileUtils; import net.minecraftforge.util.hash.HashFunction; import net.minecraftforge.mcmaven.impl.util.Util; +import net.minecraftforge.util.os.OS; /** Represents the Minecraft maven cache for this tool. */ public final class MinecraftMavenCache extends MavenCache { @@ -22,7 +23,7 @@ public final class MinecraftMavenCache extends MavenCache { HashFunction.SHA1 }; - private static final File LOCAL_MCLIBS = new File(MCJsonUtils.getMCDir(), "libraries"); + private static final File LOCAL_MCLIBS = new File(MCJsonUtils.getMCDir(OS.current()), "libraries"); /** * Initializes a new maven cache with the given cache directory. diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/data/MCPSetupFiles.java b/src/main/java/net/minecraftforge/mcmaven/impl/data/MCPSetupFiles.java index acb692c..39ebcbf 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/data/MCPSetupFiles.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/data/MCPSetupFiles.java @@ -4,16 +4,14 @@ */ package net.minecraftforge.mcmaven.impl.data; -import java.io.File; - public class MCPSetupFiles { - public File versionManifest; - public File versionJson; - public File clientRaw; - public File clientMappings; - public File serverRaw; - public File serverExtracted; - public File serverMappings; - public File librariesList; - public File joinedSrgMappings; + 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/java/net/minecraftforge/mcmaven/impl/mappings/Mappings.java b/src/main/java/net/minecraftforge/mcmaven/impl/mappings/Mappings.java index 4293c2d..3dac9ea 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/mappings/Mappings.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/mappings/Mappings.java @@ -13,7 +13,7 @@ import java.util.Map; import java.util.zip.ZipFile; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MCPSide; import net.minecraftforge.mcmaven.impl.util.Artifact; import net.minecraftforge.mcmaven.impl.util.Constants; @@ -151,7 +151,7 @@ private File getMappings(MCPSide side, Task srgMappings, Task clientTask, Task s if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var args = List.of( "--task", @@ -166,9 +166,12 @@ private File getMappings(MCPSide side, Task srgMappings, Task clientTask, Task s output.getAbsolutePath() ); - var jdk = side.getMCP().getCache().jdks().get(Constants.INSTALLER_TOOLS_JAVA_VERSION); - if (jdk == null) - throw new IllegalStateException("Failed to find JDK for version " + Constants.INSTALLER_TOOLS_JAVA_VERSION); + File jdk; + try { + jdk = side.getMCP().getCache().jdks().get(Constants.INSTALLER_TOOLS_JAVA_VERSION); + } catch (Exception e) { + throw new IllegalStateException("Failed to find JDK for version " + Constants.INSTALLER_TOOLS_JAVA_VERSION, e); + } var ret = ProcessUtils.runJar(jdk, log.getParentFile(), log, tool, Collections.emptyList(), args); if (ret.exitCode != 0) diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/Repo.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/Repo.java index db5826f..e8068ad 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/Repo.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/Repo.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.repo; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.cache.Cache; import net.minecraftforge.mcmaven.impl.data.GradleModule; import net.minecraftforge.mcmaven.impl.mappings.Mappings; @@ -17,17 +17,21 @@ import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.file.FileUtils; import net.minecraftforge.util.hash.HashStore; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; @@ -73,6 +77,13 @@ protected Supplier sourceVariant(Mappings mappings) { }; } + protected Supplier metadataVariant() { + return () -> new GradleModule.Variant[] { + GradleModule.Variant.of("metadata") + .attribute("org.gradle.usage", "metadata") + }; + } + protected static Task variantTask(Task parent, Supplier supplier) { return Task.named(parent.name() + "[variants]", Task.deps(parent), () -> { var variants = supplier.get(); @@ -105,15 +116,23 @@ protected GradleModule.Variant[] classVariants(Mappings mappings, MCPSide side, var natives = new HashMap>(); for (var artifact : side.getMCLibraries()) { - var variant = GradleAttributes.OperatingSystemFamily.from(artifact.getOs()); - if (variant == null) + var osVariants = EnumSet.noneOf(GradleAttributes.OperatingSystemFamily.class); + for (var os : artifact.getOs()) { + var variant = GradleAttributes.OperatingSystemFamily.from(os); + if (variant != null) + osVariants.add(variant); + } + + if (osVariants.isEmpty()) { all.add(artifact); - else - natives.computeIfAbsent(variant, k -> new ArrayList<>()).add(artifact); + } else { + for (var variant : osVariants) { + natives.computeIfAbsent(variant, k -> new ArrayList<>()).add(artifact); + } + } } - for (var artifact : side.getMCPConfigLibraries()) - all.add(artifact); + all.addAll(side.getMCPConfigLibraries()); for (var extra : extraDeps) { if (extra != null) @@ -164,7 +183,7 @@ protected static Task simplePom(File build, Artifact artifact) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = new POMBuilder(artifact.getGroup(), artifact.getName(), artifact.getVersion()); @@ -201,14 +220,14 @@ public File get() { return this.task.execute(); try { - Log.info(this.message); - Log.push(); + LOGGER.info(this.message); + LOGGER.push(); return this.task.execute(); } finally { if (this.variants != null) this.variants.execute(); - Log.pop(); + LOGGER.pop(); } } diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/ForgeRepo.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/ForgeRepo.java index 4d66079..8c6549c 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/ForgeRepo.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/ForgeRepo.java @@ -15,14 +15,14 @@ import net.minecraftforge.mcmaven.impl.util.Artifact; import net.minecraftforge.mcmaven.impl.util.ComparableVersion; import net.minecraftforge.mcmaven.impl.util.Constants; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.util.POMBuilder; import net.minecraftforge.mcmaven.impl.util.Task; import net.minecraftforge.mcmaven.impl.util.Util; import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.file.FileUtils; import net.minecraftforge.util.hash.HashStore; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; @@ -72,8 +72,8 @@ public List process(Artifact artifact, Mappings mappings) { throw new IllegalArgumentException("Unknown or unsupported module: " + module); var fg = FGVersion.fromForge(version); - Log.info("Processing Minecraft Forge (userdev): " + version); - var indent = Log.push(); + LOGGER.info("Processing Minecraft Forge (userdev): " + version); + var indent = LOGGER.push(); try { if (fg == null) throw new IllegalArgumentException("Python version unsupported!"); @@ -87,7 +87,7 @@ public List process(Artifact artifact, Mappings mappings) { throw new IllegalArgumentException("Forge version %s is not supported yet".formatted(version)); } finally { - Log.pop(indent); + LOGGER.pop(indent); } } @@ -145,7 +145,7 @@ private List processV3(String version, Mappings mappings) { var sources = pending("Sources", sourcesTask, name.withClassifier("sources"), true, sourceVariant(mappings)); var classes = pending("Classes", classesTask, name, false, () -> classVariants(mappings, patcher, extraCoords, mappingCoords)); - var metadata = pending("Metadata", metadata(build, patcher), name.withClassifier("metadata").withExtension("zip"), false); + var metadata = pending("Metadata", metadata(build, patcher), name.withClassifier("metadata").withExtension("zip"), false, metadataVariant()); var pom = pending("Maven POM", pom(build, patcher, version, extraCoords, mappingCoords), name.withExtension("pom"), false); @@ -174,13 +174,14 @@ private static Task metadata(File build, Patcher patcher) { var cache = HashStore .fromFile(output) - .add("data", patcher.getDataHash()) .add(versionJson) + .add(versionProperties) + .add("data", patcher.getDataHash()) .addKnown("version", "1"); if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { FileUtils.ensureParent(output); @@ -232,7 +233,7 @@ private static Task pom(File build, Patcher patcher, String version, Artifact cl if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = new POMBuilder("net.minecraftforge", "forge", version).preferGradleModule().dependencies(dependencies -> { if (clientExtra != null) diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/InjectTask.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/InjectTask.java index a8afbc6..d24548c 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/InjectTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/InjectTask.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.repo.forge; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.cache.Cache; import net.minecraftforge.mcmaven.impl.mappings.Mappings; import net.minecraftforge.mcmaven.impl.util.Artifact; @@ -16,8 +16,6 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Set; -import java.util.function.Supplier; /** * Takes a jar containing compiled class files, and injects extra data/resources from a patcher into it. @@ -79,12 +77,13 @@ private File injectDataImpl(Task inputTask, File outputJar) { if (outputJar.exists() && cache.isSame()) return outputJar; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); cache.clear().add("recompiled", recompiledJar); try { - var jars = new ArrayList(); - jars.addAll(universals); + var jars = new ArrayList<>(universals); + universals.forEach(cache::add); + jars.add(recompiledJar); FileUtils.mergeJars(outputJar, true, (file, name) -> file == recompiledJar || !name.endsWith(".class"), jars.toArray(File[]::new)); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/Patcher.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/Patcher.java index 3a7eb58..4c52d80 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/Patcher.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/forge/Patcher.java @@ -13,7 +13,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Stack; import java.util.function.Consumer; import java.util.function.Predicate; @@ -27,7 +26,7 @@ import io.codechicken.diffpatch.util.Input.MultiInput; import io.codechicken.diffpatch.util.Output.MultiOutput; import io.codechicken.diffpatch.util.archiver.ArchiveFormat; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.cache.Cache; import net.minecraftforge.mcmaven.impl.cache.MavenCache; import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MCP; @@ -42,7 +41,7 @@ import net.minecraftforge.mcmaven.impl.util.ProcessUtils; import net.minecraftforge.mcmaven.impl.util.Task; import net.minecraftforge.mcmaven.impl.util.Util; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import org.jetbrains.annotations.Nullable; // TODO: [MCMavenizer] This class needs to be split off into some sort of abstract class so that other patching processes can be implemented. @@ -336,7 +335,7 @@ private File extractJoinedFiles(String filename, List files) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); if (output.exists()) output.delete(); @@ -384,7 +383,7 @@ private File extractSingleTask(String key, String value) { if (target.exists() && cache.isSame()) return target; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try (var zip = new ZipFile(this.data)) { var entry = zip.getEntry(value); @@ -421,7 +420,7 @@ public static File modifyAccess(File globalBase, Task inputTask, File cfg, Cache if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var args = List.of( "--inJar", input.getAbsolutePath(), @@ -429,9 +428,12 @@ public static File modifyAccess(File globalBase, Task inputTask, File cfg, Cache "--outJar", output.getAbsolutePath() ); - var jdk = dlCache.jdks().get(Constants.ACCESS_TRANSFORMER_JAVA_VERSION); - if (jdk == null) - throw new IllegalStateException("Failed to find JDK for version " + Constants.ACCESS_TRANSFORMER_JAVA_VERSION); + File jdk; + try { + jdk = dlCache.jdks().get(Constants.ACCESS_TRANSFORMER_JAVA_VERSION); + } catch (Exception e) { + throw new IllegalStateException("Failed to find JDK for version " + Constants.ACCESS_TRANSFORMER_JAVA_VERSION, e); + } var ret = ProcessUtils.runJar(jdk, globalBase, log, tool, Collections.emptyList(), args); if (ret.exitCode != 0) @@ -454,7 +456,7 @@ public static File stripSides(File globalBase, Task inputTask, File cfg, Cache d if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var args = new ArrayList(); args.add("--strip"); @@ -465,9 +467,12 @@ public static File stripSides(File globalBase, Task inputTask, File cfg, Cache d args.add("--output"); args.add(output.getAbsolutePath()); - var jdk = dlCache.jdks().get(Constants.SIDE_STRIPPER_JAVA_VERSION); - if (jdk == null) - throw new IllegalStateException("Failed to find JDK for version " + Constants.SIDE_STRIPPER_JAVA_VERSION); + File jdk; + try { + jdk = dlCache.jdks().get(Constants.SIDE_STRIPPER_JAVA_VERSION); + } catch (Exception e) { + throw new IllegalStateException("Failed to find JDK for version " + Constants.SIDE_STRIPPER_JAVA_VERSION, e); + } var ret = ProcessUtils.runJar(jdk, globalBase, log, tool, Collections.emptyList(), args); if (ret.exitCode != 0) @@ -528,7 +533,7 @@ private File postProcess(Task inputTask, PatcherConfig.V2.DataFunction data, Fil if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var args = new ArrayList(); for (var arg : data.getArgs()) @@ -536,10 +541,12 @@ private File postProcess(Task inputTask, PatcherConfig.V2.DataFunction data, Fil int java_version = data.getJavaVersion(this.getMCP().getConfig()); var jdks = this.getMCP().getCache().jdks(); - var jdk = jdks.get(java_version); - if (jdk == null) - throw new IllegalStateException("Failed to find JDK for version " + java_version); - + File jdk; + try { + jdk = jdks.get(java_version); + } catch (Exception e) { + throw new IllegalStateException("Failed to find JDK for version " + java_version, e); + } var ret = ProcessUtils.runJar(jdk, log.getParentFile(), log, tool, data.getJvmArgs(), args); if (ret.exitCode != 0) @@ -568,10 +575,10 @@ private File patch(Task inputTask, File output, File rejects) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = PatchOperation.builder() - .logTo(Log::error) + .logTo(LOGGER::error) .baseInput(MultiInput.archive(ArchiveFormat.ZIP, input.toPath())) .patchesInput(MultiInput.archive(ArchiveFormat.ZIP, this.data.toPath())) .patchedOutput(MultiOutput.archive(ArchiveFormat.ZIP, output.toPath())) @@ -592,9 +599,9 @@ private File patch(Task inputTask, File output, File rejects) { boolean success = result.exit == 0; if (!success) { if (result.summary != null) - result.summary.print(Log.ERROR, true); + result.summary.print(LOGGER.getError(), true); else - Log.error("Failed to apply patches, no summary available"); + LOGGER.error("Failed to apply patches, no summary available"); throw except("Failed to apply patches, rejects saved to: " + rejects.getAbsolutePath()); } @@ -628,7 +635,7 @@ private File injectSourcesImpl(Task inputTask, File output) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { FileUtils.mergeJars(output, false, diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPConfigRepo.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPConfigRepo.java index 044baed..6bbdeac 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPConfigRepo.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPConfigRepo.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.repo.mcpconfig; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.repo.Repo; import net.minecraftforge.mcmaven.impl.tasks.RecompileTask; import net.minecraftforge.mcmaven.impl.tasks.RenameTask; @@ -123,7 +123,7 @@ public List process(Artifact artifact, Mappings mappings) { var sources = pending("Sources", sourcesTask, name.withClassifier("sources"), true, sourceVariant(mappings)); var classes = pending("Classes", classesTask, name, false, () -> classVariants(mappings, mcpSide)); - var metadata = pending("Metadata", metadata(build, mcpSide), name.withClassifier("metadata").withExtension("zip"), false); + var metadata = pending("Metadata", metadata(build, mcpSide), name.withClassifier("metadata").withExtension("zip"), false, metadataVariant()); var pom = pending("Maven POM", pom(build, side, mcpSide, version), name.withExtension("pom"), false); pending.addAll(List.of( @@ -168,7 +168,7 @@ private static Task mergeExtra(File build, String side, Task recompiled, Task ex if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { FileUtils.mergeJars(output, true, extraF, recompiledF); @@ -183,7 +183,7 @@ private static Task mergeExtra(File build, String side, Task recompiled, Task ex private static Task metadata(File build, MCPSide side) { var minecraftTasks = side.getMCP().getMinecraftTasks(); - return Task.named("metadata[forge]", Task.deps(minecraftTasks.versionJson), () -> { + return Task.named("metadata[" + side + ']', Task.deps(minecraftTasks.versionJson), () -> { var output = new File(build, "metadata.zip"); // metadata @@ -201,7 +201,7 @@ private static Task metadata(File build, MCPSide side) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { FileUtils.ensureParent(output); @@ -240,7 +240,7 @@ private static Task pom(File build, String side, MCPSide mcpSide, String version if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = new POMBuilder("net.minecraft", side, version).preferGradleModule().dependencies(dependencies -> { mcpSide.forAllLibraries(dependencies::add, Artifact::hasNoOs); @@ -265,7 +265,7 @@ private static Task pomExtra(File build, String side, String version) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = new POMBuilder("net.minecraft", side, version); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPTaskFactory.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPTaskFactory.java index 842f72a..e9126b0 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPTaskFactory.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MCPTaskFactory.java @@ -23,7 +23,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.function.BiPredicate; -import java.util.function.Supplier; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; @@ -41,11 +40,10 @@ import io.codechicken.diffpatch.util.Input.MultiInput; import io.codechicken.diffpatch.util.Output.MultiOutput; import io.codechicken.diffpatch.util.archiver.ArchiveFormat; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.cache.MavenCache; import net.minecraftforge.mcmaven.impl.util.Artifact; import net.minecraftforge.mcmaven.impl.util.Constants; -import net.minecraftforge.util.data.OS; import net.minecraftforge.util.data.json.JsonData; import net.minecraftforge.util.data.json.MCPConfig; import net.minecraftforge.util.file.FileUtils; @@ -54,8 +52,7 @@ import net.minecraftforge.mcmaven.impl.util.Task; import net.minecraftforge.mcmaven.impl.util.Util; import net.minecraftforge.srgutils.IMappingFile; -import net.minecraftforge.util.logging.Log; -import org.jetbrains.annotations.NotNull; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import org.jetbrains.annotations.Nullable; // TODO [MCMavenizer][Documentation] Document @@ -245,7 +242,7 @@ public Task findStep(String name) { var ret = this.tasks.get(name); if (ret == null) - throw except("Unknown task `" + name + "`"); + throw except("Unknown task `" + name + '`'); return ret; } @@ -281,7 +278,7 @@ private File extractSingle(String key, String value) { if (target.exists() && cache.isSame()) return target; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try (var zip = new ZipFile(getData())) { var entry = zip.getEntry(value); @@ -328,7 +325,7 @@ private File extractFolder(String key, String value) { FileUtils.ensureParent(target); if (!target.exists() || !same) { - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try (var os = new FileOutputStream(target)) { zip.getInputStream(e).transferTo(os); } @@ -380,8 +377,8 @@ private Task createTask(Map step) { if (spec >= 2) { switch (type) { - case "downloadClientMappings": return mc.versionFile("client_mappings", "txt"); - case "downloadServerMappings": return mc.versionFile("server_mappings", "txt"); + case "downloadClientMappings": return downloadClientMappings(); + case "downloadServerMappings": return downloadServerMappings(); } } @@ -393,6 +390,14 @@ private Task createTask(Map step) { return execute(name, step, custom); } + public Task downloadClientMappings() { + return this.side.getMCP().getMinecraftTasks().versionFile("client_mappings", "txt"); + } + + public Task downloadServerMappings() { + return this.side.getMCP().getMinecraftTasks().versionFile("server_mappings", "txt"); + } + private Task strip(String name, Map step) { var whitelist = "whitelist".equalsIgnoreCase(step.getOrDefault("mode", "whitelist")); var output = new File(this.build, name + ".jar").getAbsoluteFile(); @@ -414,7 +419,7 @@ private File strip(Task inputTask, boolean whitelist, File output) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); if (output.exists()) output.delete(); @@ -467,7 +472,7 @@ private File inject(Task inputTask, Task injectTask, File packages, File output) if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); if (output.exists()) output.delete(); @@ -542,10 +547,10 @@ private File patch(Task inputTask, Task patchesTask, File output, File rejects) if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); var builder = PatchOperation.builder() - .logTo(Log::error) + .logTo(LOGGER::error) .baseInput(MultiInput.archive(ArchiveFormat.ZIP, input.toPath())) .patchesInput(MultiInput.folder(patches.toPath())) .patchedOutput(MultiOutput.archive(ArchiveFormat.ZIP, output.toPath())) @@ -565,9 +570,9 @@ private File patch(Task inputTask, Task patchesTask, File output, File rejects) boolean success = result.exit == 0; if (!success) { if (result.summary != null) - result.summary.print(Log.ERROR, true); + result.summary.print(LOGGER.getError(), true); else - Log.error("Failed to apply patches, no summary available"); + LOGGER.error("Failed to apply patches, no summary available"); throw except("Failed to apply patches, Rejects saved to: " + rejects.getAbsolutePath()); } @@ -600,11 +605,17 @@ private File listLibraries(Task jsonTask, File output) { cache.addKnown(lib.coord, lib.dl.sha1); if (output.exists() && libsVarCache.exists() && cache.isSame()) { - this.libraries = JsonData.>fromJson(libsVarCache, new TypeToken<>() { }).stream().map(Lib.Cached::resolve).toList(); - return output; + try { + this.libraries = JsonData.>fromJson(libsVarCache, new TypeToken<>() { }).stream().map(Lib.Cached::resolve).toList(); + return output; + } catch (Exception e) { + LOGGER.error("Failed to load cached libraries, regenerating..."); + LOGGER.error("Cached file: " + libsVarCache.getAbsolutePath()); + e.printStackTrace(LOGGER.getError()); + } } - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); cache.clear().add(jsonF); var buf = new StringBuilder(20_000); @@ -618,9 +629,7 @@ private File listLibraries(Task jsonTask, File output) { buf.append("-e=").append(target.getAbsolutePath()).append('\n'); - var artifact = Artifact.from(lib.coord); - if (lib.os != null && lib.os != OS.UNKNOWN) - artifact = artifact.withOS(lib.os); + var artifact = Artifact.from(lib.coord).withOS(lib.os); downloadedLibs.add(new Lib(artifact, target)); cache.add(lib.coord, target); @@ -714,7 +723,7 @@ public int compareTo(LibLine o) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); cache.clear().add(bundle); var buf = new StringBuilder(); @@ -776,7 +785,7 @@ private File getExtra(Task prestripTask, Task mappingsTask) { if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { var whitelist = IMappingFile @@ -841,13 +850,16 @@ private File execute(List jvmArgs, List runArgs, MCPConfig if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); int java_version = func.getJavaVersion(this.side.getMCP().getConfig()); var jdks = this.side.getMCP().getCache().jdks(); - var jdk = jdks.get(java_version); - if (jdk == null) - throw new IllegalStateException("Failed to find JDK for version " + java_version); + File jdk; + try { + jdk = jdks.get(java_version); + } catch (Exception e) { + throw new IllegalStateException("Failed to find JDK for version " + java_version, e); + } var ret = ProcessUtils.runJar(jdk, log.getParentFile(), log, tool, jvm, run); if (ret.exitCode != 0) diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MinecraftTasks.java b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MinecraftTasks.java index d01ba0d..ed0b179 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MinecraftTasks.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/repo/mcpconfig/MinecraftTasks.java @@ -9,7 +9,7 @@ import java.util.HashMap; import java.util.Map; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.util.Constants; import net.minecraftforge.mcmaven.impl.util.Util; import net.minecraftforge.util.data.json.JsonData; @@ -40,10 +40,10 @@ public class MinecraftTasks { private File downloadLauncherManifest() { var target = new File(this.cache, "launcher_manifest.json"); - if (!target.exists() || (!GlobalOptions.isCacheOnly() && target.lastModified() < System.currentTimeMillis() - Constants.CACHE_TIMEOUT)) { + if (!target.exists() || (!Mavenizer.isCacheOnly() && target.lastModified() < System.currentTimeMillis() - Constants.CACHE_TIMEOUT)) { try { - GlobalOptions.assertNotCacheOnly(); - GlobalOptions.assertOnline(); + Mavenizer.assertNotCacheOnly(); + Mavenizer.assertOnline(); DownloadUtils.downloadFile(target, Constants.LAUNCHER_MANIFEST); } catch (IOException e) { Util.sneak(e); @@ -63,8 +63,8 @@ private File downloadVersionJson() { if (target.exists() && cache.isSame()) return target; - GlobalOptions.assertNotCacheOnly(); - GlobalOptions.assertOnline(); + Mavenizer.assertNotCacheOnly(); + Mavenizer.assertOnline(); var manifest = JsonData.launcherManifest(manifestF); var url = manifest.getUrl(this.version); @@ -72,7 +72,7 @@ private File downloadVersionJson() { throw new IllegalStateException("Failed to find url for " + this.version + " version.json"); try { - DownloadUtils.downloadFile(false, target, url.toExternalForm()); + DownloadUtils.downloadFile(target, url.toExternalForm()); } catch (IOException e) { throw new IllegalStateException("Failed to download " + url, e); } @@ -99,8 +99,8 @@ private File downloadVersionFile(String key, String ext) { if (target.exists() && cache.isSame()) return target; - GlobalOptions.assertNotCacheOnly(); - GlobalOptions.assertOnline(); + Mavenizer.assertNotCacheOnly(); + Mavenizer.assertOnline(); var manifest = JsonData.minecraftVersion(manifestF); var dl = manifest.getDownload(key); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/MCPNames.java b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/MCPNames.java index 9fb3a2b..6982d39 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/MCPNames.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/MCPNames.java @@ -6,7 +6,7 @@ import de.siegmar.fastcsv.reader.CsvReader; import net.minecraftforge.util.hash.HashFunction; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.Nullable; @@ -215,7 +215,7 @@ private boolean injectJavadoc(List lines, String line, String _package, if (len == value.getRight()) { innerClasses.pop(); } else if (len < value.getRight()) { - Log.error("Failed to properly track class blocks around class " + value.getLeft() + ":" + (lines.size() + 1)); + LOGGER.error("Failed to properly track class blocks around class " + value.getLeft() + ":" + (lines.size() + 1)); return false; } } diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RecompileTask.java b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RecompileTask.java index 7394dd4..fd58b56 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RecompileTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RecompileTask.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.tasks; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.mappings.Mappings; import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MCP; import net.minecraftforge.mcmaven.impl.util.Artifact; @@ -14,7 +14,6 @@ import java.io.File; import java.util.List; -import java.util.Set; import java.util.function.Supplier; /** @@ -72,11 +71,13 @@ private File recompileSourcesImpl(Task inputTask, File outputJar) { if (outputJar.exists() && cache.isSame()) return outputJar; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); - var jdk = this.mcp.getCache().jdks().get(javaTarget); - if (jdk == null) { - throw new IllegalStateException("JDK not found: " + javaTarget); + File jdk; + try { + jdk = this.mcp.getCache().jdks().get(javaTarget); + } catch (Exception e) { + throw new IllegalStateException("JDK not found: " + javaTarget, e); } ProcessUtils.recompileJar(jdk, this.classpath.get(), sourcesJar, outputJar, this.build); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RenameTask.java b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RenameTask.java index e22a4bb..b2534e7 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RenameTask.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/tasks/RenameTask.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.tasks; -import net.minecraftforge.mcmaven.impl.GlobalOptions; +import net.minecraftforge.mcmaven.impl.Mavenizer; import net.minecraftforge.mcmaven.impl.mappings.Mappings; import net.minecraftforge.mcmaven.impl.repo.mcpconfig.MCPSide; import net.minecraftforge.util.file.FileUtils; @@ -90,7 +90,7 @@ private static File remapSourcesImpl(Task inputTask, Task mappingsTask, File out if (output.exists() && cache.isSame()) return output; - GlobalOptions.assertNotCacheOnly(); + Mavenizer.assertNotCacheOnly(); try { var names = MCPNames.load(mappings); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/Arch.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/Arch.java deleted file mode 100644 index a56139f..0000000 --- a/src/main/java/net/minecraftforge/mcmaven/impl/util/Arch.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.mcmaven.impl.util; - -// TODO Always warn on unknown OS - -import java.util.Locale; - -/** - * Enum representing the operating system. - */ -public enum Arch { - X86 ("x86", "i386", "i686"), - X86_64 ("x86_64", "x64", "amd64"), - ARM ("arm", "aarch32", "aarch_32"), - ARM64 ("arm64", "aarch64", "aarch_64"), - UNKNOWN("unknown"); - - private static final Arch[] $values = values(); - /** The operating system that this tool is being run on. */ - public static final Arch CURRENT = getCurrent(); - - private final String key; - private final String[] names; - - Arch(String... names) { - this.key = names[0]; - this.names = names; - } - - /** - * The primary name, and enum key, of the operating system. - * - * @return The primary name - */ - public String key() { - return this.key; - } - - /** - * Returns the OS enum for the given key. - * - * @param key The key to search for - * @return The OS enum, or null if not found - */ - public static Arch byKey(String key) { - for (Arch value : $values) { - if (value.key.equals(key)) - return value; - } - - return UNKNOWN; - } - - public static Arch byName(String name) { - for (Arch value : $values) { - for (String n : value.names) { - if (n.equals(name)) - return value; - } - } - - return UNKNOWN; - } - - private static Arch getCurrent() { - String prop = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); - for (Arch os : $values) { - for (String key : os.names) { - if (prop.contains(key)) return os; - } - } - - return UNKNOWN; - } -} diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/Artifact.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/Artifact.java index 86d1abe..802932b 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/util/Artifact.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/util/Artifact.java @@ -4,12 +4,14 @@ */ package net.minecraftforge.mcmaven.impl.util; -import net.minecraftforge.util.data.OS; +import net.minecraftforge.util.os.OS; +import net.minecraftforge.util.os.Arch; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.Serial; import java.io.Serializable; +import java.util.EnumSet; import java.util.Locale; import java.util.Objects; import java.util.regex.Pattern; @@ -27,7 +29,7 @@ public class Artifact implements Comparable, Serializable { private final @Nullable String version; private final @Nullable String classifier; private final String ext; - private final OS os; + private final EnumSet os; private final Arch arch; // Cached after building the first time we're asked @@ -106,7 +108,7 @@ private Artifact(String descriptor) { this.version = pts.length > 2 ? pts[2] : null; this.classifier = pts.length > 3 ? pts[3] : null; - this.os = this.classifier != null ? findOS(this.classifier) : OS.UNKNOWN; + this.os = this.classifier != null ? findOS(this.classifier) : EnumSet.noneOf(OS.class); this.arch = this.classifier != null ? findArch(this.classifier) : Arch.UNKNOWN; } @@ -116,21 +118,31 @@ private Artifact(String group, String name, String version, @Nullable String cla this.version = version; this.classifier = classifier; this.ext = Objects.requireNonNullElse(ext, "jar"); - this.os = os == OS.UNKNOWN && classifier != null ? findOS(classifier) : os; + this.os = classifier != null ? findOS(classifier) : os == OS.UNKNOWN ? EnumSet.noneOf(OS.class) : EnumSet.of(os); this.arch = arch == Arch.UNKNOWN && classifier != null ? findArch(classifier) : arch; } - private static OS findOS(String classifier) { + private Artifact(String group, String name, String version, @Nullable String classifier, @Nullable String ext, EnumSet os, Arch arch) { + this.group = group; + this.name = name; + this.version = version; + this.classifier = classifier; + this.ext = Objects.requireNonNullElse(ext, "jar"); + this.os = os; + this.arch = arch == Arch.UNKNOWN && classifier != null ? findArch(classifier) : arch; + } + + private static EnumSet findOS(String classifier) { for (var s : classifier.split("-")) { if (s.isBlank()) continue; var osCandidate = OS.byName(s); if (osCandidate != OS.UNKNOWN) { - return osCandidate; + return EnumSet.of(osCandidate); } } - return OS.UNKNOWN; + return EnumSet.noneOf(OS.class); } private static Arch findArch(String classifier) { @@ -216,12 +228,12 @@ public String getExtension() { } /** @return The os of this artifact (defaults to {@link OS#UNKNOWN}) */ - public OS getOs() { + public EnumSet getOs() { return os; } public boolean hasNoOs() { - return this.os == OS.UNKNOWN; + return this.os.isEmpty(); } /** @return The arch of this artifact (defaults to {@link Arch#UNKNOWN}) */ @@ -274,6 +286,10 @@ public Artifact withOS(OS os) { return new Artifact(group, name, version, classifier, ext, os, arch); } + public Artifact withOS(EnumSet os) { + return new Artifact(group, name, version, classifier, ext, os, arch); + } + public Artifact withArch(Arch arch) { return new Artifact(group, name, version, classifier, ext, os, arch); } diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/GradleAttributes.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/GradleAttributes.java index fddd405..53cea58 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/util/GradleAttributes.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/util/GradleAttributes.java @@ -5,7 +5,7 @@ package net.minecraftforge.mcmaven.impl.util; import net.minecraftforge.mcmaven.impl.data.GradleModule; -import net.minecraftforge.util.data.OS; +import net.minecraftforge.util.os.OS; import org.jetbrains.annotations.Nullable; import java.util.Locale; @@ -24,12 +24,13 @@ public String getValue() { return this.name().toLowerCase(Locale.ROOT); } - public static @Nullable OperatingSystemFamily from(OS os) { + public static @Nullable OperatingSystemFamily from(@Nullable OS os) { return switch (os) { case WINDOWS -> WINDOWS; case MACOS -> MACOS; - case LINUX, ALPINE, MUSL -> LINUX; - case null, default -> null; + case null -> null; + case UNKNOWN -> null; + default -> LINUX; }; } } diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java index ab1c448..864a4a5 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/util/ProcessUtils.java @@ -4,9 +4,9 @@ */ package net.minecraftforge.mcmaven.impl.util; -import net.minecraftforge.java_provisioner.util.OS; +import net.minecraftforge.util.os.OS; import net.minecraftforge.util.file.FileUtils; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import org.apache.commons.io.IOUtils; import java.io.BufferedReader; @@ -114,7 +114,7 @@ public static int runCommand(Consumer lines, String... args) { * @return The exit code of the process */ public static int runCommand(File workDir, Consumer lines, String... args) { - Log.debug("Running Command: " + String.join(" ", args)); + LOGGER.debug("Running Command: " + String.join(" ", args)); Process process; try { @@ -189,9 +189,9 @@ public static Result runJar(File javaHome, File workDir, File logFile, File tool classpath += File.pathSeparator + classes.getAbsolutePath(); var main = getMainClass(tool); - var launcher = new File(javaHome, "bin/java" + OS.CURRENT.exe()); + var launcher = new File(javaHome, "bin/java" + OS.current().exe()); Consumer lines = line -> { - Log.quiet(line); + LOGGER.quiet(line); log.println(line); }; lines.accept("Java: " + launcher.getAbsolutePath()); @@ -284,10 +284,10 @@ public static File recompileJar(File javaHome, List classpath, File source var process = ProcessUtils.runJavac(javaHome, workDir, new File(outputJar.getAbsolutePath() + ".log"), args); if (process.exitCode != 0) { - Log.error("Javac failed to execute! Exit code " + process.exitCode); - Log.error("--- BEGIN JAVAC LOG ---"); - process.lines.forEach(Log::error); - Log.error("--- END JAVAC LOG ---"); + LOGGER.error("Javac failed to execute! Exit code " + process.exitCode); + LOGGER.error("--- BEGIN JAVAC LOG ---"); + process.lines.forEach(LOGGER::error); + LOGGER.error("--- END JAVAC LOG ---"); throw new RuntimeException("Javac failed to execute! Exit code " + process.exitCode); } @@ -335,9 +335,9 @@ private static Result runJavac(File javaHome, File workDir, File logFile, List lines = line -> { - Log.quiet(line); + LOGGER.quiet(line); log.println(line); }; lines.accept("Java Compiler: " + launcher.getAbsolutePath()); diff --git a/src/main/java/net/minecraftforge/mcmaven/impl/util/Task.java b/src/main/java/net/minecraftforge/mcmaven/impl/util/Task.java index b868dcf..ec2b25f 100644 --- a/src/main/java/net/minecraftforge/mcmaven/impl/util/Task.java +++ b/src/main/java/net/minecraftforge/mcmaven/impl/util/Task.java @@ -4,7 +4,7 @@ */ package net.minecraftforge.mcmaven.impl.util; -import net.minecraftforge.util.logging.Log; +import static net.minecraftforge.mcmaven.impl.Mavenizer.LOGGER; import java.io.File; import java.time.Duration; @@ -103,19 +103,19 @@ public File execute() { } } - Log.info(name); - var indent = Log.push(); + LOGGER.info(name); + var indent = LOGGER.push(); var start = System.nanoTime(); try { this.file = supplier.call(); var time = Duration.ofNanos(System.nanoTime() - start); - Log.debug(String.format("-> took %d:%02d.%03d", time.toMinutesPart(), time.toSecondsPart(), time.toMillisPart())); - Log.debug("-> " + this.file.getAbsolutePath()); + LOGGER.debug(String.format("-> took %d:%02d.%03d", time.toMinutesPart(), time.toSecondsPart(), time.toMillisPart())); + LOGGER.debug("-> " + this.file.getAbsolutePath()); } catch (Exception e) { throw new RuntimeException("Failed to execute task `%s`".formatted(this.name()), e); } finally { - Log.pop(indent); + LOGGER.pop(indent); } }