diff --git a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java index ae942d764..116258821 100644 --- a/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java +++ b/src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java @@ -13,6 +13,7 @@ import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.artifacts.ExternalModuleDependency; +import org.gradle.api.provider.Provider; /// [Project][org.gradle.api.Project]-specific additions for the Minecraft extension. These will be accessible from the /// `minecraft` DSL object within your project's buildscript. @@ -27,7 +28,7 @@ public interface MinecraftExtensionForProject extends MinecraftExtension, Minecr /// @return The dependency /// @see Declaring Dependencies /// in Gradle - ExternalModuleDependency dependency( + Provider dependency( Object value, @DelegatesTo(ExternalModuleDependency.class) @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency") @@ -42,7 +43,7 @@ ExternalModuleDependency dependency( /// @return The dependency /// @see Declaring Dependencies /// in Gradle - default ExternalModuleDependency dependency(Object value, Action action) { + default Provider dependency(Object value, Action action) { return this.dependency(value, Closures.action(this, action)); } @@ -52,7 +53,7 @@ default ExternalModuleDependency dependency(Object value, ActionDeclaring Dependencies /// in Gradle - default ExternalModuleDependency dependency(Object value) { + default Provider dependency(Object value) { return this.dependency(value, Closures.empty(this)); } } diff --git a/src/main/java/net/minecraftforge/gradle/internal/Constants.java b/src/main/java/net/minecraftforge/gradle/internal/Constants.java index 40c8c5e29..0ce575537 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/Constants.java +++ b/src/main/java/net/minecraftforge/gradle/internal/Constants.java @@ -18,7 +18,7 @@ final class Constants { static final String SLIMELAUNCHER_MAIN = "net.minecraftforge.launcher.Main"; static final String MAVENIZER_NAME = "mavenizer"; - static final String MAVENIZER_VERSION = "0.4.19"; + static final String MAVENIZER_VERSION = "0.4.20"; static final String MAVENIZER_DL_URL = "https://maven.minecraftforge.net/net/minecraftforge/minecraft-mavenizer/" + MAVENIZER_VERSION + "/minecraft-mavenizer-" + MAVENIZER_VERSION + ".jar"; static final int MAVENIZER_JAVA_VERSION = 25; static final String MAVENIZER_MAIN = "net.minecraftforge.mcmaven.cli.Main"; diff --git a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java index 5b55f3fe1..9478dc8ca 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java +++ b/src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java @@ -140,7 +140,6 @@ void mavenizerOutOfDate(boolean throwIt, Object dependency) { .severity(Severity.ERROR) .solution("Re-import your project in your IDE, as this will automatically synchronize the Mavenizer.") .solution("Run `gradlew` with no arguments, as this will automatically synchronize the Mavenizer.") - .solution("Manually run the `syncMavenizer` task, located in the 'Build Setup' group.") .solution("Temporary revert any edits to the Minecraft dependency until the Mavenizer is re-run.") .solution(HELP_MESSAGE); diff --git a/src/main/java/net/minecraftforge/gradle/internal/MavenizerValueSource.java b/src/main/java/net/minecraftforge/gradle/internal/MavenizerValueSource.java new file mode 100644 index 000000000..68253d906 --- /dev/null +++ b/src/main/java/net/minecraftforge/gradle/internal/MavenizerValueSource.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ +package net.minecraftforge.gradle.internal; + +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.ValueSource; +import org.gradle.api.provider.ValueSourceParameters; +import org.gradle.process.ExecOperations; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + +abstract class MavenizerValueSource implements ValueSource { + interface Parameters extends ValueSourceParameters { + ConfigurableFileCollection getClasspath(); + RegularFileProperty getJavaLauncher(); + ListProperty getArguments(); + } + + private final ExecOperations execOps; + private final static Logger logger = LoggerFactory.getLogger(MavenizerValueSource.class); + + @Inject + public MavenizerValueSource(ExecOperations execOps) { + this.execOps = execOps; + } + + private void log(String line) { + logger.info(line); + } + + @Override + @Nullable + public Boolean obtain() { + this.execOps.javaexec(spec -> { + var params = this.getParameters(); + spec.setClasspath(params.getClasspath()); + spec.setExecutable(params.getJavaLauncher().get()); + spec.setArgs(params.getArguments().get()); + + log("Executing Mavenizer: "); + var itr = params.getClasspath().iterator(); + log(" Classpath: " + itr.next().getAbsolutePath()); + while (itr.hasNext()) + log(" " + itr.next().getAbsolutePath()); + + log(" Java: " + params.getJavaLauncher().get().getAsFile().getAbsolutePath()); + var args = params.getArguments().get(); + var prefix = " Arguments: "; + for (int x = 0; x < args.size(); x++) { + var current = args.get(x); + var next = args.size() > x + 1 ? args.get(x + 1) : null; + var line = current; + if (current.startsWith("--") && next != null && !next.startsWith("--")) { + x++; + line += ' ' + next; + } + log(prefix + line); + prefix = " "; + } + }).assertNormalExitValue(); + return false; + } +} diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyImpl.java index 08ebab257..1a87ea9b6 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyImpl.java @@ -10,7 +10,6 @@ import net.minecraftforge.gradle.MinecraftMappings; import net.minecraftforge.gradle.SlimeLauncherOptions; import net.minecraftforge.gradleutils.shared.Closures; -import org.gradle.api.Action; import org.gradle.api.InvalidUserCodeException; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.NamedDomainObjectSet; @@ -19,14 +18,10 @@ import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.ExternalModuleDependency; import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.api.artifacts.type.ArtifactTypeDefinition; -import org.gradle.api.attributes.Attribute; -import org.gradle.api.attributes.AttributeContainer; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.Directory; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.file.ProjectLayout; -import org.gradle.api.file.RegularFileProperty; import org.gradle.api.flow.FlowProviders; import org.gradle.api.flow.FlowScope; import org.gradle.api.model.ObjectFactory; @@ -34,7 +29,6 @@ import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskProvider; import org.jspecify.annotations.Nullable; import javax.inject.Inject; @@ -44,7 +38,6 @@ abstract class MinecraftDependencyImpl implements MinecraftDependencyInternal { // These can be nullable due to configuration caching. private transient @Nullable ExternalModuleDependency delegate; - private transient @Nullable TaskProvider mavenizer; private transient @Nullable NamedDomainObjectContainer runs; // Minecraft extension @@ -129,12 +122,6 @@ private boolean hasAccessTransformers() { return this.delegate; } - // Can be nullable due to configuration caching. - @Override - public @Nullable TaskProvider asTask() { - return this.mavenizer; - } - @Override public ExternalModuleDependency init(Object dependencyNotation, Closure closure) { this.runs = getObjects().domainObjectContainer(SlimeLauncherOptionsImpl.class); @@ -153,8 +140,6 @@ public ExternalModuleDependency init(Object dependencyNotation, Closure closu return module; })); - this.mavenizer = SyncMavenizer.register(getProject(), dependency, this.mappings, this.accessTransformer, mavenizerOutput); - this.asString.set(dependency.toString()); this.asPath.set(Util.pathify(dependency)); this.module.set(dependency.getModule()); @@ -190,10 +175,6 @@ public void handle(Configuration configuration) { @Override public void handle(NamedDomainObjectSet sourceSets, NamedDomainObjectSet allSourceSets) { - allSourceSets.all(sourceSet -> - getProject().getTasks().named(sourceSet.getTaskName("sync", "mavenizer"), task -> task.dependsOn(this.mavenizer)) - ); - var asString = this.asString.get(); var dependencyOutput = this.mavenizerOutput.dir(this.asPath); getFlowScope().always(ForgeGradleFlowAction.MavenizerSyncCheck.class, spec -> @@ -221,6 +202,10 @@ public void handle(NamedDomainObjectSet sourceSets, NamedDomainObject }); }); + finalizeAccessTransformers(sourceSets); + } + + void finalizeAccessTransformers(NamedDomainObjectSet sourceSets) { if (this.accessTransformer.isEmpty() && !this.accessTransformerPath.isPresent()) { this.accessTransformer.convention(minecraft.getAccessTransformer()); this.accessTransformerPath.convention(minecraft.getAccessTransformerPath()); diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyInternal.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyInternal.java index bc664c0e1..3a0d9d2d8 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyInternal.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyInternal.java @@ -55,9 +55,6 @@ static boolean is(Dependency dependency) { // Can be nullable due to configuration caching. @Nullable ExternalModuleDependency asDependency(); - // Can be nullable due to configuration caching. - @Nullable TaskProvider asTask(); - default Closure closure(Closure closure) { return closure.rehydrate(closure.getDelegate(), new ClosureOwnerImpl.MinecraftDependencyImpl(closure.getOwner(), this), closure.getThisObject()); } diff --git a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java index b4986fa25..343aa2da1 100644 --- a/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java +++ b/src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java @@ -39,9 +39,12 @@ import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.reflect.TypeOf; import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.toolchain.JavaLauncher; +import org.gradle.jvm.toolchain.JavaToolchainService; import org.gradle.plugins.ide.eclipse.model.EclipseModel; import org.jetbrains.annotations.UnmodifiableView; import org.jspecify.annotations.Nullable; @@ -50,17 +53,22 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.function.Predicate; import java.util.stream.Collectors; abstract class MinecraftExtensionImpl implements MinecraftExtensionInternal { + private static final String MAVENIZER_REPO_NAME = "MinecraftMavenizer"; + private final DirectoryProperty mavenizerOutput = getObjects().directoryProperty(); private final Property mappings = getObjects().property(MinecraftMappingsInternal.class); private final ForgeGradleProblems problems = getObjects().newInstance(ForgeGradleProblems.class); + protected final ForgeGradlePlugin plugin; + protected abstract @Inject ObjectFactory getObjects(); protected abstract @Inject ProviderFactory getProviders(); @@ -85,6 +93,7 @@ static void register( @Inject public MinecraftExtensionImpl(ForgeGradlePlugin plugin) { + this.plugin = plugin; this.mavenizerOutput.convention(plugin.localCaches().dir("mavenizer/output").map(this.problems.ensureFileLocation())); } @@ -101,7 +110,7 @@ public DirectoryProperty getMavenizerOutput() { @Override public Action getMavenizer() { return maven -> { - maven.setName("MinecraftMavenizer"); + maven.setName(MAVENIZER_REPO_NAME); maven.setUrl(this.getMavenizerOutput()); }; } @@ -306,26 +315,12 @@ private void finish(Project project) { var sourceSetsDir = this.getObjects().directoryProperty().value(this.getProjectLayout().getBuildDirectory().dir("sourceSets")); var mergeSourceSets = this.problems.test("net.minecraftforge.gradle.merge-source-sets"); sourceSets.all(sourceSet -> { - var sourceSetName = sourceSet.getName(); - var syncMavenizer = Util.runFirst(project, project.getTasks().register(sourceSet.getTaskName("sync", "mavenizer"), task -> { - task.setGroup("Build Setup"); - task.setDescription("Synchronizes the Mavenizer output for source set '" + sourceSetName + '.'); - })); - - try { - project.getTasks().named(sourceSet.getCompileJavaTaskName(), task -> task.dependsOn(syncMavenizer)); - } catch (UnknownTaskException ignored) { } - if (mergeSourceSets) { // This is documented in SourceSetOutput's javadoc comment var unifiedDir = sourceSetsDir.dir(sourceSet.getName()); sourceSet.getOutput().setResourcesDir(unifiedDir); sourceSet.getJava().getDestinationDirectory().set(unifiedDir); } - - project.getPluginManager().withPlugin("eclipse", appliedPlugin -> - project.getExtensions().configure(EclipseModel.class, eclipse -> eclipse.synchronizationTasks(syncMavenizer)) - ); }); project.getPluginManager().withPlugin("eclipse", eclipsePlugin -> { @@ -375,8 +370,9 @@ public NamedDomainObjectContainer getRuns() { return this.runs; } + @SuppressWarnings({"UnstableApiUsage"}) @Override - public ExternalModuleDependency dependency( + public Provider dependency( Object value, @DelegatesTo(ExternalModuleDependency.class) @ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.MinecraftDependency.ClosureOwner") @@ -389,7 +385,64 @@ public ExternalModuleDependency dependency( var minecraftDependency = this.getObjects().newInstance(MinecraftDependencyImpl.class, this.getMavenizerOutput()); this.minecraftDependencies.add(minecraftDependency); - return minecraftDependency.init(value, closure); + var dep = minecraftDependency.init(value, closure); + var mavenizer = this.getProviders().of(MavenizerValueSource.class, spec -> { + spec.parameters(params -> { + var tool = this.plugin.getTool(Tools.MAVENIZER); + params.getClasspath().setFrom(tool.getClasspath()); + params.getJavaLauncher().set(tool.getJavaLauncher().map(JavaLauncher::getExecutablePath)); + params.getArguments().set(this.getProviders().provider(() -> { + var toolCache = this.plugin.globalCaches() + .dir(tool.getName().toLowerCase(Locale.ENGLISH)) + .map(this.problems.ensureFileLocation()); + var cache = toolCache.get().dir("caches").getAsFile().getAbsolutePath(); + + var ret = new ArrayList(); + ret.addAll(List.of( + "--maven", + "--cache", cache, + "--jdk-cache", cache, + "--output", this.getMavenizerOutput().get().getAsFile().getAbsolutePath(), + "--artifact", dep.getModule().toString(), + "--version", Objects.requireNonNull(dep.getVersion()), + "--global-auxiliary-variants" + )); + + // If we are finding the access transformer from sourcesets, just find from any source set + // We can't filter by configurations becase the config cache doesn't like that. + // So if users fuck up, then we can output a warning, or they can manually set the AT file. + // This is a 'best effort' + var sourceSets = getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets(); + minecraftDependency.finalizeAccessTransformers(sourceSets); + + for (var at : minecraftDependency.getAccessTransformer()) { + //System.out.println("Access Transformer: " + at); + ret.add("--access-transformer"); + ret.add(at.getAbsolutePath()); + } + + var mappings = minecraftDependency.getMappings(); + if ("parchment".equals(mappings.getChannel())) + ret.addAll(List.of("--parchment", mappings.getVersion())); + + for (var repo : this.getRepositories()) { + if (MAVENIZER_REPO_NAME.equals(repo.getName())) + continue; + var url = repo.getUrl().toString(); + if (!url.endsWith("/")) + url += '/'; + ret.add("--repository"); + ret.add(repo.getName() + ',' + url); + } + return ret; + })); + }); + }); + + return this.getProviders().provider(() -> { + mavenizer.get();// Invoke mavenizer, it should be invoked already by gradle config cache, but Force it to be + return dep; + }); } private void checkRepos(List repos) { diff --git a/src/main/java/net/minecraftforge/gradle/internal/SyncMavenizer.java b/src/main/java/net/minecraftforge/gradle/internal/SyncMavenizer.java deleted file mode 100644 index 0decd8d94..000000000 --- a/src/main/java/net/minecraftforge/gradle/internal/SyncMavenizer.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ -package net.minecraftforge.gradle.internal; - -import net.minecraftforge.gradle.MinecraftExtensionForProject; -import net.minecraftforge.gradle.MinecraftMappings; -import org.codehaus.groovy.runtime.StringGroovyMethods; -import org.gradle.api.Project; -import org.gradle.api.UnknownDomainObjectException; -import org.gradle.api.artifacts.ExternalModuleDependency; -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.api.artifacts.repositories.MavenArtifactRepository; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.Directory; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.file.RegularFile; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.provider.ListProperty; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.InputFiles; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.TaskProvider; -import org.gradle.process.ExecResult; -import org.gradle.work.DisableCachingByDefault; - -import javax.inject.Inject; -import java.io.IOException; -import java.util.List; - -@DisableCachingByDefault(because = "Mavenizer uses its own in-house caching") -abstract class SyncMavenizer extends ToolExec { - static TaskProvider register(Project project, ExternalModuleDependency dependency, Provider mappings, ConfigurableFileCollection accessTransformer, Provider output) { - var version = dependency.getVersion(); - var taskName = "syncMavenizerFor" - + StringGroovyMethods.capitalize(dependency.getName()) - + (version == null ? "" : version.replace(".", "").replace('-', '_')); - - var tasks = project.getTasks(); - return tasks.getNames().contains(taskName) - ? tasks.named(taskName, SyncMavenizer.class) - : tasks.register(taskName, SyncMavenizer.class, task -> { - task.getOutput().set(output); - task.getModule().set(dependency.getModule()); - task.getVersion().set(dependency.getVersion()); - task.getMappings().set(mappings); - task.getAccessTransformer().setFrom(accessTransformer); - }); - } - - protected abstract @Internal DirectoryProperty getCaches(); - - protected abstract @Internal DirectoryProperty getOutput(); - - protected abstract @Input Property getModule(); - - protected abstract @Input Property getVersion(); - - protected abstract @Input Property getMappings(); - - protected abstract @InputFiles @Optional ConfigurableFileCollection getAccessTransformer(); - - protected abstract @Input @Optional ListProperty getRepositories(); - - private void addRepositories(Iterable repositories) { - for (var repository : repositories) { - if (repository.getName().equals("MinecraftMavenizer")) - continue; - - String url; - { - var s = repository.getUrl().toString(); - if (!s.endsWith("/")) - s += "/"; - url = s; - } - - this.getRepositories().add(this.getProviders().provider(() -> repository.getName() + ',' + url)); - } - } - - @Inject - public SyncMavenizer() { - super(Tools.MAVENIZER); - - this.getCaches().convention(this.defaultToolDir.dir("caches")); - - var minecraft = ((MinecraftExtensionInternal.ForProject) getProject().getExtensions().getByType(MinecraftExtensionForProject.class)); - this.addRepositories(minecraft.getRepositories()); - } - - @Override - protected ExecResult exec() throws IOException { - return super.exec().rethrowFailure().assertNormalExitValue(); - } - - @Override - protected void addArguments() { - super.addArguments(); - - this.args("--maven"); - this.args("--cache", this.getCaches()); - this.args("--output", this.getOutput()); - this.args("--jdk-cache", this.getCaches()); - this.args("--artifact", this.getModule()); - this.args("--version", this.getVersion()); - this.args("--access-transformer", this.getAccessTransformer()); - this.args("--global-auxiliary-variants"); - - if ("parchment".equals(this.getMappings().get().getChannel())) { - this.args("--parchment", this.getMappings().get().getVersion()); - } - - for (var repository : this.getRepositories().getOrElse(List.of())) { - this.args("--repository", repository); - } - } -}