diff --git a/.github/workflows/test-prs.yml b/.github/workflows/test-prs.yml index 853eeb269..e92a0092b 100644 --- a/.github/workflows/test-prs.yml +++ b/.github/workflows/test-prs.yml @@ -86,7 +86,6 @@ jobs: - name: Build run: ./gradlew --info -s -x assemble -Ptraining-wheels.gradle-version=${{ env.GRADLE_VERSION }} - test: name: "${{ matrix.test.displayName }} (${{ matrix.os }})" runs-on: "${{ matrix.os }}-latest" @@ -99,6 +98,7 @@ jobs: os: [ubuntu, windows, macos] env: GRADLE_VERSION: ${{ needs.set-gradle-version.outputs.gradle-version }} + CI: false steps: - name: Checkout repository uses: neoforged/actions/checkout@main @@ -175,7 +175,6 @@ jobs: name: test-results-${{ steps.format-artifact-name-windows.outputs.artifact-name }} path: junit.xml retention-days: 1 - process-test-data: name: Process Test Data runs-on: ubuntu-latest @@ -192,10 +191,11 @@ jobs: path: downloaded_artifacts - name: Publish Test Report - uses: mikepenz/action-junit-report@v4 + uses: mikepenz/action-junit-report@v6 if: always() # always run even if the previous step fails with: report_paths: '**/*.xml' + update_check: 'true' - name: Merge Test Reports if: always() diff --git a/build.gradle b/build.gradle index 97ab16781..ee68ca1f8 100644 --- a/build.gradle +++ b/build.gradle @@ -65,11 +65,6 @@ subprojects.forEach { Project subProject -> subProject.java.toolchain.languageVersion = JavaLanguageVersion.of(project.java_version) subProject.java.withSourcesJar() - //We exclude ASM from all subprojects, it is handled by Gradle itself. - subProject.configurations.configureEach { Configuration configuration -> - configuration.exclude group: 'org.ow2.asm' - } - ['apiElements', 'runtimeElements'].each { subProject.configurations.named(it).configure { attributes { diff --git a/common/build.gradle b/common/build.gradle index ad81b802b..28936c2e4 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -35,6 +35,10 @@ dependencies { api "net.neoforged:JarJarMetadata:${project.jarjar_version}" api "net.neoforged:JarJarSelector:${project.jarjar_version}" + api "org.ow2.asm:asm:${project.asm_version}" + api "org.ow2.asm:asm-commons:${project.asm_version}" + api "org.ow2.asm:asm-tree:${project.asm_version}" + // IDE support api "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:${project.gradle_idea_extension_version}" } diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithEnabledProperty.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithEnabledProperty.java index b3c50f60b..2b64f7121 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithEnabledProperty.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithEnabledProperty.java @@ -57,6 +57,15 @@ protected Provider getBooleanProperty(String propertyName, boolean defa ); } + @Override + protected Provider getBooleanProperty(final String propertyName) + { + return getIsEnabled().zip( + getBooleanLocalProperty(propertyName), + (enabled, value) -> enabled ? value : null + ); + } + @Override protected Provider> getSpaceSeparatedListProperty(String propertyName, List defaultValue) { diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithLocalProperties.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithLocalProperties.java index 216a53d5a..203f6729d 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithLocalProperties.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithLocalProperties.java @@ -37,6 +37,11 @@ protected Provider getBooleanLocalProperty(String propertyName, boolean return super.getBooleanProperty(String.format("%s.%s", prefix, propertyName), defaultValue, false); } + protected Provider getBooleanLocalProperty(String propertyName) + { + return super.getBooleanProperty(String.format("%s.%s", prefix, propertyName)); + } + protected Provider> getSpaceSeparatedListLocalProperty(String propertyName, List defaultValue) { return super.getSpaceSeparatedListProperty(String.format("%s.%s", prefix, propertyName), defaultValue); diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithPropertyLookup.java b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithPropertyLookup.java index 2ae1547d9..435964535 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithPropertyLookup.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/base/WithPropertyLookup.java @@ -35,14 +35,25 @@ protected Provider getDirectoryProperty(String propertyName, Provider protected Provider getBooleanProperty(String propertyName, boolean defaultValue, boolean disabledValue) { String fullPropertyName = SUBSYSTEM_PROPERTY_PREFIX + propertyName; return this.project.getProviders().gradleProperty(fullPropertyName) - .map(value -> { - try { - return Boolean.valueOf(value); - } catch (Exception e) { - throw new GradleException("Gradle Property " + fullPropertyName + " is not set to a boolean value: '" + value + "'"); - } - }) - .orElse(defaultValue); + .map(value -> { + try { + return Boolean.valueOf(value); + } catch (Exception e) { + throw new GradleException("Gradle Property " + fullPropertyName + " is not set to a boolean value: '" + value + "'"); + } + }).orElse(defaultValue); + } + + protected Provider getBooleanProperty(String propertyName) { + String fullPropertyName = SUBSYSTEM_PROPERTY_PREFIX + propertyName; + return this.project.getProviders().gradleProperty(fullPropertyName) + .map(value -> { + try { + return Boolean.valueOf(value); + } catch (Exception e) { + throw new GradleException("Gradle Property " + fullPropertyName + " is not set to a boolean value: '" + value + "'"); + } + }); } protected Provider> getSpaceSeparatedListProperty(String propertyName, List defaultValue) { diff --git a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java index 5924f9977..6dadd77c1 100644 --- a/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java +++ b/common/src/main/java/net/neoforged/gradle/common/extensions/subsystems/SubsystemsExtension.java @@ -1,9 +1,7 @@ package net.neoforged.gradle.common.extensions.subsystems; -import groovy.lang.Closure; import groovy.transform.Internal; import net.neoforged.gdi.ConfigurableDSLElement; -import net.neoforged.gradle.common.extensions.base.WithEnabledProperty; import net.neoforged.gradle.common.extensions.base.WithLocalProperties; import net.neoforged.gradle.common.extensions.base.WithPropertyLookup; import net.neoforged.gradle.dsl.common.extensions.subsystems.*; @@ -21,16 +19,17 @@ import static net.neoforged.gradle.dsl.common.util.Constants.*; -public abstract class SubsystemsExtension extends WithPropertyLookup implements ConfigurableDSLElement, Subsystems { +public abstract class SubsystemsExtension extends WithPropertyLookup implements ConfigurableDSLElement, Subsystems +{ private final Conventions conventions; - private final Parchment parchment; - private final Tools tools; + private final Parchment parchment; + private final Tools tools; private final Integration integration; - @Inject - public SubsystemsExtension(Project project) { + public SubsystemsExtension(Project project) + { super(project); this.integration = project.getObjects().newInstance(IntegrationExtensions.class, project); @@ -46,77 +45,95 @@ public SubsystemsExtension(Project project) { configureRenderDocDefaults(); } - private void configureRenderDocDefaults() { + private void configureRenderDocDefaults() + { RenderDoc devLogin = getRenderDoc(); devLogin.getConfigurationSuffix().convention( - getStringProperty("renderDoc.configurationSuffix", "RenderDocLocalOnly") + getStringProperty("renderDoc.configurationSuffix", "RenderDocLocalOnly") ); } - private void configureDevLoginDefaults() { + private void configureDevLoginDefaults() + { DevLogin devLogin = getDevLogin(); devLogin.getMainClass().convention( - getStringProperty("devLogin.mainClass", DEVLOGIN_MAIN_CLASS) + getStringProperty("devLogin.mainClass", DEVLOGIN_MAIN_CLASS) ); devLogin.getConfigurationSuffix().convention( - getStringProperty("devLogin.configurationSuffix", "DevLoginLocalOnly") + getStringProperty("devLogin.configurationSuffix", "DevLoginLocalOnly") ); } - private void configureToolsDefaults() { + private void configureToolsDefaults() + { Tools tools = getTools(); tools.getJST().convention( - getStringProperty("tools.jst", JST_TOOL_ARTIFACT) + getStringProperty("tools.jst", JST_TOOL_ARTIFACT) ); tools.getDevLogin().convention( - getStringProperty("tools.devLogin", DEVLOGIN_TOOL_ARTIFACT) + getStringProperty("tools.devLogin", DEVLOGIN_TOOL_ARTIFACT) ); tools.getBinaryPatcher().convention( - getStringProperty("tools.binaryPatcher", BINPARCHER_TOOL_ARTIFACT) + getStringProperty("tools.binaryPatcher", BINPARCHER_TOOL_ARTIFACT) ); tools.getAccessTransformer().convention( - getStringProperty("tools.accessTransformer", ACCESSTRANSFORMER_TOOL_ARTIFACT) + getStringProperty("tools.accessTransformer", ACCESSTRANSFORMER_TOOL_ARTIFACT) ); tools.getAutoRenamingTool().convention( - getStringProperty("tools.autoRenamingTool", FART_TOOL_ARTIFACT) + getStringProperty("tools.autoRenamingTool", FART_TOOL_ARTIFACT) ); tools.getInstallerTools().convention( - getStringProperty("tools.installerTools", INSTALLERTOOLS_TOOL_ARTIFACT) + getStringProperty("tools.installerTools", INSTALLERTOOLS_TOOL_ARTIFACT) ); tools.getJarSplitter().convention( - getStringProperty("tools.jarSplitter", JARSPLITTER_TOOL_ARTIFACT) + getStringProperty("tools.jarSplitter", JARSPLITTER_TOOL_ARTIFACT) ); tools.getDecompiler().convention( - getStringProperty("tools.decompiler", DECOMPILER_TOOL_ARTIFACT) + getStringProperty("tools.decompiler", DECOMPILER_TOOL_ARTIFACT) ); RenderDocTools renderDocTools = tools.getRenderDoc(); renderDocTools.getRenderDocPath().convention( - getDirectoryProperty("tools.renderDoc.path", getProject().getLayout().getBuildDirectory().dir("renderdoc")) + getDirectoryProperty("tools.renderDoc.path", getProject().getLayout().getBuildDirectory().dir("renderdoc")) ); renderDocTools.getRenderDocVersion().convention( - getStringProperty("tools.renderDoc.version", "1.33") + getStringProperty("tools.renderDoc.version", "1.33") ); renderDocTools.getRenderNurse().convention( - getStringProperty("tools.renderDoc.renderNurse", RENDERNURSE_TOOL_ARTIFACT) + getStringProperty("tools.renderDoc.renderNurse", RENDERNURSE_TOOL_ARTIFACT) ); } - private void configureDecompilerDefaults() { + private void configureDecompilerDefaults() + { Decompiler decompiler = getDecompiler(); decompiler.getMaxMemory().convention(getStringProperty("decompiler.maxMemory", "4g")); decompiler.getMaxThreads().convention(getStringProperty("decompiler.maxThreads", "0").map(Integer::parseUnsignedInt)); decompiler.getLogLevel().convention(getStringProperty("decompiler.logLevel", "ERROR").map(s -> { - try { + try + { return DecompilerLogLevel.valueOf(s.toUpperCase(Locale.ROOT)); - } catch (Exception e) { + } + catch (Exception e) + { throw new GradleException("Unknown DecompilerLogLevel: " + s + ". Available options: " + Arrays.toString(DecompilerLogLevel.values())); } })); decompiler.getJvmArgs().convention(getSpaceSeparatedListProperty("decompiler.jvmArgs", Collections.emptyList())); + decompiler.getIsDisabled().convention( + getBooleanProperty("decompiler.enabled") + .map(prop -> !prop) + .orElse( + this.project.getProviders() + .environmentVariable("CI") + .map(ciMode -> ciMode.toLowerCase(Locale.ROOT).trim().equals("true") || ciMode.toLowerCase(Locale.ROOT).trim().equals("1")) + ) + .orElse(false) + ); } - private void configureRecompilerDefaults() { + private void configureRecompilerDefaults() + { Recompiler recompiler = getRecompiler(); recompiler.getArgs().convention(getSpaceSeparatedListProperty("recompiler.args", Collections.emptyList())); recompiler.getJvmArgs().convention(getSpaceSeparatedListProperty("recompiler.jvmArgs", Collections.emptyList())); @@ -125,7 +142,8 @@ private void configureRecompilerDefaults() { recompiler.getType().convention(getStringProperty("recompiler.type", RecompilerType.getDefaultCompilerType().name()).map(RecompilerType::valueOf)); } - private void configureParchmentDefaults() { + private void configureParchmentDefaults() + { Parchment parchment = getParchment(); project.afterEvaluate(p -> { MavenArtifactRepository repo = p.getRepositories().maven(m -> { @@ -140,48 +158,55 @@ private void configureParchmentDefaults() { } @Override - public Integration getIntegration() { + public Integration getIntegration() + { return integration; } @Override - public Conventions getConventions() { + public Conventions getConventions() + { return conventions; } @Override - public Parchment getParchment() { + public Parchment getParchment() + { return parchment; } @Override - public Tools getTools() { + public Tools getTools() + { return tools; } - public static abstract class ParchmentExtensions extends WithLocalProperties implements Parchment { + public static abstract class ParchmentExtensions extends WithLocalProperties implements Parchment + { @Inject - public ParchmentExtensions(Project project) { + public ParchmentExtensions(Project project) + { super(project, "parchment"); getParchmentArtifact().convention( - getStringLocalProperty("parchmentArtifact", null) + getStringLocalProperty("parchmentArtifact", null) ); getConflictPrefix().convention("p_"); getMinecraftVersion().convention( - getStringLocalProperty("minecraftVersion", null) + getStringLocalProperty("minecraftVersion", null) ); getMappingsVersion().convention( - getStringLocalProperty("mappingsVersion", null) + getStringLocalProperty("mappingsVersion", null) ); getAddRepository().convention( - getBooleanLocalProperty("addRepository", true) + getBooleanLocalProperty("addRepository", true) ); } @Internal - public Provider getSelectedParchmentArtifact(String artifactMinecraftVersion) { + public Provider getSelectedParchmentArtifact(String artifactMinecraftVersion) + { return getParchmentArtifact().orElse(getMinecraftVersion().orElse(artifactMinecraftVersion) .zip(getMappingsVersion(), (minecraftVersion, mappingVersion) -> { return DEFAULT_PARCHMENT_GROUP diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/DefaultRuntime.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/DefaultRuntime.java index bde9ae8ff..46eec2ff8 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/DefaultRuntime.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/DefaultRuntime.java @@ -24,7 +24,7 @@ public abstract class DefaultRuntime extends JavaRuntimeTask implements Runtime public DefaultRuntime() { super(); - arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class, getProviderFactory()); + arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class); multiArguments = getObjectFactory().newInstance(RuntimeMultiArgumentsImpl.class, getProviderFactory()); //All of these taskOutputs belong to the MCP group diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RecompileSourceJar.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RecompileSourceJar.java index ea37400e6..e9314e356 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RecompileSourceJar.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RecompileSourceJar.java @@ -46,7 +46,7 @@ public abstract class RecompileSourceJar extends JavaCompile implements Runtime public RecompileSourceJar() { super(); - arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class, getProviderFactory()); + arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class); multiArguments = getObjectFactory().newInstance(RuntimeMultiArgumentsImpl.class, getProviderFactory()); this.javaVersion = getProject().getObjects().property(JavaLanguageVersion.class); diff --git a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RuntimeArgumentsImpl.java b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RuntimeArgumentsImpl.java index 5ac42aaea..5beccfd5f 100644 --- a/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RuntimeArgumentsImpl.java +++ b/common/src/main/java/net/neoforged/gradle/common/runtime/tasks/RuntimeArgumentsImpl.java @@ -5,81 +5,69 @@ import org.gradle.api.file.Directory; import org.gradle.api.file.RegularFile; import org.gradle.api.provider.Provider; -import org.gradle.api.provider.ProviderFactory; +import org.jetbrains.annotations.Nullable; -import javax.inject.Inject; import java.io.File; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; -public abstract class RuntimeArgumentsImpl implements RuntimeArguments { - - private final ProviderFactory providerFactory; - - @Inject - public RuntimeArgumentsImpl(ProviderFactory providerFactory) { - this.providerFactory = providerFactory; - } - - public Provider get(String key) { - return getSimple().zip(getFiles(), (simple, files) -> { - final Optional namedFile = files.stream().filter(f -> f.getName().equals(key)).findFirst(); - - if (simple.containsKey(key) && namedFile.isPresent()) { - throw new IllegalArgumentException("Cannot have both a simple and file argument for key: " + key); - } else if (simple.containsKey(key)) { - return simple.get(key); - } else { - return namedFile.map(file -> file.getFile().getAbsolutePath()).orElse(null); - } - }); +public abstract class RuntimeArgumentsImpl implements RuntimeArguments +{ + @Nullable + public Provider get(String key) + { + return + getFiles().map(files -> files.stream().filter(f -> f.getName().equals(key)).findFirst().orElse(null)) + .flatMap(NamedFileRef::getFile) + .map(File::getAbsolutePath) + .orElse(getSimple().getting(key)); } - - public Provider getOrDefault(String key, Provider defaultProvider) { - return get(key).orElse(defaultProvider); + + public Provider getOrDefault(String key, Provider defaultProvider) + { + final Provider getResult = get(key); + if (getResult == null) + { + return defaultProvider; + } + + return getResult.orElse(defaultProvider); } - - public Provider>> asMap() { + + public Provider>> asMap() + { final Provider>> simpleProvider = - getSimple().map(Map::keySet) - .map(keySet -> keySet.stream().collect(Collectors.toMap(key -> key, key -> getSimple().getting(key)))); - + getSimple().map(Map::keySet) + .map(keySet -> keySet.stream().collect(Collectors.toMap(key -> key, key -> getSimple().getting(key)))); + final Provider>> filesProvider = - getFiles().map(files -> files.stream() - .collect(Collectors.toMap( - NamedFileRef::getName, - namedFiles -> providerFactory.provider(() -> namedFiles.getFile().getAbsolutePath()), - (a, b) -> b, - HashMap::new - ))); - + getFiles().map(files -> files.stream() + .collect(Collectors.toMap( + NamedFileRef::getName, + namedFiles -> namedFiles.getFile().map(File::getAbsolutePath), + (a, b) -> b, + HashMap::new + ))); + return simpleProvider.zip(filesProvider, (simple, files) -> ImmutableMap.>builder().putAll(simple).putAll(files).build()); } - + @Override - public void putFile(String input, Provider fileProvider) { - getFiles().add(fileProvider.map(file -> new NamedFile(input, file))); + public void putFile(String input, Provider fileProvider) + { + getFiles().add(new NamedFile(input, fileProvider)); } - + @Override - public void putRegularFile(String input, Provider fileProvider) { + public void putRegularFile(String input, Provider fileProvider) + { getFiles().add(new NamedRegularFile(input, fileProvider)); } - - @Override - public void putDirectoryFile(String input, Provider fileProvider) { - getFiles().add(fileProvider.map(file -> new NamedDirectoryFile(input, file))); - } - - @Override - public void putDirectory(String input, Provider fileProvider) { - getFiles().add(new NamedDirectory(input, fileProvider)); - } - + @Override - public void put(String input, Provider stringProvider) { + public void put(String input, Provider stringProvider) + { getSimple().put(input, stringProvider); } } diff --git a/common/src/main/java/net/neoforged/gradle/common/tasks/PotentiallySignJar.java b/common/src/main/java/net/neoforged/gradle/common/tasks/PotentiallySignJar.java deleted file mode 100644 index 3afcd0470..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/tasks/PotentiallySignJar.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.gradle.common.tasks; - -import com.google.common.collect.Maps; -import groovy.lang.Closure; -import net.neoforged.gradle.dsl.common.tasks.WithOutput; -import net.neoforged.gradle.dsl.common.tasks.WithWorkspace; -import net.neoforged.gradle.util.FileUtils; -import net.neoforged.gradle.util.ZipBuildingFileTreeVisitor; -import org.gradle.api.DefaultTask; -import org.gradle.api.NonNullApi; -import org.gradle.api.file.FileTree; -import org.gradle.api.file.FileTreeElement; -import org.gradle.api.file.RegularFile; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.specs.Spec; -import org.gradle.api.tasks.*; -import org.gradle.api.tasks.util.PatternFilterable; -import org.gradle.api.tasks.util.PatternSet; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.zip.ZipOutputStream; - -@CacheableTask -@NonNullApi -public abstract class PotentiallySignJar extends DefaultTask implements PatternFilterable, WithOutput, WithWorkspace { - private final PatternSet patternSet = new PatternSet(); - - public PotentiallySignJar() { - getOutputFileName().convention(getInput().map(RegularFile::getAsFile).map(file -> FileUtils.postFixClassifier(file, "signed"))); - getOutput().convention(getOutputFileName().flatMap(fileName -> getLayout().getBuildDirectory().dir("libs").map(libsDir -> libsDir.file(fileName)))); - } - - @TaskAction - public void doTask() throws IOException { - final Map> ignoredStuff = Maps.newHashMap(); - - - File input = getInput().get().getAsFile(); - File output = ensureFileWorkspaceReady(getOutput()); - - if (!getAlias().isPresent() || !getStorePass().isPresent()) { - org.apache.commons.io.FileUtils.copyFile(input, output); - return; - } - - File toSign = ensureFileWorkspaceReady(new File(getTemporaryDir(), input.getName() + ".unsigned.tmp")); - File signed = ensureFileWorkspaceReady(new File(getTemporaryDir(), input.getName() + ".signed.tmp")); - - // load in input jar, and create temp jar - processInputJar(input, toSign); - - // SIGN! - Map map = Maps.newHashMap(); - map.put("alias", getAlias().get()); - map.put("storePass", getStorePass().get()); - map.put("jar", toSign.getAbsolutePath()); - map.put("signedJar", signed.getAbsolutePath()); - - if (getKeyPass().isPresent()) map.put("keypass", getKeyPass().get()); - if (getKeyStore().isPresent()) map.put("keyStore", getKeyStore().get()); - - getAnt().invokeMethod("signjar", map); - - // write out - writeOutputJar(signed, input, output); - } - - private void processInputJar(File inputJar, File toSign) throws IOException { - final FileTree inputFileTree = getArchiveOperations().zipTree(inputJar); - try (OutputStream toSignOutputStream = new FileOutputStream(toSign); ZipOutputStream toSignZipOutputStream = new ZipOutputStream(toSignOutputStream)) { - - ZipBuildingFileTreeVisitor toSignBuilder = new ZipBuildingFileTreeVisitor(toSignZipOutputStream); - inputFileTree.matching(patternSet).visit(toSignBuilder); - } - } - - private void writeOutputJar(File signedJar, File inputJar, File outputJar) throws IOException { - final FileTree inputFileTree = getArchiveOperations().zipTree(inputJar); - final FileTree signedFileTree = getArchiveOperations().zipTree(signedJar); - try (OutputStream outputStream = new FileOutputStream(outputJar); ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { - - ZipBuildingFileTreeVisitor builder = new ZipBuildingFileTreeVisitor(zipOutputStream); - signedFileTree.visit(builder); - inputFileTree.minus(inputFileTree.matching(patternSet)).getAsFileTree().visit(builder); - } - } - - @InputFile - @Optional - @PathSensitive(PathSensitivity.NONE) - public abstract RegularFileProperty getInput(); - - @Input - @Optional - public abstract Property getAlias(); - - @Input - @Optional - public abstract Property getStorePass(); - - @Input - @Optional - public abstract Property getKeyPass(); - - @Input - @Optional - public abstract Property getKeyStore(); - - @Override - public PatternFilterable exclude(String... arg0) { - return patternSet.exclude(arg0); - } - - @Override - public PatternFilterable exclude(Iterable arg0) { - return patternSet.exclude(arg0); - } - - @Override - public PatternFilterable exclude(Spec arg0) { - return patternSet.exclude(arg0); - } - - @Override - public PatternFilterable exclude(Closure arg0) { - return patternSet.exclude(arg0); - } - - @Internal - @Override - public Set getExcludes() { - return patternSet.getExcludes(); - } - - @Internal - @Override - public Set getIncludes() { - return patternSet.getIncludes(); - } - - @Override - public PatternFilterable include(String... arg0) { - return patternSet.include(arg0); - } - - @Override - public PatternFilterable include(Iterable arg0) { - return patternSet.include(arg0); - } - - @Override - public PatternFilterable include(Spec arg0) { - return patternSet.include(arg0); - } - - @Override - public PatternFilterable include(Closure arg0) { - return patternSet.include(arg0); - } - - @Override - public PatternFilterable setExcludes(Iterable arg0) { - return patternSet.setExcludes(arg0); - } - - @Override - public PatternFilterable setIncludes(Iterable arg0) { - return patternSet.setIncludes(arg0); - } -} diff --git a/common/src/main/java/net/neoforged/gradle/common/tasks/RawAndSourceCombiner.java b/common/src/main/java/net/neoforged/gradle/common/tasks/RawAndSourceCombiner.java deleted file mode 100644 index e196b0f90..000000000 --- a/common/src/main/java/net/neoforged/gradle/common/tasks/RawAndSourceCombiner.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.neoforged.gradle.common.tasks; - -import com.google.common.io.Files; -import net.neoforged.gradle.dsl.common.tasks.NeoGradleBase; -import net.neoforged.gradle.dsl.common.tasks.WithOutput; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.tasks.*; - -import java.io.File; -import java.io.IOException; - -public abstract class RawAndSourceCombiner extends NeoGradleBase implements WithOutput { - - @TaskAction - public void doCombine() { - final File rawJarOutput = ensureFileWorkspaceReady(getOutput()); - final File sourceJarOutput = ensureFileWorkspaceReady(getSourceJarOutput()); - - final File rawJarInput = getInput().getAsFile().get(); - final File sourceJarInput = getSourceJarInput().getAsFile().get(); - - copy(rawJarInput, rawJarOutput); - copy(sourceJarInput, sourceJarOutput); - } - - private static void copy(final File input, final File output) { - try { - Files.copy(input, output); - } catch (IOException e) { - throw new RuntimeException(String.format("Failed to copy: %s to outputs: %s", input.getAbsolutePath(), output.getAbsolutePath()), e); - } - } - - @InputFile - @PathSensitive(PathSensitivity.NONE) - public abstract RegularFileProperty getInput(); - - @InputFile - @PathSensitive(PathSensitivity.NONE) - public abstract RegularFileProperty getSourceJarInput(); - - @OutputFile - public abstract RegularFileProperty getSourceJarOutput(); -} diff --git a/common/src/main/java/net/neoforged/gradle/common/tasks/StripFinalFromParametersTask.java b/common/src/main/java/net/neoforged/gradle/common/tasks/StripFinalFromParametersTask.java new file mode 100644 index 000000000..dc8a4ec53 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/tasks/StripFinalFromParametersTask.java @@ -0,0 +1,75 @@ +package net.neoforged.gradle.common.tasks; + +import net.neoforged.gradle.common.runtime.tasks.DefaultRuntime; +import net.neoforged.gradle.common.services.caching.CachedExecutionService; +import net.neoforged.gradle.common.services.caching.jobs.ICacheableJob; +import net.neoforged.gradle.dsl.common.tasks.WithOperations; +import net.neoforged.gradle.dsl.common.tasks.WithOutput; +import net.neoforged.gradle.dsl.common.tasks.WithWorkspace; +import net.neoforged.gradle.dsl.common.tasks.specifications.InputFileSpecification; +import net.neoforged.gradle.util.ClassVisitingFileTreeVisitor; +import org.gradle.api.provider.Property; +import org.gradle.api.services.ServiceReference; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.TaskAction; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipOutputStream; + +@CacheableTask +public abstract class StripFinalFromParametersTask extends DefaultRuntime implements WithOutput, InputFileSpecification, WithOperations, WithWorkspace +{ + public StripFinalFromParametersTask() { + super(); + } + + @ServiceReference(CachedExecutionService.NAME) + public abstract Property getCacheService(); + + @TaskAction + public void stripFinal() throws IOException { + getCacheService().get() + .cached(this, + ICacheableJob.Default.file(this::doStripFinal, getOutput()) + ) + .execute(); + } + + private void doStripFinal() throws IOException { + var input = getArchiveOperations().zipTree(getInput()); + try(final FileOutputStream fos = new FileOutputStream(ensureFileWorkspaceReady(getOutput())); + final ZipOutputStream zos = new ZipOutputStream(fos)) { + final ClassVisitingFileTreeVisitor visitor = new ClassVisitingFileTreeVisitor(zos, StripFinalClassVisitor::new); + input.visit(visitor); + } + } + + private static class StripFinalClassVisitor extends ClassVisitor { + public StripFinalClassVisitor(int api, ClassVisitor classVisitor) { + super(api, classVisitor); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + return new StripFinalParameterVisitor(api, mv); + } + } + + private static class StripFinalParameterVisitor extends MethodVisitor { + public StripFinalParameterVisitor(int api, MethodVisitor methodVisitor) { + super(api, methodVisitor); + } + + @Override + public void visitParameter(String name, int access) { + // Remove ACC_FINAL from parameter access flags + int newAccess = access & ~Opcodes.ACC_FINAL; + super.visitParameter(name, newAccess); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/neoforged/gradle/common/util/ListUtils.java b/common/src/main/java/net/neoforged/gradle/common/util/ListUtils.java new file mode 100644 index 000000000..596b735c6 --- /dev/null +++ b/common/src/main/java/net/neoforged/gradle/common/util/ListUtils.java @@ -0,0 +1,41 @@ +package net.neoforged.gradle.common.util; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; + +public class ListUtils +{ + + public static int removeIfAndReturnIndex(List collection, Predicate filter) { + Objects.requireNonNull(filter); + Objects.requireNonNull(collection); + + int index = -1; + for (int i = 0; i < collection.size(); i++) + { + if (filter.test(collection.get(i))) { + index = i; + break; + } + } + + if (index != -1) { + collection.remove(index); + } + + return index; + } + + public static @Nullable E find(List collection, Predicate filter) { + for (final E e : collection) + { + if (filter.test(e)) + return e; + } + + return null; + } +} diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy index bf4a683e5..86e61fb91 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/extensions/subsystems/Decompiler.groovy @@ -49,4 +49,16 @@ interface Decompiler extends ConfigurableDSLElement { @DSLProperty ListProperty getJvmArgs(); + /** + * Indicates whether the decompiler is enabled or not. + *

+ * When running in CI mode the decompiler will be disabled as long as no sources artifact are requested manually. + *

+ * + * @return True for disabled, false when enabled. + */ + @Input + @Optional + @DSLProperty + Property getIsDisabled(); } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectory.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectory.groovy index cc454a2a0..b1578a3ed 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectory.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectory.groovy @@ -26,7 +26,7 @@ class NamedDirectory implements NamedFileRef { @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) - File getFile() { - return file.get().asFile + Provider getFile() { + return file.map { it.asFile } } } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectoryFile.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectoryFile.groovy deleted file mode 100644 index e4d5f79b6..000000000 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedDirectoryFile.groovy +++ /dev/null @@ -1,31 +0,0 @@ -package net.neoforged.gradle.dsl.common.runtime.tasks - -import groovy.transform.CompileStatic -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity - -@CompileStatic -class NamedDirectoryFile implements NamedFileRef { - - private final String name; - private final File file; - - NamedDirectoryFile(String name, File file) { - this.name = name - this.file = file - } - - @Input - String getName() { - return name - } - - @InputDirectory - @PathSensitive(PathSensitivity.RELATIVE) - File getFile() { - return file - } -} diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFile.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFile.groovy index 42163bbfc..83973723f 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFile.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFile.groovy @@ -1,6 +1,7 @@ package net.neoforged.gradle.dsl.common.runtime.tasks import groovy.transform.CompileStatic +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.PathSensitive @@ -10,9 +11,9 @@ import org.gradle.api.tasks.PathSensitivity class NamedFile implements NamedFileRef { private final String name; - private final File file; + private final Provider file; - NamedFile(String name, File file) { + NamedFile(String name, Provider file) { this.name = name this.file = file } @@ -24,7 +25,7 @@ class NamedFile implements NamedFileRef { @InputFile @PathSensitive(PathSensitivity.RELATIVE) - File getFile() { + Provider getFile() { return file } } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFileRef.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFileRef.groovy index 24d7bce1a..0d97ae9d1 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFileRef.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedFileRef.groovy @@ -1,5 +1,6 @@ package net.neoforged.gradle.dsl.common.runtime.tasks +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.PathSensitive @@ -12,5 +13,5 @@ interface NamedFileRef { @InputFile @PathSensitive(PathSensitivity.NONE) - File getFile() + Provider getFile() } \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedRegularFile.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedRegularFile.groovy index 4a2cc1a51..70004955d 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedRegularFile.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/NamedRegularFile.groovy @@ -26,7 +26,7 @@ class NamedRegularFile implements NamedFileRef { @InputFile @PathSensitive(PathSensitivity.RELATIVE) - File getFile() { - return file.get().asFile + Provider getFile() { + return file.map { it.asFile } } } diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/RuntimeArguments.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/RuntimeArguments.groovy index 52742d89e..184b53941 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/RuntimeArguments.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/RuntimeArguments.groovy @@ -11,6 +11,7 @@ import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Provider import org.gradle.api.tasks.Input import org.gradle.api.tasks.Nested +import org.jetbrains.annotations.Nullable @CompileStatic interface RuntimeArguments extends ConfigurableDSLElement { @@ -23,6 +24,7 @@ interface RuntimeArguments extends ConfigurableDSLElement { @Nested abstract ListProperty getFiles(); + @Nullable Provider get(String key) Provider getOrDefault(String key, Provider defaultProvider) @@ -33,9 +35,5 @@ interface RuntimeArguments extends ConfigurableDSLElement { void putRegularFile(String input, Provider fileProvider); - void putDirectoryFile(String input, Provider fileProvider); - - void putDirectory(String input, Provider fileProvider); - void put(String input, Provider stringProvider); } \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/tree/AndTaskTreeAdapter.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/tree/AndTaskTreeAdapter.groovy index a15a1d2e0..8057c5723 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/tree/AndTaskTreeAdapter.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/runtime/tasks/tree/AndTaskTreeAdapter.groovy @@ -31,7 +31,7 @@ class AndTaskTreeAdapter implements TaskTreeAdapter { if (currentAdapted != null) dependentTaskConfigurationHandler.accept(currentAdapted); - final TaskProvider afterAdapted = right.adapt(definition, currentAdapted, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler); + final TaskProvider afterAdapted = right.adapt(definition, currentAdapted != null ? currentAdapted : previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler); if (currentAdapted != null && afterAdapted == null) return currentAdapted; diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/InputFileSpecification.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/InputFileSpecification.groovy index b19aa60ec..7c8296902 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/InputFileSpecification.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/tasks/specifications/InputFileSpecification.groovy @@ -5,6 +5,8 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity /** * Represents an object with an input file. @@ -20,5 +22,6 @@ trait InputFileSpecification implements ProjectSpecification { @DSLProperty @InputFile @Optional + @PathSensitive(PathSensitivity.NONE) abstract RegularFileProperty getInput(); } \ No newline at end of file diff --git a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy index 8fd30d688..4411f90e2 100644 --- a/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy +++ b/dsl/common/src/main/groovy/net/neoforged/gradle/dsl/common/util/Constants.groovy @@ -14,7 +14,7 @@ class Constants { public static final String BINPARCHER_TOOL_ARTIFACT = "net.neoforged.installertools:binarypatcher:2.1.7:fatjar" public static final String ACCESSTRANSFORMER_TOOL_ARTIFACT = "net.neoforged.accesstransformers:at-cli:11.0.2:fatjar" public static final String FART_TOOL_ARTIFACT = "net.neoforged:AutoRenamingTool:2.0.4:all" - public static final String INSTALLERTOOLS_TOOL_ARTIFACT = "net.neoforged.installertools:installertools:2.1.7" + public static final String INSTALLERTOOLS_TOOL_ARTIFACT = "net.neoforged.installertools:installertools:4.0.6:fatjar" public static final String JARSPLITTER_TOOL_ARTIFACT = "net.neoforged.installertools:jarsplitter:2.1.7" public static final String DECOMPILER_TOOL_ARTIFACT = "org.vineflower:vineflower:1.10.1" diff --git a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/configuration/NeoFormConfigConfigurationSpecV1.java b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/configuration/NeoFormConfigConfigurationSpecV1.java index 79a765287..a76c917fd 100644 --- a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/configuration/NeoFormConfigConfigurationSpecV1.java +++ b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/configuration/NeoFormConfigConfigurationSpecV1.java @@ -104,11 +104,6 @@ public List getSteps(String side) { return ret == null ? Collections.emptyList() : ret; } - @Nullable - public Function getFunction(String name) { - return functions == null ? null : functions.get(name); - } - @Nested @Optional public Map getFunctions() { @@ -133,7 +128,7 @@ public static class Step { @Nullable private final Map values; - private Step(String type, String name, @Nullable Map values) { + public Step(String type, String name, @Nullable Map values) { this.type = type; this.name = name; this.values = values; @@ -184,6 +179,26 @@ public static class Function { @Nullable protected List jvmargs; + public Function() + { + } + + public Function(final String version, @Nullable final List args, @Nullable final List jvmargs) + { + this.version = version; + this.args = args; + this.jvmargs = jvmargs; + this.repo = null; + } + + public Function(final String version, @Nullable final List args) + { + this.version = version; + this.args = args; + this.jvmargs = List.of(); + this.repo = null; + } + @Input public String getVersion() { return version; diff --git a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/definition/NeoFormDefinition.groovy b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/definition/NeoFormDefinition.groovy index b606e5deb..7f9097d81 100644 --- a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/definition/NeoFormDefinition.groovy +++ b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/definition/NeoFormDefinition.groovy @@ -2,8 +2,11 @@ package net.neoforged.gradle.dsl.neoform.runtime.definition import groovy.transform.CompileStatic; import net.neoforged.gradle.dsl.common.runtime.definition.Definition +import net.neoforged.gradle.dsl.common.tasks.WithOutput +import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1 import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2 -import net.neoforged.gradle.dsl.neoform.runtime.specification.NeoFormSpecification; +import net.neoforged.gradle.dsl.neoform.runtime.specification.NeoFormSpecification +import org.gradle.api.tasks.TaskProvider; import org.jetbrains.annotations.NotNull /** @@ -20,4 +23,9 @@ interface NeoFormDefinition extends Definition> getTaskOutputsByStepName(); @NotNull + Map>> getTaskInputsByStepName(); + @NotNull + List getBakedSteps(); @NotNull + Map getBakedFunctions(); } diff --git a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/specification/NeoFormSpecification.groovy b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/specification/NeoFormSpecification.groovy index 448afde4e..de1eac310 100644 --- a/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/specification/NeoFormSpecification.groovy +++ b/dsl/neoform/src/main/groovy/net/neoforged/gradle/dsl/neoform/runtime/specification/NeoFormSpecification.groovy @@ -2,8 +2,13 @@ package net.neoforged.gradle.dsl.neoform.runtime.specification import groovy.transform.CompileStatic import net.neoforged.gradle.dsl.common.runtime.spec.Specification +import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1 import org.gradle.api.file.FileCollection -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable + +import java.util.function.BiConsumer +import java.util.function.Consumer; /** * Defines an NeoForm specific runtime specification. @@ -29,6 +34,15 @@ interface NeoFormSpecification extends Specification { @NotNull FileCollection getAdditionalRecompileDependencies(); + /** + * Returns the task name of the task to use as an output as a replacement of the task with the given name. + * + * @param taskName The task name to check for. + * @return The task name of the task to replace it with, null if not replaced. + */ + @Nullable + String skipTaskWith(final String taskName); BiConsumer, Map> getMutator ( ) ; + /** * Defines a builder for an {@link NeoFormSpecification}. * @@ -62,5 +76,26 @@ interface NeoFormSpecification extends Specification { */ @NotNull B withAdditionalDependencies(@NotNull final FileCollection files); + + /** + * Configures this runtime to skip the given task. + *

+ * The individual behaviour of the skipping is up to the runtime implementation. + *

+ * + * @param task The task name to skip. + * @param outputSource The task to take the output of as the output of the source. + * @return The builder. + */ + @NotNull + B withSkippedTask(@NotNull final String task, @NotNull final String outputSource); + + /** + * Registers a new mutator that can alter the step list and known functions. + * + * @param mutator The mutator + * @return The builder + */ + B withStepsMutator(@NotNull final BiConsumer, Map> mutator) } } diff --git a/dsl/userdev/src/main/groovy/net/neoforged/gradle/dsl/userdev/configurations/UserdevProfile.groovy b/dsl/userdev/src/main/groovy/net/neoforged/gradle/dsl/userdev/configurations/UserdevProfile.groovy index f58d2e3d3..6d47eaa57 100644 --- a/dsl/userdev/src/main/groovy/net/neoforged/gradle/dsl/userdev/configurations/UserdevProfile.groovy +++ b/dsl/userdev/src/main/groovy/net/neoforged/gradle/dsl/userdev/configurations/UserdevProfile.groovy @@ -261,6 +261,11 @@ abstract class UserdevProfile implements ConfigurableDSLElement @Optional abstract Property getIsNoLegacyClasspath(); + @Input + @DSLProperty + @Optional + abstract Property getUsesCombinedBinaryPatches(); + @CompileStatic static class Serializer implements JsonSerializer, JsonDeserializer { @@ -280,6 +285,7 @@ abstract class UserdevProfile implements ConfigurableDSLElement final Features instance = objectFactory.newInstance(Features.class) deserializeBool(instance.isNoLegacyClasspath, object, "noLegacyClasspath") + deserializeBool(instance.usesCombinedBinaryPatches, object, "combinedBinaryPatches") return instance } @@ -289,6 +295,7 @@ abstract class UserdevProfile implements ConfigurableDSLElement final JsonObject object = new JsonObject(); serializeBool(features.isNoLegacyClasspath, object, "noLegacyClasspath") + deserializeBool(features.usesCombinedBinaryPatches, object, "combinedBinaryPatches") return object } diff --git a/gradle.properties b/gradle.properties index f3879c2e6..b9476df5c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,6 +28,7 @@ gradle_idea_extension_version=1.2 groovy_dsl_improver_version=2.0.0 eclipse_launch_configs_version=0.1.3 vscode_launch_configs_version=1.0.8 +asm_version=9.9 #Test dependencies junit_version=5.9.2 @@ -36,6 +37,6 @@ spock_version=2.1 spock_groovy_version=3.0 mockito_version=4.11.0 jimfs_version=1.2 -trainingwheels_version=2.0.2 +trainingwheels_version=2.0.3 githubCiTesting=true \ No newline at end of file diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/naming/NeoFormOfficialNamingChannelConfigurator.java b/neoform/src/main/java/net/neoforged/gradle/neoform/naming/NeoFormOfficialNamingChannelConfigurator.java index 0f9688c9f..9f7e0f0f6 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/naming/NeoFormOfficialNamingChannelConfigurator.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/naming/NeoFormOfficialNamingChannelConfigurator.java @@ -81,7 +81,7 @@ private TaskProvider adaptApplySourceMappingsTask(@NotNull fi final NeoFormRuntimeDefinition neoformRuntimeDefinition = runtimeDefinition.get(); final Provider neoformMappings = context.getProject().getProviders().of(MappingsFileValueSource.class, spec -> { - spec.getParameters().getNeoFormArchive().from(context.getProject().fileTree(neoformRuntimeDefinition.getSpecification().getNeoFormArchive())); + spec.getParameters().getNeoFormArchive().from(context.getProject().fileTree(neoformRuntimeDefinition.getSpecification().getUnpackedNeoFormArchive())); spec.getParameters().getMappingsFilePath().set(neoformRuntimeDefinition.getNeoFormConfig().getData("mappings")); }); @@ -152,7 +152,7 @@ private static TaskProvider createCombinedMappingsFor(@NotNul final NeoFormRuntimeDefinition neoformRuntimeDefinition = runtimeDefinition.get(); final Provider neoformMappings = context.getProject().getProviders().of(MappingsFileValueSource.class, spec -> { - spec.getParameters().getNeoFormArchive().from(context.getProject().fileTree(neoformRuntimeDefinition.getSpecification().getNeoFormArchive())); + spec.getParameters().getNeoFormArchive().from(context.getProject().fileTree(neoformRuntimeDefinition.getSpecification().getUnpackedNeoFormArchive())); spec.getParameters().getMappingsFilePath().set(neoformRuntimeDefinition.getNeoFormConfig().getData("mappings")); }); diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java index 7c19ba0f2..500470b81 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/definition/NeoFormRuntimeDefinition.java @@ -1,5 +1,6 @@ package net.neoforged.gradle.neoform.runtime.definition; +import com.google.common.collect.Maps; import net.neoforged.gradle.common.runs.run.RunImpl; import net.neoforged.gradle.common.runtime.definition.CommonRuntimeDefinition; import net.neoforged.gradle.common.runtime.tasks.DownloadAssets; @@ -7,9 +8,9 @@ import net.neoforged.gradle.common.tasks.ArtifactFromOutput; import net.neoforged.gradle.common.util.VersionJson; import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime; -import net.neoforged.gradle.dsl.common.tasks.ArtifactProvider; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.util.GameArtifact; +import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1; import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2; import net.neoforged.gradle.dsl.neoform.runtime.definition.NeoFormDefinition; import net.neoforged.gradle.neoform.runtime.specification.NeoFormRuntimeSpecification; @@ -20,29 +21,37 @@ import org.jetbrains.annotations.NotNull; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; /** * Represents a configured and registered runtime for Mcp. */ -public class NeoFormRuntimeDefinition extends CommonRuntimeDefinition implements NeoFormDefinition { - private final NeoFormConfigConfigurationSpecV2 neoform; - - private final TaskProvider assetsTaskProvider; - private final TaskProvider nativesTaskProvider; - - public NeoFormRuntimeDefinition(@NotNull NeoFormRuntimeSpecification specification, - @NotNull LinkedHashMap> taskOutputs, - @NotNull TaskProvider sourceJarTask, - @NotNull TaskProvider rawJarTask, - @NotNull Map> gameArtifactProvidingTasks, - @NotNull Configuration minecraftDependenciesConfiguration, - @NotNull Consumer> associatedTaskConsumer, - @NotNull Provider versionJson, - @NotNull NeoFormConfigConfigurationSpecV2 neoform, - @NotNull TaskProvider assetsTaskProvider, - @NotNull TaskProvider nativesTaskProvider) { +public class NeoFormRuntimeDefinition extends CommonRuntimeDefinition implements NeoFormDefinition +{ + private final NeoFormConfigConfigurationSpecV2 neoform; + private final TaskProvider assetsTaskProvider; + private final TaskProvider nativesTaskProvider; + private final Map> taskOutputsByStepName = Maps.newHashMap(); + private final Map>> taskInputsByStepName = Maps.newHashMap(); + private List bakedSteps = List.of(); + private Map bakedFunctions = Maps.newHashMap(); + + public NeoFormRuntimeDefinition( + @NotNull NeoFormRuntimeSpecification specification, + @NotNull LinkedHashMap> taskOutputs, + @NotNull TaskProvider sourceJarTask, + @NotNull TaskProvider rawJarTask, + @NotNull Map> gameArtifactProvidingTasks, + @NotNull Configuration minecraftDependenciesConfiguration, + @NotNull Consumer> associatedTaskConsumer, + @NotNull Provider versionJson, + @NotNull NeoFormConfigConfigurationSpecV2 neoform, + @NotNull TaskProvider assetsTaskProvider, + @NotNull TaskProvider nativesTaskProvider) + { super(specification, taskOutputs, sourceJarTask, rawJarTask, gameArtifactProvidingTasks, minecraftDependenciesConfiguration, associatedTaskConsumer, versionJson); this.neoform = neoform; this.assetsTaskProvider = assetsTaskProvider; @@ -53,38 +62,53 @@ public NeoFormRuntimeDefinition(@NotNull NeoFormRuntimeSpecification specificati @Override @NotNull - public NeoFormConfigConfigurationSpecV2 getNeoFormConfig() { + public NeoFormConfigConfigurationSpecV2 getNeoFormConfig() + { return neoform; } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof NeoFormRuntimeDefinition that)) return false; - if (!super.equals(o)) return false; + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof NeoFormRuntimeDefinition that)) + { + return false; + } + if (!super.equals(o)) + { + return false; + } return neoform.equals(that.neoform); } @Override - public int hashCode() { + public int hashCode() + { int result = super.hashCode(); result = 31 * result + neoform.hashCode(); return result; } @Override - public @NotNull TaskProvider getAssets() { + public @NotNull TaskProvider getAssets() + { return assetsTaskProvider; } @Override - public @NotNull TaskProvider getNatives() { + public @NotNull TaskProvider getNatives() + { return nativesTaskProvider; } @Override - public void buildRunInterpolationData(RunImpl run, MapProperty interpolationData) { + public void buildRunInterpolationData(RunImpl run, MapProperty interpolationData) + { super.buildRunInterpolationData(run, interpolationData); interpolationData.put("mcp_version", neoform.getVersion()); // NeoForge still references this in the environment variable MCP_MAPPINGS, which is unused since 1.20.2 @@ -94,7 +118,38 @@ public void buildRunInterpolationData(RunImpl run, MapProperty i @NotNull @Override - public TaskProvider getListLibrariesTaskProvider() { + public TaskProvider getListLibrariesTaskProvider() + { return getTask("listLibraries"); } + + @Override + public @NotNull Map> getTaskOutputsByStepName() + { + return this.taskOutputsByStepName; + } + + @Override + public @NotNull Map>> getTaskInputsByStepName() + { + return this.taskInputsByStepName; + } + + public void setBakedStepsAndFunctions(final List steps, final Map functions) + { + this.bakedSteps = List.copyOf(steps); + this.bakedFunctions = Maps.newHashMap(functions); + } + + @Override + public @NotNull List getBakedSteps() + { + return bakedSteps; + } + + @Override + public @NotNull Map getBakedFunctions() + { + return bakedFunctions; + } } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java index e06e99638..6527f90dc 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/extensions/NeoFormRuntimeExtension.java @@ -14,7 +14,9 @@ import net.neoforged.gradle.dsl.common.extensions.Mappings; import net.neoforged.gradle.dsl.common.extensions.Minecraft; import net.neoforged.gradle.dsl.common.extensions.MinecraftArtifactCache; -import net.neoforged.gradle.dsl.common.extensions.subsystems.*; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Decompiler; +import net.neoforged.gradle.dsl.common.extensions.subsystems.DecompilerLogLevel; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime; import net.neoforged.gradle.dsl.common.runtime.tasks.RuntimeArguments; import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter; @@ -28,7 +30,10 @@ import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2; import net.neoforged.gradle.neoform.runtime.definition.NeoFormRuntimeDefinition; import net.neoforged.gradle.neoform.runtime.specification.NeoFormRuntimeSpecification; -import net.neoforged.gradle.neoform.runtime.tasks.*; +import net.neoforged.gradle.neoform.runtime.tasks.InjectFromFileTreeSource; +import net.neoforged.gradle.neoform.runtime.tasks.InjectZipContent; +import net.neoforged.gradle.neoform.runtime.tasks.Patch; +import net.neoforged.gradle.neoform.runtime.tasks.StripJar; import net.neoforged.gradle.neoform.util.NeoFormRuntimeConstants; import net.neoforged.gradle.neoform.util.NeoFormRuntimeUtils; import net.neoforged.gradle.util.TransformerUtils; @@ -46,41 +51,78 @@ import java.util.stream.Collectors; @SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "unused"}) // API Design -public abstract class NeoFormRuntimeExtension extends CommonRuntimeExtension implements ConfigurableDSLElement { +public abstract class NeoFormRuntimeExtension extends CommonRuntimeExtension + implements ConfigurableDSLElement +{ private static final Set DISABLED_STEPS = Sets.newHashSet("downloadManifest", "downloadJson"); @javax.inject.Inject - public NeoFormRuntimeExtension(Project project) { + public NeoFormRuntimeExtension(Project project) + { super(project); } - private static void configureMcpRuntimeTaskWithDefaults(NeoFormRuntimeSpecification spec, File neoFormDirectory, Map symbolicDataSources, LinkedHashMap> tasks, NeoFormConfigConfigurationSpecV1.Step step, Runtime neoFormRuntimeTask, Optional> alternativeInputProvider) { + public static void configureMcpRuntimeTaskWithDefaults( + final NeoFormRuntimeDefinition definition, + final Runtime neoFormRuntimeTask, + final NeoFormConfigConfigurationSpecV1.Step step + ) { + final NeoFormRuntimeSpecification spec = definition.getSpecification(); + configureMcpRuntimeTaskWithDefaults( + spec, + spec.getProject().getLayout().getBuildDirectory().dir(String.format("neoForm/%s", spec.getIdentifier())).get().getAsFile(), + buildDataFilesMap(definition.getNeoFormConfig(), spec.getDistribution()), + definition.getTasks(), + step, + neoFormRuntimeTask, + Optional.empty() + ); + } + + public static void configureMcpRuntimeTaskWithDefaults( + NeoFormRuntimeSpecification spec, + File neoFormDirectory, + Map symbolicDataSources, + LinkedHashMap> tasks, + NeoFormConfigConfigurationSpecV1.Step step, + Runtime neoFormRuntimeTask, + Optional> alternativeInputProvider) + { buildArguments(neoFormRuntimeTask.getArguments(), spec, step, tasks, neoFormRuntimeTask, alternativeInputProvider); configureCommonRuntimeTaskParameters(neoFormRuntimeTask, symbolicDataSources, step.getName(), spec, neoFormDirectory); - neoFormRuntimeTask.getNeoFormArchive().from(spec.getNeoFormArchive()); + neoFormRuntimeTask.getNeoFormArchive().from(spec.getUnpackedNeoFormArchive()); } - private static void configureMcpRuntimeTaskWithDefaults(NeoFormRuntimeSpecification spec, File neoFormDirectory, Map symbolicDataSources, Runtime neoFormRuntimeTask) { + private static void configureMcpRuntimeTaskWithDefaults( + NeoFormRuntimeSpecification spec, + File neoFormDirectory, + Map symbolicDataSources, + Runtime neoFormRuntimeTask) + { configureCommonRuntimeTaskParameters(neoFormRuntimeTask, symbolicDataSources, CommonRuntimeUtils.buildStepName(spec, neoFormRuntimeTask.getName()), spec, neoFormDirectory); - neoFormRuntimeTask.getNeoFormArchive().from(spec.getNeoFormArchive()); + neoFormRuntimeTask.getNeoFormArchive().from(spec.getUnpackedNeoFormArchive()); } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Nullable - private static TaskProvider createBuiltIn(final NeoFormRuntimeSpecification spec, - final NeoFormRuntimeDefinition definition, - NeoFormConfigConfigurationSpecV2 neoFormConfigV2, - NeoFormConfigConfigurationSpecV1.Step step, - final Map> tasks, - final Map> gameArtifactTaskProviders, - final MinecraftArtifactCache cache, - final Optional> adaptedInput) { - switch (step.getType()) { + private static TaskProvider createBuiltIn( + final NeoFormRuntimeSpecification spec, + final NeoFormRuntimeDefinition definition, + final NeoFormConfigConfigurationSpecV2 neoFormConfigV2, + final NeoFormConfigConfigurationSpecV1.Step step, + final Map functions, + final Map> tasks, + final Map> gameArtifactTaskProviders, + final MinecraftArtifactCache cache, + final Optional> adaptedInput) + { + switch (step.getType()) + { case "decompile": - return createDecompile(spec, step, neoFormConfigV2); + return createDecompile(spec, step, neoFormConfigV2, functions); case "downloadClient": return gameArtifactTaskProviders.computeIfAbsent(GameArtifact.CLIENT_JAR, a -> { throw new IllegalStateException("Client Jar is required for this step, but was not provided"); @@ -90,7 +132,11 @@ private static TaskProvider createBuiltIn(final NeoFormRun throw new IllegalStateException("Server Jar is required for this step, but was not provided"); }); case "strip": - return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), StripJar.class, task -> task.getInput().fileProvider(NeoFormRuntimeUtils.getTaskInputFor(spec, tasks, step, task))); + return spec.getProject() + .getTasks() + .register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), + StripJar.class, + task -> task.getInput().fileProvider(NeoFormRuntimeUtils.getTaskInputFor(spec, tasks, step, task))); case "listLibraries": return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), ListLibraries.class, task -> { task.getVersionJsonLibraries().from( @@ -101,33 +147,36 @@ private static TaskProvider createBuiltIn(final NeoFormRun return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), InjectZipContent.class, task -> { task.getInjectionSource().fileProvider(NeoFormRuntimeUtils.getTaskInputFor(spec, tasks, step, task)); task.getInjectedSources() - .add(task.getRuntimeData().map(data -> data.get("inject")) - .map(inject -> { - final InjectFromFileTreeSource fileTreeSource = task.getObjectFactory() - .newInstance(InjectFromFileTreeSource.class); - fileTreeSource.getFiles().from(inject - .matching(fileTree -> { - if (spec.getDistribution().equals(DistributionType.SERVER)) { - fileTree.include("**/server/**"); - } else if (spec.getDistribution().equals(DistributionType.CLIENT)) { - fileTree.include("**/client/**"); - } - }) - ); - fileTreeSource.getTreePrefix().set(task.getSymbolicDataSources().map(data -> data.get("inject"))); - return fileTreeSource; + .add(task.getRuntimeData().map(data -> data.get("inject")) + .map(inject -> { + final InjectFromFileTreeSource fileTreeSource = task.getObjectFactory() + .newInstance(InjectFromFileTreeSource.class); + fileTreeSource.getFiles().from(inject + .matching(fileTree -> { + if (spec.getDistribution().equals(DistributionType.SERVER)) + { + fileTree.include("**/server/**"); + } + else if (spec.getDistribution().equals(DistributionType.CLIENT)) + { + fileTree.include("**/client/**"); + } }) - ); + ); + fileTreeSource.getTreePrefix().set(task.getSymbolicDataSources().map(data -> data.get("inject"))); + return fileTreeSource; + }) + ); }); case "patch": return spec.getProject().getTasks().register( - CommonRuntimeUtils.buildTaskName(spec, step.getName()), - Patch.class, - task -> { - task.getInput().fileProvider(NeoFormRuntimeUtils.getTaskInputFor(spec, tasks, step, task)); - task.getPatchArchive().from(spec.getProject().fileTree(spec.getNeoFormArchive())); - task.getPatchDirectory().set(neoFormConfigV2.getData("patches", spec.getDistribution().getName())); - } + CommonRuntimeUtils.buildTaskName(spec, step.getName()), + Patch.class, + task -> { + task.getInput().fileProvider(NeoFormRuntimeUtils.getTaskInputFor(spec, tasks, step, task)); + task.getPatchArchive().from(spec.getProject().fileTree(spec.getUnpackedNeoFormArchive())); + task.getPatchDirectory().set(neoFormConfigV2.getData("patches", spec.getDistribution().getName())); + } ); case "bundleExtractJar": //We handle extraction of the bundle in-house @@ -135,8 +184,10 @@ private static TaskProvider createBuiltIn(final NeoFormRun throw new IllegalStateException("Extracted Server Jar is required for this step, but was not provided"); }); } - if (neoFormConfigV2.getSpec() >= 2) { - switch (step.getType()) { + if (neoFormConfigV2.getSpec() >= 2) + { + switch (step.getType()) + { case "downloadClientMappings": return gameArtifactTaskProviders.computeIfAbsent(GameArtifact.CLIENT_MAPPINGS, a -> { throw new IllegalStateException("Client Mappings are required for this step, but were not provided"); @@ -152,9 +203,15 @@ private static TaskProvider createBuiltIn(final NeoFormRun } @NotNull - private static TaskProvider createDecompile(NeoFormRuntimeSpecification spec, NeoFormConfigConfigurationSpecV1.Step step, NeoFormConfigConfigurationSpecV2 neoFormConfig) { - NeoFormConfigConfigurationSpecV1.Function function = neoFormConfig.getFunction(step.getType()); - if (function == null) { + private static TaskProvider createDecompile( + final NeoFormRuntimeSpecification spec, + final NeoFormConfigConfigurationSpecV1.Step step, + final NeoFormConfigConfigurationSpecV2 neoFormConfig, + final Map functions) + { + NeoFormConfigConfigurationSpecV1.Function function = functions.get(step.getType()); + if (function == null) + { throw new IllegalArgumentException(String.format("Invalid NeoForm Config, Unknown function step type: %s File: %s", step.getType(), neoFormConfig)); } @@ -165,8 +222,10 @@ private static TaskProvider createDecompile(NeoFormRuntimeSpe // Retrieve the default memory size from the JVM arguments configured in NeoForm String defaultMaxMemory = "4g"; List jvmArgs = new ArrayList<>(function.getJvmArgs()); - for (int i = jvmArgs.size() - 1; i >= 0; i--) { - if (jvmArgs.get(i).startsWith("-Xmx")) { + for (int i = jvmArgs.size() - 1; i >= 0; i--) + { + if (jvmArgs.get(i).startsWith("-Xmx")) + { defaultMaxMemory = jvmArgs.get(i).substring("-Xmx".length()); jvmArgs.remove(i); } @@ -180,7 +239,8 @@ private static TaskProvider createDecompile(NeoFormRuntimeSpe jvmArgs.addAll(settings.getJvmArgs().get()); jvmArgs.add("-Xmx" + maxMemory); - if (maxThreads > 0) { + if (maxThreads > 0) + { decompilerArgs.add(0, "-thr=" + maxThreads); } decompilerArgs.add(0, "-log=" + logLevel); @@ -192,8 +252,10 @@ private static TaskProvider createDecompile(NeoFormRuntimeSpe }); } - private static String getDecompilerLogLevelArg(DecompilerLogLevel logLevel, String version) { - return switch (logLevel) { + private static String getDecompilerLogLevelArg(DecompilerLogLevel logLevel, String version) + { + return switch (logLevel) + { case TRACE -> "trace"; case INFO -> "info"; case WARN -> "warn"; @@ -202,7 +264,11 @@ private static String getDecompilerLogLevelArg(DecompilerLogLevel logLevel, Stri }; } - private TaskProvider createExecute(final NeoFormRuntimeSpecification spec, final NeoFormConfigConfigurationSpecV1.Step step, final NeoFormConfigConfigurationSpecV1.Function function) { + private TaskProvider createExecute( + final NeoFormRuntimeSpecification spec, + final NeoFormConfigConfigurationSpecV1.Step step, + final NeoFormConfigConfigurationSpecV1.Function function) + { return spec.getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(spec, step.getName()), DefaultExecute.class, task -> { task.getExecutingJar().set(ToolUtilities.resolveTool(task.getProject(), function.getVersion())); task.getJvmArguments().addAll(function.getJvmArgs()); @@ -210,19 +276,38 @@ private TaskProvider createExecute(final NeoFormRuntimeSpecif }); } - private static void buildArguments(final RuntimeArguments arguments, final NeoFormRuntimeSpecification spec, NeoFormConfigConfigurationSpecV1.Step step, final Map> tasks, final Runtime taskForArguments, final Optional> alternativeInputProvider) { + private static void buildArguments( + final RuntimeArguments arguments, + final NeoFormRuntimeSpecification spec, + NeoFormConfigConfigurationSpecV1.Step step, + final Map> tasks, + final Runtime taskForArguments, + final Optional> alternativeInputProvider) + { step.getValues().forEach((key, value) -> { - if (value.startsWith("{") && value.endsWith("}")) { + //NeoForm is always available as a global contextural value. + if (value.equals("{neoform}")) { + arguments.putFile(key, spec.getNeoFormArchive()); + return; + } + + if (value.startsWith("{") && value.endsWith("}")) + { Optional> dependentTask; - if (!Objects.equals(key, "input") || alternativeInputProvider.isEmpty()) { + if (!Objects.equals(key, "input") || alternativeInputProvider.isEmpty()) + { dependentTask = NeoFormRuntimeUtils.getInputTaskForTaskFrom(spec, value, tasks); - } else { + } + else + { dependentTask = alternativeInputProvider; } dependentTask.ifPresent(taskForArguments::dependsOn); dependentTask.ifPresent(task -> arguments.putRegularFile(key, task.flatMap(OutputSpecification::getOutput))); - } else { + } + else + { arguments.put(key, spec.getProject().provider(() -> value)); } }); @@ -230,19 +315,23 @@ private static void buildArguments(final RuntimeArguments arguments, final NeoFo @SuppressWarnings("unchecked") @NotNull - private static Map buildDataFilesMap(NeoFormConfigConfigurationSpecV2 neoFormConfig, final DistributionType side) { + private static Map buildDataFilesMap(NeoFormConfigConfigurationSpecV2 neoFormConfig, final DistributionType side) + { return neoFormConfig.getData().entrySet().stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> e.getValue() instanceof Map ? ((Map) e.getValue()).get(side.getName()) : (String) e.getValue() - )); + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue() instanceof Map ? ((Map) e.getValue()).get(side.getName()) : (String) e.getValue() + )); } @SuppressWarnings({"ResultOfMethodCallIgnored"}) @NotNull - protected NeoFormRuntimeDefinition doCreate(final NeoFormRuntimeSpecification spec) { + protected NeoFormRuntimeDefinition doCreate(final NeoFormRuntimeSpecification spec) + { if (this.definitions.containsKey(spec.getIdentifier())) + { throw new IllegalArgumentException("Cannot register runtime with identifier '" + spec.getIdentifier() + "' because it already exists"); + } final Minecraft minecraftExtension = spec.getProject().getExtensions().getByType(Minecraft.class); final Mappings mappingsExtension = minecraftExtension.getMappings(); @@ -256,69 +345,74 @@ protected NeoFormRuntimeDefinition doCreate(final NeoFormRuntimeSpecification sp final Provider versionJson = artifactCache.cacheVersionManifest(spec.getMinecraftVersion()).map(TransformerUtils.guard(VersionJson::get)); final Configuration minecraftDependenciesConfiguration = ConfigurationUtils.temporaryUnhandledConfiguration( - spec.getProject().getConfigurations(), - "NeoFormMinecraftDependenciesFor" + spec.getIdentifier() + spec.getProject().getConfigurations(), + "NeoFormMinecraftDependenciesFor" + spec.getIdentifier() ); minecraftDependenciesConfiguration.getDependencies().addAllLater( - versionJson.map(VersionJson::getLibraries) - .map(libraries -> libraries.stream() - .map(library -> getProject().getDependencies().create(library.getName())) - .collect(Collectors.toList()) - ) + versionJson.map(VersionJson::getLibraries) + .map(libraries -> libraries.stream() + .map(library -> getProject().getDependencies().create(library.getName())) + .collect(Collectors.toList()) + ) ); final File neoFormDirectory = spec.getProject().getExtensions().getByType(ConfigurationData.class) - .getLocation() - .dir(String.format("neoForm/%s", spec.getIdentifier())).get().getAsFile(); + .getLocation() + .dir(String.format("neoForm/%s", spec.getIdentifier())).get().getAsFile(); final File stepsMcpDirectory = new File(neoFormDirectory, "steps"); stepsMcpDirectory.mkdirs(); NeoFormConfigConfigurationSpecV2 neoFormConfig = spec.getConfig(); neoFormConfig.getLibraries(spec.getDistribution().getName()).forEach(library -> minecraftDependenciesConfiguration.getDependencies().add( - spec.getProject().getDependencies().create(library) + spec.getProject().getDependencies().create(library) )); final Map symbolicDataSources = buildDataFilesMap(neoFormConfig, spec.getDistribution()); - final TaskProvider sourceJarTask = spec.getProject().getTasks().register("supplySourcesFor" + spec.getIdentifier(), ArtifactFromOutput.class, task -> { - task.getOutput().set(new File(neoFormDirectory, "sources.jar")); - }); - final TaskProvider rawJarTask = spec.getProject().getTasks().register("supplyRawJarFor" + spec.getIdentifier(), ArtifactFromOutput.class, task -> { - task.getOutput().set(new File(neoFormDirectory, "raw.jar")); - }); + final TaskProvider sourceJarTask = + spec.getProject().getTasks().register("supplySourcesFor" + spec.getIdentifier(), ArtifactFromOutput.class, task -> { + task.getOutput().set(new File(neoFormDirectory, "sources.jar")); + }); + final TaskProvider rawJarTask = + spec.getProject().getTasks().register("supplyRawJarFor" + spec.getIdentifier(), ArtifactFromOutput.class, task -> { + task.getOutput().set(new File(neoFormDirectory, "raw.jar")); + }); return new NeoFormRuntimeDefinition( - spec, - new LinkedHashMap<>(), - sourceJarTask, - rawJarTask, - gameArtifactTasks, - minecraftDependenciesConfiguration, - taskProvider -> taskProvider.configure(runtimeTask -> { - configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, runtimeTask); - }), - versionJson, - neoFormConfig, - createDownloadAssetsTasks(spec, versionJson), - createExtractNativesTasks(spec, symbolicDataSources, neoFormDirectory, versionJson) + spec, + new LinkedHashMap<>(), + sourceJarTask, + rawJarTask, + gameArtifactTasks, + minecraftDependenciesConfiguration, + taskProvider -> taskProvider.configure(runtimeTask -> { + configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, runtimeTask); + }), + versionJson, + neoFormConfig, + createDownloadAssetsTasks(spec, versionJson), + createExtractNativesTasks(spec, symbolicDataSources, neoFormDirectory, versionJson) ); } @Override - protected void afterRegistration(NeoFormRuntimeDefinition runtime) { + protected void afterRegistration(NeoFormRuntimeDefinition runtime) + { //TODO: Right now this is needed so that runs and other components can be order free in the buildscript, //TODO: We should consider making this somehow lazy and remove the unneeded complexity because of it. ProjectUtils.afterEvaluate(runtime.getSpecification().getProject(), () -> this.bakeDefinition(runtime)); } @Override - protected NeoFormRuntimeSpecification.Builder createBuilder() { + protected NeoFormRuntimeSpecification.Builder createBuilder() + { return NeoFormRuntimeSpecification.Builder.from(getProject()); } - protected void bakeDefinition(NeoFormRuntimeDefinition definition) { + protected void bakeDefinition(NeoFormRuntimeDefinition definition) + { final NeoFormRuntimeSpecification spec = definition.getSpecification(); final NeoFormConfigConfigurationSpecV2 neoFormConfig = definition.getNeoFormConfig(); @@ -339,83 +433,124 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { final Map symbolicDataSources = buildDataFilesMap(neoFormConfig, spec.getDistribution()); final List steps = new ArrayList<>(neoFormConfig.getSteps(spec.getDistribution().getName())); - if (steps.isEmpty()) { + if (steps.isEmpty()) + { throw new IllegalArgumentException("Unknown side: " + spec.getDistribution() + " for NeoForm " + definition.getSpecification().getNeoFormVersion()); } + final Map functions = Maps.newHashMap(neoFormConfig.getFunctions()); + steps.removeIf(step -> DISABLED_STEPS.contains(step.getType())); + spec.getMutator().accept(steps, functions); + + definition.setBakedStepsAndFunctions(steps, functions); + final LinkedHashMap> taskOutputs = definition.getTasks(); - for (NeoFormConfigConfigurationSpecV1.Step step : steps) { + final Map> taskOutputsByStepName = definition.getTaskOutputsByStepName(); + final Map>> taskInputsByStepName = definition.getTaskInputsByStepName(); + for (NeoFormConfigConfigurationSpecV1.Step step : steps) + { Optional> adaptedInput = Optional.empty(); - if (spec.getPreTaskTypeAdapters().containsKey(step.getName())) { + if (spec.getPreTaskTypeAdapters().containsKey(step.getName())) + { adaptedInput = adaptPreTaskInput(definition, step, spec, taskOutputs, neoFormDirectory, symbolicDataSources, adaptedInput); } - TaskProvider neoFormRuntimeTaskProvider = createBuiltIn( - spec, - definition, - neoFormConfig, - step, - taskOutputs, - definition.getGameArtifactProvidingTasks(), - artifactCacheExtension, - adaptedInput + TaskProvider neoFormRuntimeTaskProvider; + final String skippedSourceName = spec.skipTaskWith(step.getName()); + if (skippedSourceName != null) + { + if (!taskOutputsByStepName.containsKey(skippedSourceName)) + { + throw new GradleException("The configured skip for: %s uses an output of: %s which has not been registered yet!".formatted(step.getName(), skippedSourceName)); + } + + neoFormRuntimeTaskProvider = taskOutputsByStepName.get(skippedSourceName); + taskOutputs.put(CommonRuntimeUtils.buildTaskName(spec, step.getName()), neoFormRuntimeTaskProvider); + taskOutputsByStepName.put(step.getName(), neoFormRuntimeTaskProvider); + continue; + } + + neoFormRuntimeTaskProvider = createBuiltIn( + spec, + definition, + neoFormConfig, + step, + functions, + taskOutputs, + definition.getGameArtifactProvidingTasks(), + artifactCacheExtension, + adaptedInput ); - if (neoFormRuntimeTaskProvider == null) { - NeoFormConfigConfigurationSpecV1.Function function = neoFormConfig.getFunction(step.getType()); - if (function == null) { + if (neoFormRuntimeTaskProvider == null) + { + NeoFormConfigConfigurationSpecV1.Function function = functions.get(step.getType()); + if (function == null) + { throw new IllegalArgumentException(String.format("Invalid MCP Config, Unknown function step type: %s File: %s", step.getType(), neoFormConfig)); } neoFormRuntimeTaskProvider = createExecute(spec, step, function); - if (step.getType().equals("mergeMappings")) { + if (step.getType().equals("mergeMappings")) + { neoFormRuntimeTaskProvider.configure(tsk -> tsk.getOutputFileName().set("outputs.tsrg")); } } Optional> finalAdaptedInput = adaptedInput; + taskInputsByStepName.put(step.getName(), finalAdaptedInput); neoFormRuntimeTaskProvider.configure((WithOutput neoFormRuntimeTask) -> { - if (neoFormRuntimeTask instanceof Runtime runtimeTask) { + if (neoFormRuntimeTask instanceof Runtime runtimeTask) + { configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, taskOutputs, step, runtimeTask, finalAdaptedInput); } }); final String taskName = neoFormRuntimeTaskProvider.getName(); - if (!spec.getPostTypeAdapters().containsKey(step.getName())) { - taskOutputs.put(taskName, neoFormRuntimeTaskProvider); - } else { - for (TaskTreeAdapter taskTreeAdapter : spec.getPostTypeAdapters().get(step.getName())) { - final TaskProvider taskProvider = taskTreeAdapter.adapt(definition, neoFormRuntimeTaskProvider, neoFormDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), dependentTaskProvider -> dependentTaskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task))); - if (taskProvider != null) { + if (spec.getPostTypeAdapters().containsKey(step.getName())) + { + for (TaskTreeAdapter taskTreeAdapter : spec.getPostTypeAdapters().get(step.getName())) + { + final TaskProvider taskProvider = taskTreeAdapter.adapt(definition, + neoFormRuntimeTaskProvider, + neoFormDirectory, + definition.getGameArtifactProvidingTasks(), + definition.getMappingVersionData(), + dependentTaskProvider -> dependentTaskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task))); + if (taskProvider != null) + { taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)); neoFormRuntimeTaskProvider = taskProvider; } } // We consider the outputs of the final post adapter the outputs of step - taskOutputs.put(taskName, neoFormRuntimeTaskProvider); taskOutputs.put(neoFormRuntimeTaskProvider.getName(), neoFormRuntimeTaskProvider); } + + taskOutputs.put(taskName, neoFormRuntimeTaskProvider); + taskOutputsByStepName.put(step.getName(), neoFormRuntimeTaskProvider); } final TaskProvider lastTask = Iterators.getLast(taskOutputs.values().iterator()); final Set> additionalRuntimeTasks = Sets.newHashSet(); TaskProvider recompileInput = adaptPreTaskInput( - definition, - "recompile", - spec, - neoFormDirectory, - symbolicDataSources, - Optional.of(lastTask), - Optional.of(lastTask) + definition, + "recompile", + spec, + neoFormDirectory, + symbolicDataSources, + Optional.of(lastTask), + Optional.of(lastTask) ).orElseThrow(() -> new IllegalStateException("No input for recompile task due to pre-task adapters")); - final FileCollection recompileDependencies = definition.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration())); + final FileCollection recompileDependencies = + definition.getAdditionalRecompileDependencies().plus(spec.getProject().files(definition.getMinecraftDependenciesConfiguration())); final TaskProvider recompileTask = createRecompileTask(definition, recompileInput, recompileDependencies, task -> { @@ -432,9 +567,18 @@ protected void bakeDefinition(NeoFormRuntimeDefinition definition) { }); } - private static Optional> adaptPreTaskInput(NeoFormRuntimeDefinition definition, NeoFormConfigConfigurationSpecV1.Step step, NeoFormRuntimeSpecification spec, LinkedHashMap> taskOutputs, File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput) { + private static Optional> adaptPreTaskInput( + NeoFormRuntimeDefinition definition, + NeoFormConfigConfigurationSpecV1.Step step, + NeoFormRuntimeSpecification spec, + LinkedHashMap> taskOutputs, + File neoFormDirectory, + Map symbolicDataSources, + Optional> adaptedInput) + { final String inputArgumentMarker = step.getValue("input"); - if (inputArgumentMarker == null) { + if (inputArgumentMarker == null) + { throw new IllegalStateException("Can not change input chain on: " + step.getName() + " it has no input to transform!"); } @@ -450,11 +594,20 @@ private static Optional> adaptPreTaskInput( File neoFormDirectory, Map symbolicDataSources, Optional> adaptedInput, - Optional> inputTask) { - if (!spec.getPreTaskTypeAdapters().get(stepName).isEmpty() && inputTask.isPresent()) { - for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(stepName)) { - final TaskProvider modifiedTree = taskTreeAdapter.adapt(definition, inputTask.get(), neoFormDirectory, definition.getGameArtifactProvidingTasks(), definition.getMappingVersionData(), taskProvider -> taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task))); - if (modifiedTree != null) { + Optional> inputTask) + { + if (!spec.getPreTaskTypeAdapters().get(stepName).isEmpty() && inputTask.isPresent()) + { + for (TaskTreeAdapter taskTreeAdapter : spec.getPreTaskTypeAdapters().get(stepName)) + { + final TaskProvider modifiedTree = taskTreeAdapter.adapt(definition, + inputTask.get(), + neoFormDirectory, + definition.getGameArtifactProvidingTasks(), + definition.getMappingVersionData(), + taskProvider -> taskProvider.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task))); + if (modifiedTree != null) + { modifiedTree.configure(task -> configureMcpRuntimeTaskWithDefaults(spec, neoFormDirectory, symbolicDataSources, task)); inputTask = Optional.of(modifiedTree); } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/specification/NeoFormRuntimeSpecification.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/specification/NeoFormRuntimeSpecification.java index 9fc5b45b7..88146949b 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/specification/NeoFormRuntimeSpecification.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/specification/NeoFormRuntimeSpecification.java @@ -1,11 +1,13 @@ package net.neoforged.gradle.neoform.runtime.specification; +import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import net.neoforged.gradle.common.runtime.specification.CommonRuntimeSpecification; import net.neoforged.gradle.common.util.ToolUtilities; import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskCustomizer; import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter; import net.neoforged.gradle.dsl.common.util.DistributionType; +import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1; import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV2; import net.neoforged.gradle.dsl.neoform.runtime.specification.NeoFormSpecification; import net.neoforged.gradle.neoform.runtime.extensions.NeoFormRuntimeExtension; @@ -16,6 +18,7 @@ import org.gradle.api.Task; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ResolvedArtifact; +import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFileProperty; @@ -27,192 +30,283 @@ import java.io.*; import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; import java.util.zip.ZipEntry; -import java.util.zip.ZipException; import java.util.zip.ZipFile; /** * Defines a specification for an MCP runtime. */ -public class NeoFormRuntimeSpecification extends CommonRuntimeSpecification implements NeoFormSpecification { - private final Provider neoFormArchive; - private final NeoFormConfigConfigurationSpecV2 config; - private final FileCollection additionalRecompileDependencies; - - private NeoFormRuntimeSpecification(Project project, - String version, - Provider neoFormArchive, - NeoFormConfigConfigurationSpecV2 config, - DistributionType side, - Multimap preTaskTypeAdapters, - Multimap postTypeAdapters, - Multimap> taskCustomizers, - FileCollection additionalRecompileDependencies) { +public class NeoFormRuntimeSpecification extends CommonRuntimeSpecification implements NeoFormSpecification +{ + private final Provider unpackedNeoFormArchive; + private final NeoFormConfigConfigurationSpecV2 config; + private final FileCollection additionalRecompileDependencies; + private final Map skipTasks; + private final BiConsumer, Map> mutator; + private final Provider neoFormArchive; + + private NeoFormRuntimeSpecification( + final Project project, + final String version, + final Provider unpackedNeoFormArchive, + final Provider archive, + final NeoFormConfigConfigurationSpecV2 config, + final DistributionType side, + final Multimap preTaskTypeAdapters, + final Multimap postTypeAdapters, + final Multimap> taskCustomizers, + final FileCollection additionalRecompileDependencies, + final Map skipTasks, + final BiConsumer, Map> mutator) + { super(project, "neoForm", version, side, preTaskTypeAdapters, postTypeAdapters, taskCustomizers, NeoFormRuntimeExtension.class); - this.neoFormArchive = neoFormArchive; + this.unpackedNeoFormArchive = unpackedNeoFormArchive; this.config = config; this.additionalRecompileDependencies = additionalRecompileDependencies; + this.skipTasks = skipTasks; + this.mutator = mutator; + this.neoFormArchive = archive; } - public NeoFormConfigConfigurationSpecV2 getConfig() { + public NeoFormConfigConfigurationSpecV2 getConfig() + { return config; } - public String getMinecraftVersion() { + public String getMinecraftVersion() + { return config.getVersion(); } - public String getNeoFormVersion() { + public String getNeoFormVersion() + { String prefix = getMinecraftVersion() + "-"; - if (getVersion().startsWith(prefix)) { + if (getVersion().startsWith(prefix)) + { return getVersion().substring(prefix.length()); - } else { + } + else + { throw new RuntimeException("NeoForm version " + getVersion() + " does not start with Minecraft version" + getMinecraftVersion()); } } + public Provider getUnpackedNeoFormArchive() + { + return unpackedNeoFormArchive; + } + public Provider getNeoFormArchive() { return neoFormArchive; } @Override - public @NotNull FileCollection getAdditionalRecompileDependencies() { + public @NotNull FileCollection getAdditionalRecompileDependencies() + { return additionalRecompileDependencies; } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof NeoFormRuntimeSpecification)) return false; - if (!super.equals(o)) return false; + public @Nullable String skipTaskWith(final String taskName) + { + return skipTasks.get(taskName); + } + + @Override + public BiConsumer, Map> getMutator() + { + return mutator; + } - NeoFormRuntimeSpecification spec = (NeoFormRuntimeSpecification) o; + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof final NeoFormRuntimeSpecification spec)) + { + return false; + } + if (!super.equals(o)) + { + return false; + } - if (!neoFormArchive.equals(spec.neoFormArchive)) return false; + if (!unpackedNeoFormArchive.equals(spec.unpackedNeoFormArchive)) + { + return false; + } return additionalRecompileDependencies.equals(spec.additionalRecompileDependencies); } @Override - public int hashCode() { + public int hashCode() + { int result = super.hashCode(); - result = 31 * result + neoFormArchive.hashCode(); + result = 31 * result + unpackedNeoFormArchive.hashCode(); result = 31 * result + additionalRecompileDependencies.hashCode(); return result; } - public static final class Builder extends CommonRuntimeSpecification.Builder implements NeoFormSpecification.Builder { - - private Dependency neoFormDependency; - private FileCollection additionalDependencies; + public static final class Builder extends CommonRuntimeSpecification.Builder + implements NeoFormSpecification.Builder + { + private Dependency neoFormDependency; + private FileCollection additionalDependencies; + private final Map skipped = Maps.newHashMap(); + private BiConsumer, Map> mutator = (steps, function) -> {}; - private Builder(Project project) { + private Builder(Project project) + { super(project); this.additionalDependencies = project.getObjects().fileCollection(); withNeoFormVersion("+"); } @Override - protected Builder getThis() { + protected Builder getThis() + { return this; } - public static Builder from(final Project project) { + public static Builder from(final Project project) + { return new Builder(project); } @NotNull @Override - public Builder withNeoFormVersion(@NotNull String version) { + public Builder withNeoFormVersion(@NotNull String version) + { this.neoFormDependency = project.getDependencies().create("net.neoforged:neoform:" + version + "@zip"); return getThis(); } @NotNull @Override - public Builder withNeoFormDependency(@NotNull Object notation) { + public Builder withNeoFormDependency(@NotNull Object notation) + { this.neoFormDependency = getProject().getDependencies().create(notation); return getThis(); } @Override - public Builder withAdditionalDependencies(final FileCollection files) { + public Builder withAdditionalDependencies(final FileCollection files) + { this.additionalDependencies = this.additionalDependencies.plus(files); return getThis(); } - public @NotNull NeoFormRuntimeSpecification build() { + @Override + public @NotNull Builder withSkippedTask(@NotNull final String task, @NotNull final String outputSource) + { + this.skipped.put(task, outputSource); + return getThis(); + } + + @Override + public Builder withStepsMutator(@NotNull final BiConsumer, Map> mutator) + { + this.mutator = + this.mutator.andThen(mutator); + return this; + } + + public @NotNull NeoFormRuntimeSpecification build() + { ResolvedArtifact artifact = ToolUtilities.resolveToolArtifact(project, neoFormDependency); File archive = artifact.getFile(); String effectiveVersion = artifact.getModuleVersion().getId().getVersion(); // Read the NF config from the archive NeoFormConfigConfigurationSpecV2 config; - try { + try + { config = FileUtils.processFileFromZip(archive, "config.json", NeoFormConfigConfigurationSpecV2::get); - } catch (IOException e) { + } + catch (IOException e) + { throw new GradleException("Failed to read NeoForm config file from version " + effectiveVersion); } return new NeoFormRuntimeSpecification( - project, - effectiveVersion, - project.getProviders().of(NeoFormUnpack.class, spec -> { - spec.parameters(p -> { - p.getArchive().set(archive); - p.getDestination().set(project.getLayout().getBuildDirectory().dir("neoform/" + effectiveVersion)); - }); - }), - config, - distributionType.get(), - preTaskAdapters, - postTaskAdapters, - taskCustomizers, - additionalDependencies - ); + project, + effectiveVersion, + project.getProviders().of(NeoFormUnpack.class, spec -> { + spec.parameters(p -> { + p.getArchive().set(archive); + p.getDestination().set(project.getLayout().getBuildDirectory().dir("neoform/" + effectiveVersion)); + }); + }), + project.provider(() -> archive), + config, + distributionType.get(), + preTaskAdapters, + postTaskAdapters, + taskCustomizers, + additionalDependencies, + skipped, + mutator); } } - public static interface NeoFormUnpackParameters extends ValueSourceParameters { + public static interface NeoFormUnpackParameters extends ValueSourceParameters + { RegularFileProperty getArchive(); DirectoryProperty getDestination(); } - public static abstract class NeoFormUnpack implements ValueSource { + public static abstract class NeoFormUnpack implements ValueSource + { @Nullable @Override - public File obtain() { + public Directory obtain() + { RegularFileProperty archive = getParameters().getArchive(); DirectoryProperty destination = getParameters().getDestination(); File dest = destination.getAsFile().get(); - if (!dest.exists() && !dest.mkdirs()) { + if (!dest.exists() && !dest.mkdirs()) + { throw new GradleException("Failed to create directory " + dest); } - try (java.util.zip.ZipFile zipFile = new ZipFile(archive.getAsFile().get())) { + try (java.util.zip.ZipFile zipFile = new ZipFile(archive.getAsFile().get())) + { Enumeration entries = zipFile.entries(); - while (entries.hasMoreElements()) { + while (entries.hasMoreElements()) + { ZipEntry entry = entries.nextElement(); - File entryDestination = new File(dest, entry.getName()); - if (entry.isDirectory()) { + File entryDestination = new File(dest, entry.getName()); + if (entry.isDirectory()) + { entryDestination.mkdirs(); - } else { + } + else + { entryDestination.getParentFile().mkdirs(); try (InputStream in = zipFile.getInputStream(entry); - OutputStream out = new FileOutputStream(entryDestination)) { + OutputStream out = new FileOutputStream(entryDestination)) + { IOUtils.copy(in, out); } } } - } catch (ZipException e) { - throw new RuntimeException(e); - } catch (IOException e) { + } + catch (IOException e) + { throw new RuntimeException(e); } - return dest; + + return destination.get(); } } } diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/PackJar.java b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/PackJar.java index 220ff77a3..0cac807e0 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/PackJar.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/runtime/tasks/PackJar.java @@ -29,7 +29,7 @@ public abstract class PackJar extends Zip implements Runtime { public PackJar() { super(); - arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class, getProviderFactory()); + arguments = getObjectFactory().newInstance(RuntimeArgumentsImpl.class); multiArguments = getObjectFactory().newInstance(RuntimeMultiArgumentsImpl.class, getProviderFactory()); this.javaVersion = getProject().getObjects().property(JavaLanguageVersion.class); diff --git a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java index a995e8142..2358a681f 100644 --- a/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java +++ b/neoform/src/main/java/net/neoforged/gradle/neoform/util/NeoFormRuntimeUtils.java @@ -88,26 +88,16 @@ public static Optional> getInputTaskForTaskFr String stepName = matcher.group(1); if (stepName != null) { String taskName = CommonRuntimeUtils.buildTaskName(spec, stepName); - switch (stepName) { - case "downloadManifest": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_LAUNCHER_METADATA, spec.getMinecraftVersion()); - break; - case "downloadJson": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MANIFEST, spec.getMinecraftVersion()); - break; - case "downloadClient": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_VERSION_ARTIFACT_CLIENT, spec.getMinecraftVersion()); - break; - case "downloadServer": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_VERSION_ARTIFACT_SERVER, spec.getMinecraftVersion()); - break; - case "downloadClientMappings": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MAPPINGS_CLIENT, spec.getMinecraftVersion()); - break; - case "downloadServerMappings": - taskName = String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MAPPINGS_SERVER, spec.getMinecraftVersion()); - break; - } + taskName = switch (stepName) + { + case "downloadManifest" -> String.format("%s%s", NamingConstants.Task.CACHE_LAUNCHER_METADATA, spec.getMinecraftVersion()); + case "downloadJson" -> String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MANIFEST, spec.getMinecraftVersion()); + case "downloadClient" -> String.format("%s%s", NamingConstants.Task.CACHE_VERSION_ARTIFACT_CLIENT, spec.getMinecraftVersion()); + case "downloadServer" -> String.format("%s%s", NamingConstants.Task.CACHE_VERSION_ARTIFACT_SERVER, spec.getMinecraftVersion()); + case "downloadClientMappings" -> String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MAPPINGS_CLIENT, spec.getMinecraftVersion()); + case "downloadServerMappings" -> String.format("%s%s", NamingConstants.Task.CACHE_VERSION_MAPPINGS_SERVER, spec.getMinecraftVersion()); + default -> taskName; + }; String finalTaskName = taskName; return Optional.ofNullable(tasks.get(finalTaskName)); diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy index 727c6186b..cdb04a66a 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AccessTransformerTests.groovy @@ -58,6 +58,51 @@ class AccessTransformerTests extends BuilderBasedTestSpecification { initialRun.task(":compileJava").outcome == TaskOutcome.SUCCESS } + def "the userdev runtime supports loading ats from a file with the decompiler disabled"() { + given: + def project = create("userdev_supports_ats_from_file_decompiler_disabled", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + minecraft.accessTransformers.file rootProject.file('src/main/resources/META-INF/accesstransformer.cfg') + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/resources/META-INF/accesstransformer.cfg", """public-f net.minecraft.client.Minecraft fixerUpper # fixerUpper""") + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().fixerUpper.getClass().toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.parallel() + it.property("neogradle.subsystems.decompiler.enabled", "false") + }) + + when: + def initialRun = project.run { + it.tasks('compileJava') + it.stacktrace() + } + + then: + initialRun.task(":compileJava").outcome == TaskOutcome.SUCCESS + initialRun.task(":neoFormDecompile") == null + } + def "the userdev runtime supports loading ats from a file after the dependencies block"() { given: def project = create("userdev_supports_ats_from_file", { diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AdvancedFmlTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AdvancedFmlTests.groovy new file mode 100644 index 000000000..d11c1b774 --- /dev/null +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/AdvancedFmlTests.groovy @@ -0,0 +1,201 @@ +package net.neoforged.gradle.userdev + +import net.neoforged.trainingwheels.gradle.functional.BuilderBasedTestSpecification +import org.gradle.testkit.runner.TaskOutcome + +class AdvancedFmlTests extends BuilderBasedTestSpecification { + + @Override + protected void configurePluginUnderTest() { + pluginUnderTest = "net.neoforged.gradle.userdev"; + injectIntoAllProject = true; + } + + def "a mod with userdev as dependency can run the patch task for that dependency"() { + given: + def project = create("running_patch_task_is_possible", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + repositories { + mavenLocal() + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def run = project.run { + it.tasks(':neoFormRecompile') + it.stacktrace() + } + + then: + run.task(':neoFormRecompile').outcome == TaskOutcome.SUCCESS + } + + def "a mod with userdev as dependency can run the compile task for that dependency in ci mode"() { + given: + def project = create("running_compile_in_ci", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + repositories { + mavenLocal() + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.withEnvironmentVariable("CI", "true") + }) + + when: + def run = project.run { + it.tasks(':compileJava') + it.stacktrace() + } + + then: + run.task(':compileJava').outcome == TaskOutcome.SUCCESS + run.task(":neoFormDecompile") == null + } + + def "a mod with userdev as dependency can run the compile task for that dependency with the decompiler disabled"() { + given: + def project = create("running_compile_with_compiler_disabled", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.property("neogradle.subsystems.decompiler.enabled", "false") + }) + + when: + def run = project.run { + it.tasks(':compileJava') + it.stacktrace() + } + + then: + run.task(':compileJava').outcome == TaskOutcome.SUCCESS + run.task(":neoFormDecompile") == null + } + + def "a mod using a disabled decompiler should be able to run the game"() { + given: + def project = create("disabled_decompiler_runs_game", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + repositories { + mavenCentral() + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + + it.withMod() + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.property("neogradle.subsystems.decompiler.enabled", "false") + }) + + when: + def run = project.run { + it.tasks(':runClientData') + it.stacktrace() + } + + then: + run.checkModLoading() + run.task(":neoFormDecompile") == null + } + + def "a mod using an enabled decompiler should use the setup to decompile the game"() { + given: + def project = create("enabled_decompiler_uses_setup", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + repositories { + mavenCentral() + } + + dependencies { + implementation 'net.neoforged:neoforge:[21.10.60-beta,)' + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + it.property("neogradle.subsystems.decompiler.enabled", "false") + }) + + when: + def run = project.run { + it.tasks(':neoFormDecompile') + it.stacktrace() + it.debug() + } + + then: + run.task(":neoFormSetup") != null + } +} diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy index 24b721f81..8225cfb02 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/FunctionalTests.groovy @@ -247,6 +247,46 @@ class FunctionalTests extends BuilderBasedTestSpecification { run.task(':compileJava').outcome == TaskOutcome.SUCCESS } + def "a mod with userdev as dependency can access neoforge classes"() { + given: + def project = create("compile_with_gradle_and_official_mappings", { + it.build(""" + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencies { + implementation 'net.neoforged:neoforge:+' + } + """) + it.file("src/main/java/net/neoforged/gradle/userdev/FunctionalTests.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + import net.neoforged.neoforge.event.VanillaGameEvent; + + public class FunctionalTests { + public static void main(String[] args) { + System.out.println(Minecraft.getInstance().getClass().toString()); + System.out.println(VanillaGameEvent.class.toString()); + } + } + """) + it.withToolchains() + it.withGlobalCacheDirectory(tempDir) + }) + + when: + def run = project.run { + it.tasks('compileJava') + } + + then: + run.task(':compileJava').outcome == TaskOutcome.SUCCESS + } + def "a mod with userdev as dependency and official mappings can run build and clean in the same execution"() { given: def project = create("gradle_userdev_clean_build", { diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy index eb1a469a6..5c61ec914 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/MultiProjectTests.groovy @@ -74,19 +74,7 @@ class MultiProjectTests extends BuilderBasedTestSpecification { } } """) - it.file("src/main/java/net/neoforged/gradle/main/ApiTests.java", """ - package net.neoforged.gradle.main; - - import net.minecraft.client.Minecraft; - import net.neoforged.gradle.apitest.FunctionalTests; - - public class ApiTests { - public static void main(String[] args) { - System.out.println(Minecraft.getInstance().getClass().toString()); - FunctionalTests.main(args); - } - } - """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) it.plugin(this.pluginUnderTest) @@ -95,13 +83,10 @@ class MultiProjectTests extends BuilderBasedTestSpecification { when: def run = rootProject.run { it.tasks(':main:runClientData') - //We are expecting this test to fail, since there is a mod without any files included so it is fine. - it.shouldFail() } then: - run.output.contains("Error during pre-loading phase: ERROR: File null is not a valid mod file") || - run.output.contains("net.neoforged.fml.ModLoadingException: Loading errors encountered:") + run.checkModLoading() } def "multiple projects with neoforge dependencies from version catalogs should be able to build"() { @@ -193,7 +178,6 @@ class MultiProjectTests extends BuilderBasedTestSpecification { def run = rootProject.run { it.tasks(':main:build') it.stacktrace() - it.debug() } then: @@ -265,19 +249,7 @@ class MultiProjectTests extends BuilderBasedTestSpecification { } } """) - it.file("src/main/java/net/neoforged/gradle/main/ApiTests.java", """ - package net.neoforged.gradle.main; - - import net.minecraft.client.Minecraft; - import net.neoforged.gradle.apitest.FunctionalTests; - - public class ApiTests { - public static void main(String[] args) { - System.out.println(Minecraft.getInstance().getClass().toString()); - FunctionalTests.main(args); - } - } - """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) it.plugin(this.pluginUnderTest) @@ -286,13 +258,10 @@ class MultiProjectTests extends BuilderBasedTestSpecification { when: def run = rootProject.run { it.tasks(':main:runClientData') - //We are expecting this test to fail, since there is a mod without any files included so it is fine. - it.shouldFail() } then: - run.output.contains("Error during pre-loading phase: ERROR: File null is not a valid mod file") || - run.output.contains("net.neoforged.fml.ModLoadingException: Loading errors encountered:") + run.checkModLoading() } def "multiple projects with neoforge dependencies should run using the central cache"() { diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy index 2eaca4770..c59af9bdf 100644 --- a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/RunTests.groovy @@ -40,6 +40,7 @@ class RunTests extends BuilderBasedTestSpecification { implementation(libs.neoforge) } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -47,27 +48,11 @@ class RunTests extends BuilderBasedTestSpecification { when: def run = project.run { it.tasks(':runClientData') - //We are expecting this test to fail, since there is a mod without any files included so it is fine. - it.shouldFail() - it.stacktrace() - } - - then: - true - run.output.contains("is not a valid mod file") - - when: - def runG14 = project.run { - it.tasks(':runClientData') - //We are expecting this test to fail, since there is a mod without any files included so it is fine. - it.shouldFail() it.stacktrace() - it.gradleVersion("8.14") } then: - true - run.output.contains("is not a valid mod file") + run.checkModLoading() } def "configuring of the configurations after the dependencies block should work"() { @@ -105,6 +90,7 @@ class RunTests extends BuilderBasedTestSpecification { modRunImplementation.extendsFrom implementation } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -112,12 +98,10 @@ class RunTests extends BuilderBasedTestSpecification { when: def run = project.run { it.tasks(':runClientData') - //We are expecting this test to fail, since there is a mod without any files included so it is fine. - it.shouldFail() } then: - run.output.contains("is not a valid mod file") + run.checkModLoading() } def "runs can be declared before the dependencies block"() { @@ -144,6 +128,7 @@ class RunTests extends BuilderBasedTestSpecification { implementation 'net.neoforged:neoforge:+' } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -186,6 +171,7 @@ class RunTests extends BuilderBasedTestSpecification { } } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -238,6 +224,7 @@ class RunTests extends BuilderBasedTestSpecification { } } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -290,6 +277,7 @@ class RunTests extends BuilderBasedTestSpecification { } } """) + it.withMod() it.withToolchains() it.withGlobalCacheDirectory(tempDir) }) @@ -298,16 +286,14 @@ class RunTests extends BuilderBasedTestSpecification { def run = project.run { it.tasks(':runClientData') it.stacktrace() - it.shouldFail() - it.debug() } then: run.output.contains("You are using a version of NeoForge which does not need run specific dependencies") run.output.contains("NeoGradle detected a problem with your project: Run.getDependencies().runtime() in run: client") - run.output.contains("is not a valid mod file") !run.output.contains("NeoGradle detected a problem with your project: Run.getDependencies().runtime() in run: server") run.task(":writeMinecraftClasspathClient") == null + run.checkModLoading() } def "userdev supports custom run dependencies from configuration"() { @@ -351,7 +337,6 @@ class RunTests extends BuilderBasedTestSpecification { def run = project.run { it.tasks(':writeMinecraftClasspathClient') it.stacktrace() - } then: diff --git a/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/extensions/TestExtensions.groovy b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/extensions/TestExtensions.groovy new file mode 100644 index 000000000..dceda6a98 --- /dev/null +++ b/userdev/src/functionalTest/groovy/net/neoforged/gradle/userdev/extensions/TestExtensions.groovy @@ -0,0 +1,52 @@ +package net.neoforged.gradle.userdev.extensions + +import net.neoforged.trainingwheels.gradle.functional.builder.Runtime.Builder +import net.neoforged.trainingwheels.gradle.functional.builder.Runtime.RunResult; + +class TestExtensions { + + /** + * Registers a test mod to the project run builder. + * + * @param self The run builder + * @return The run builder, with the test mod added. + */ + static Builder withMod( + Builder self + ) { + self.file("src/main/resources/META-INF/neoforge.mods.toml", """ + license="MIT" + [[mods]] + modId="neogradle_test" + version="1.0" + displayName="NeoGradle Test" + description='''Test Mod for NeoGradle''' + """.stripMargin()) + + self.file("src/main/java/net/neoforged/gradle/userdev/EntryPoint.java", """ + package net.neoforged.gradle.userdev; + + import net.minecraft.client.Minecraft; + import net.neoforged.fml.common.Mod; + import org.slf4j.Logger; + import com.mojang.logging.LogUtils; + + @Mod("neogradle_test") + public class EntryPoint { + + // Directly reference a slf4j logger + public static final Logger LOGGER = LogUtils.getLogger(); + + public EntryPoint() { + LOGGER.info("NeoGradle Test Mod has been loaded successfully!"); + } + } + """.stripMargin()) + + return self + } + + static boolean checkModLoading(RunResult self) { + return self.output.contains("NeoGradle Test Mod has been loaded successfully!") + } +} diff --git a/userdev/src/functionalTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/userdev/src/functionalTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule new file mode 100644 index 000000000..471e7c598 --- /dev/null +++ b/userdev/src/functionalTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule @@ -0,0 +1,3 @@ +moduleName=Builder Test extension. +moduleVersion=1.0.0 +extensionClasses=net.neoforged.gradle.userdev.extensions.TestExtensions \ No newline at end of file diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java index 207f98885..e650173cd 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/definition/UserDevRuntimeDefinition.java @@ -48,17 +48,6 @@ public UserDevRuntimeDefinition(@NotNull UserDevRuntimeSpecification specificati this.unpackedUserDevJarDirectory = unpackedUserDevJarDirectory; this.userdevConfiguration = userdevConfiguration; this.additionalUserDevDependencies = additionalUserDevDependencies; - - //Create the client-extra jar dependency. - final Dependency clientExtraJar = this.getSpecification().getProject().getDependencies().create( - ExtraJarDependencyManager.generateClientCoordinateFor(this.getSpecification().getMinecraftVersion()) - ); - - //Add it as a user dev dependency, this will trigger replacement, which will need to be addressed down-below. - this.additionalUserDevDependencies.getDependencies().add( - clientExtraJar - ); - this.getAllDependencies().from(neoformRuntimeDefinition.getAllDependencies()); this.getAllDependencies().from(getAdditionalUserDevDependencies()); this.getAllDependencies().from(getUserdevConfiguration()); diff --git a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java index 91dc135b4..7713c2697 100644 --- a/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java +++ b/userdev/src/main/java/net/neoforged/gradle/userdev/runtime/extension/UserDevRuntimeExtension.java @@ -1,28 +1,37 @@ package net.neoforged.gradle.userdev.runtime.extension; +import net.neoforged.gradle.common.dependency.ExtraJarDependencyManager; import net.neoforged.gradle.common.runtime.extensions.CommonRuntimeExtension; -import net.neoforged.gradle.common.runtime.tasks.JavaSourceTransformer; -import net.neoforged.gradle.common.util.CommonRuntimeTaskUtils; -import net.neoforged.gradle.common.util.ConfigurationUtils; +import net.neoforged.gradle.common.runtime.tasks.BinaryAccessTransformer; +import net.neoforged.gradle.common.tasks.StripFinalFromParametersTask; +import net.neoforged.gradle.common.util.*; import net.neoforged.gradle.common.util.run.TypesUtil; +import net.neoforged.gradle.dsl.common.extensions.AccessTransformers; +import net.neoforged.gradle.dsl.common.extensions.Minecraft; import net.neoforged.gradle.dsl.common.extensions.subsystems.Conventions; +import net.neoforged.gradle.dsl.common.extensions.subsystems.Decompiler; import net.neoforged.gradle.dsl.common.extensions.subsystems.Subsystems; import net.neoforged.gradle.dsl.common.runs.run.RunManager; import net.neoforged.gradle.dsl.common.runs.type.RunTypeManager; +import net.neoforged.gradle.dsl.common.runtime.definition.Definition; +import net.neoforged.gradle.dsl.common.runtime.tasks.Runtime; import net.neoforged.gradle.dsl.common.runtime.tasks.tree.TaskTreeAdapter; import net.neoforged.gradle.dsl.common.tasks.WithOutput; import net.neoforged.gradle.dsl.common.util.CommonRuntimeUtils; import net.neoforged.gradle.dsl.common.util.DistributionType; +import net.neoforged.gradle.dsl.common.util.GameArtifact; +import net.neoforged.gradle.dsl.neoform.configuration.NeoFormConfigConfigurationSpecV1; import net.neoforged.gradle.dsl.userdev.configurations.UserdevProfile; import net.neoforged.gradle.neoform.runtime.definition.NeoFormRuntimeDefinition; import net.neoforged.gradle.neoform.runtime.extensions.NeoFormRuntimeExtension; import net.neoforged.gradle.neoform.runtime.tasks.InjectZipContent; import net.neoforged.gradle.neoform.runtime.tasks.Patch; -import net.neoforged.gradle.common.util.JavaSourceTransformAdapterUtils; import net.neoforged.gradle.userdev.runtime.definition.UserDevRuntimeDefinition; import net.neoforged.gradle.userdev.runtime.specification.UserDevRuntimeSpecification; +import net.neoforged.gradle.util.StringCapitalizationUtils; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.FileTree; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; @@ -30,37 +39,57 @@ import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public abstract class UserDevRuntimeExtension extends CommonRuntimeExtension +{ -public abstract class UserDevRuntimeExtension extends CommonRuntimeExtension { - @javax.inject.Inject - public UserDevRuntimeExtension(Project project) { + public UserDevRuntimeExtension(Project project) + { super(project); } - + @Override - protected @NotNull UserDevRuntimeDefinition doCreate(UserDevRuntimeSpecification spec) { + protected @NotNull UserDevRuntimeDefinition doCreate(UserDevRuntimeSpecification spec) + { final NeoFormRuntimeExtension neoFormRuntimeExtension = getProject().getExtensions().getByType(NeoFormRuntimeExtension.class); + final Decompiler decompilerSubsystemConfiguration = getProject().getExtensions().getByType(Subsystems.class).getDecompiler(); + final Minecraft minecraftExtension = getProject().getExtensions().getByType(Minecraft.class); final UserdevProfile userDevProfile = spec.getProfile(); final FileTree userDevJar = spec.getUserDevArchive(); final Configuration userDevAdditionalDependenciesConfiguration = ConfigurationUtils.temporaryConfiguration( - getProject(), - "AdditionalDependenciesFor" + spec.getIdentifier() + getProject(), + "AdditionalDependenciesFor" + spec.getIdentifier() ); - for (String dependencyCoordinate : userDevProfile.getAdditionalDependencyArtifactCoordinates().get()) { + + if (useCombinedJarWithNeoForgeOnRecompile(userDevProfile)) + { + userDevAdditionalDependenciesConfiguration.getDependencies().addLater( + userDevProfile.getUniversalJarArtifactCoordinate().map(spec.getProject().getDependencies()::create) + ); + } + + for (String dependencyCoordinate : userDevProfile.getAdditionalDependencyArtifactCoordinates().get()) + { userDevAdditionalDependenciesConfiguration.getDependencies().add(getProject().getDependencies().create(dependencyCoordinate)); } - - if (!userDevProfile.getNeoForm().isPresent()) { + + if (!userDevProfile.getNeoForm().isPresent()) + { throw new IllegalStateException("Userdev configuration spec has no MCP version. As of now this is not supported!"); } final NeoFormRuntimeDefinition neoFormRuntimeDefinition = neoFormRuntimeExtension.maybeCreate(builder -> { builder.withNeoFormDependency(userDevProfile.getNeoForm().get()) - .withDistributionType(DistributionType.JOINED) - .withAdditionalDependencies(getProject().files(userDevAdditionalDependenciesConfiguration)); + .withDistributionType(DistributionType.JOINED) + .withAdditionalDependencies(getProject().files(userDevAdditionalDependenciesConfiguration)) + .withStepsMutator(this.adaptNeoFormRuntime(spec, userDevProfile, userDevJar)); final FileTree accessTransformerFiles = userDevJar.matching(filter -> filter.include(userDevProfile.getAccessTransformerDirectory().get() + "/**")); @@ -69,36 +98,235 @@ public UserDevRuntimeExtension(Project project) { builder.withPostTaskAdapter("patch", createPatchAdapter(userDevJar, userDevProfile.getSourcePatchesDirectory().get())); - builder.withTaskCustomizer("inject", InjectZipContent.class, task -> { - FileTree injectionDirectoryTree; - if (userDevProfile.getInjectedFilesDirectory().isPresent()) { - injectionDirectoryTree = getProject().fileTree(new File(userDevProfile.getInjectedFilesDirectory().get())); - } else { - injectionDirectoryTree = null; - } + if (!useCombinedJarWithNeoForgeOnRecompile(userDevProfile)) + { + builder.withTaskCustomizer("inject", InjectZipContent.class, task -> { + FileTree injectionDirectoryTree; + if (userDevProfile.getInjectedFilesDirectory().isPresent()) + { + injectionDirectoryTree = getProject().fileTree(new File(userDevProfile.getInjectedFilesDirectory().get())); + } + else + { + injectionDirectoryTree = null; + } - configureNeoforgeInjects( + configureNeoforgeInjects( task, injectionDirectoryTree, ConfigurationUtils.getArtifactProvider(getProject(), "NeoForgeSourceLookupFor" + spec.getIdentifier(), userDevProfile.getSourcesJarArtifactCoordinate()), ConfigurationUtils.getArtifactProvider(getProject(), "NeoForgeRawLookupFor" + spec.getIdentifier(), userDevProfile.getUniversalJarArtifactCoordinate()) - ); - }); + ); + }); + } else if (decompilerSubsystemConfiguration.getIsDisabled().get()){ + builder.withPostTaskAdapter("downloadClient", new TaskTreeAdapter() { + @Override + public @NotNull TaskProvider adapt( + final Definition definition, + final Provider previousTasksOutput, + final File runtimeWorkspace, + final Map> gameArtifacts, + final Map mappingVersionData, + final Consumer> dependentTaskConfigurationHandler) + { + var stripper = definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), + "stripClientFinals"), StripFinalFromParametersTask.class, task -> { + task.getInput().set(previousTasksOutput.flatMap(WithOutput::getOutput)); + }); + dependentTaskConfigurationHandler.accept(stripper); + return stripper; + } + }); + + builder.withPostTaskAdapter("downloadServer", new TaskTreeAdapter() { + @Override + public @NotNull TaskProvider adapt( + final Definition definition, + final Provider previousTasksOutput, + final File runtimeWorkspace, + final Map> gameArtifacts, + final Map mappingVersionData, + final Consumer> dependentTaskConfigurationHandler) + { + var stripper = definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), + "stripServerFinals"), StripFinalFromParametersTask.class, task -> { + task.getInput().set(previousTasksOutput.flatMap(WithOutput::getOutput)); + }); + dependentTaskConfigurationHandler.accept(stripper); + return stripper; + } + }); + + builder.withPostTaskAdapter("setup", (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> { + final AccessTransformers userAts = minecraftExtension.getAccessTransformers(); + + if (userAts.getFiles().isEmpty()) { + return null; + } + + final TaskProvider task = CommonRuntimeTaskUtils.createBinaryAccessTransformer( + definition, + "", + userAts.getFiles().getAsFileTree() + ); + + task.configure(t -> { + t.getInputFile().set(previousTasksOutput.flatMap(WithOutput::getOutput)); + t.dependsOn(previousTasksOutput); + }); + + dependentTaskConfigurationHandler.accept(task); + + return task; + }); + } }); spec.setMinecraftVersion(neoFormRuntimeDefinition.getSpecification().getMinecraftVersion()); + if (!useCombinedJarWithNeoForgeOnRecompile(userDevProfile)) + { + //Create the client-extra jar dependency. + final Dependency clientExtraJar = spec.getProject().getDependencies().create( + ExtraJarDependencyManager.generateClientCoordinateFor(spec.getMinecraftVersion()) + ); + + //Add it as a user dev dependency, this will trigger replacement, which will need to be addressed down-below. + userDevAdditionalDependenciesConfiguration.getDependencies().add( + clientExtraJar + ); + } + return new UserDevRuntimeDefinition( - spec, - neoFormRuntimeDefinition, - userDevJar, + spec, + neoFormRuntimeDefinition, + userDevJar, + userDevProfile, + userDevAdditionalDependenciesConfiguration + ); + } + + private @NotNull BiConsumer, Map> adaptNeoFormRuntime( + final UserDevRuntimeSpecification spec, + final UserdevProfile userDevProfile, + final FileTree userDevJar) + { + return (steps, functions) -> { + if (!useCombinedJarWithNeoForgeOnRecompile(userDevProfile)) + { + return; + } + + //We use a new PROCESS_JAR from installer tools. + //So we yeet, merge, merge mappings, and rename + //Additionally we need to remove the decompile, as it requires the rename to exist, we just rebuild it. + steps.removeIf(step -> step.getType().equals("strip")); + steps.removeIf(step -> step.getType().equals("merge")); + steps.removeIf(step -> step.getType().equals("mergeMappings")); + steps.removeIf(step -> step.getType().equals("rename")); + + final int decompileIndex = ListUtils.removeIfAndReturnIndex( + steps, step -> step.getType().equals("decompile") + ); + + //Now read the decompile step, using the setup output as its input. + steps.add( + decompileIndex, + new NeoFormConfigConfigurationSpecV1.Step( + "decompile", "decompile", + Map.of( + "libraries", "{listLibrariesOutput}", + "input", "{setupOutput}" + ) + ) + ); + + final Subsystems subsystems = spec.getProject().getExtensions().getByType(Subsystems.class); + final SetupConfiguration configuration = buildSetupConfiguration( userDevProfile, - userDevAdditionalDependenciesConfiguration + userDevJar + ); + + //Register the setup function: + functions.put( + "setup", + new NeoFormConfigConfigurationSpecV1.Function( + subsystems.getTools().getInstallerTools().get(), + configuration.arguments() + ) + ); + + //And we inject the setup step, at the same index as decompile which will inject it before that. + steps.add( + decompileIndex, + new NeoFormConfigConfigurationSpecV1.Step( + "setup", "setup", + configuration.values() + ) + ); + }; + } + + private record SetupConfiguration( + List arguments, + Map values) {} + + private SetupConfiguration buildSetupConfiguration(final UserdevProfile userDevProfile, final FileTree userDevJar) + { + final Decompiler decompilerSubsystemConfiguration = getProject().getExtensions().getByType(Subsystems.class).getDecompiler(); + if (decompilerSubsystemConfiguration.getIsDisabled().get() && useCombinedJarWithNeoForgeOnRecompile(userDevProfile)) + { + return new SetupConfiguration( + List.of( + "--task", "PROCESS_MINECRAFT_JAR", + "--input", "{client}", + "--input", "{server}", + "--output", "{output}", + "--input-mappings", "{clientMappings}", + "--neoform-data", "{neoform}", + "--apply-patches", "{patches}" + ), + Map.of( + "client", "{downloadClientOutput}", + "clientMappings", "{downloadClientMappingsOutput}", + "server", "{downloadServerOutput}", + "neoform", "{neoform}", + "patches", userDevProfile.getBinaryPatchFile() + .map(patchFilePath -> userDevJar + .matching(matcher -> matcher.include(patchFilePath)) + .getSingleFile()).get().getAbsolutePath() + ) + ); + } + + return new SetupConfiguration( + List.of( + "--task", "PROCESS_MINECRAFT_JAR", + "--input", "{client}", + "--input", "{server}", + "--output", "{output}", + "--input-mappings", "{clientMappings}", + "--neoform-data", "{neoform}" + ), + Map.of( + "client", "{downloadClientOutput}", + "clientMappings", "{downloadClientMappingsOutput}", + "server", "{downloadServerOutput}", + "neoform", "{neoform}" + ) ); } + private boolean useCombinedJarWithNeoForgeOnRecompile(final UserdevProfile userDevProfile) + { + return userDevProfile.getFeatures() + .flatMap(UserdevProfile.Features::getUsesCombinedBinaryPatches) + .getOrElse(false); + } + @Override - protected void afterRegistration(UserDevRuntimeDefinition runtime) { + protected void afterRegistration(UserDevRuntimeDefinition runtime) + { final RunTypeManager runTypes = getProject().getExtensions().getByType(RunTypeManager.class); runtime.getUserdevConfiguration().getRunTypes().forEach((type) -> { TypesUtil.registerWithPotentialPrefix(runTypes, runtime.getSpecification().getIdentifier(), type.getName(), type::copyTo); @@ -106,65 +334,93 @@ protected void afterRegistration(UserDevRuntimeDefinition runtime) { final Conventions conventions = getProject().getExtensions().getByType(Subsystems.class).getConventions(); if (conventions.getIsEnabled().get() - && conventions.getRuns().getIsEnabled().get() - && conventions.getRuns().getShouldDefaultRunsBeCreated().get()) { + && conventions.getRuns().getIsEnabled().get() + && conventions.getRuns().getShouldDefaultRunsBeCreated().get()) + { final RunManager runs = getProject().getExtensions().getByType(RunManager.class); runtime.getUserdevConfiguration().getRunTypes().forEach(runType -> { - if (runs.getNames().contains(runType.getName())) { + if (runs.getNames().contains(runType.getName())) + { return; } - try { + try + { runs.create(runType.getName()); - } catch (IllegalStateException ignored) { + } + catch (IllegalStateException ignored) + { //thrown when the dependency is added lazily. This is fine. } - }); } + //After the project evaluation completes, we can register the binary patch mode if the decompiler is enabled and we support it + ProjectUtils.afterEvaluate( + getProject(), + () -> bakeDefinition(runtime) + ); + } + + private void bakeDefinition(UserDevRuntimeDefinition definition) + { + final Decompiler decompilerSubsystemConfiguration = this.getProject().getExtensions().getByType(Subsystems.class).getDecompiler(); + if (decompilerSubsystemConfiguration.getIsDisabled().get() && useCombinedJarWithNeoForgeOnRecompile(definition.getUserdevConfiguration())) + { + definition.getNeoFormRuntimeDefinition().getRawJarTask().configure(task -> { + task.getInput().set(definition.getNeoFormRuntimeDefinition().getTask("setup").flatMap(WithOutput::getOutput)); + }); + } } @Override - protected UserDevRuntimeSpecification.Builder createBuilder() { + protected UserDevRuntimeSpecification.Builder createBuilder() + { return UserDevRuntimeSpecification.Builder.from(getProject()); } - private TaskTreeAdapter createPatchAdapter(FileTree userDevArchive, String patchDirectory) { - return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> definition.getSpecification().getProject().getTasks().register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), "patchUserDev"), Patch.class, task -> { - task.getInput().set(previousTasksOutput.flatMap(WithOutput::getOutput)); - task.getPatchArchive().from(userDevArchive); - task.getPatchDirectory().set(patchDirectory); - }); + private TaskTreeAdapter createPatchAdapter(FileTree userDevArchive, String patchDirectory) + { + return (definition, previousTasksOutput, runtimeWorkspace, gameArtifacts, mappingVersionData, dependentTaskConfigurationHandler) -> definition.getSpecification() + .getProject() + .getTasks() + .register(CommonRuntimeUtils.buildTaskName(definition.getSpecification(), "patchUserDev"), Patch.class, task -> { + task.getInput().set(previousTasksOutput.flatMap(WithOutput::getOutput)); + task.getPatchArchive().from(userDevArchive); + task.getPatchDirectory().set(patchDirectory); + }); } /* * Configures the inject task, which runs right before patching, to also include the content that Neoforge * adds to the Minecraft jar, such as the Neoforge sources and resources. */ - private void configureNeoforgeInjects(InjectZipContent task, - @Nullable FileTree userDevInjectDir, - Provider sourcesInjectArtifact, - Provider resourcesInjectArtifact) { + private void configureNeoforgeInjects( + InjectZipContent task, + @Nullable FileTree userDevInjectDir, + Provider sourcesInjectArtifact, + Provider resourcesInjectArtifact) + { - if (userDevInjectDir != null) { + if (userDevInjectDir != null) + { task.injectFileTree(userDevInjectDir); } - if (sourcesInjectArtifact.isPresent()) { + if (sourcesInjectArtifact.isPresent()) + { task.injectZip(sourcesInjectArtifact, filter -> { filter.include("net/**"); }); } - if (resourcesInjectArtifact.isPresent()) { + if (resourcesInjectArtifact.isPresent()) + { task.injectZip(resourcesInjectArtifact, filter -> { filter.exclude("**/*.class"); filter.exclude("META-INF/**/*.DSA"); filter.exclude("**/*.SF"); }); } - } - } diff --git a/utils/build.gradle b/utils/build.gradle index 619b4d729..f7a340d33 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -6,4 +6,8 @@ dependencies { api "commons-io:commons-io:${project.commons_io_version}" api "net.minecraftforge:srgutils:${project.srgutils_version}" api "de.siegmar:fastcsv:${project.fastcsv_version}" + + api "org.ow2.asm:asm:${project.asm_version}" + api "org.ow2.asm:asm-commons:${project.asm_version}" + api "org.ow2.asm:asm-tree:${project.asm_version}" } \ No newline at end of file diff --git a/utils/src/main/java/net/neoforged/gradle/util/AdaptingZipBuildingFileTreeVisitor.java b/utils/src/main/java/net/neoforged/gradle/util/AdaptingZipBuildingFileTreeVisitor.java index 2c232887b..3b6e01e89 100644 --- a/utils/src/main/java/net/neoforged/gradle/util/AdaptingZipBuildingFileTreeVisitor.java +++ b/utils/src/main/java/net/neoforged/gradle/util/AdaptingZipBuildingFileTreeVisitor.java @@ -1,6 +1,7 @@ package net.neoforged.gradle.util; import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.tasks.OutputFile; import java.io.IOException; import java.io.OutputStream; @@ -13,9 +14,9 @@ */ public class AdaptingZipBuildingFileTreeVisitor extends ZipBuildingFileTreeVisitor { - private final BiConsumer fileAdapter; + private final Adapter fileAdapter; - public AdaptingZipBuildingFileTreeVisitor(ZipOutputStream outputZipStream, BiConsumer fileAdapter) { + public AdaptingZipBuildingFileTreeVisitor(ZipOutputStream outputZipStream, Adapter fileAdapter) { super(outputZipStream); this.fileAdapter = fileAdapter; } @@ -31,4 +32,8 @@ public void visitFile(FileVisitDetails fileVisitDetails) { throw new RuntimeException("Could not create zip file: " + fileVisitDetails.getRelativePath().getPathString(), e); } } + + public interface Adapter { + void accept(FileVisitDetails details, OutputStream outputStream) throws IOException; + } } diff --git a/utils/src/main/java/net/neoforged/gradle/util/ClassVisitingFileTreeVisitor.java b/utils/src/main/java/net/neoforged/gradle/util/ClassVisitingFileTreeVisitor.java new file mode 100644 index 000000000..3e57b33e9 --- /dev/null +++ b/utils/src/main/java/net/neoforged/gradle/util/ClassVisitingFileTreeVisitor.java @@ -0,0 +1,35 @@ +package net.neoforged.gradle.util; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; + +import java.io.InputStream; +import java.util.zip.ZipOutputStream; + +public class ClassVisitingFileTreeVisitor extends AdaptingZipBuildingFileTreeVisitor +{ + public ClassVisitingFileTreeVisitor(final ZipOutputStream outputZipStream, VisitorBuilder builder) + { + super(outputZipStream, (details, outputStream) -> { + if (!details.getName().endsWith(".class")) + { + details.copyTo(outputStream); + return; + } + + try (InputStream stream = details.open()) { + ClassReader cr = new ClassReader(stream); + ClassWriter cw = new ClassWriter(0); + ClassVisitor cv = builder.build(Opcodes.ASM9, cw); + cr.accept(cv, 0); + outputStream.write(cw.toByteArray()); + } + }); + } + + public interface VisitorBuilder { + ClassVisitor build(int apiVersion, ClassVisitor sink); + } +}