Skip to content

Commit 6153d64

Browse files
authored
Migrate Mavenizer to ValueSource (#1033)
- Moves the invocation of Mavenizer to a ValueSource. - ValueSource allows for the execution of external processes without invalidating the configuration cache. - This allows Mavenizer to generate the local repository before it is serialized to the configuration cache. - Removes `syncMavenizer` and related tasks. - Technically a breaking change, but these tasks will never return, even in ForgeGradle 8. It's better to remove them now before release. - Bumps Mavenizer to fix client-extra jar issue on fresh projects.
1 parent 8a7de14 commit 6153d64

8 files changed

Lines changed: 149 additions & 167 deletions

src/main/java/net/minecraftforge/gradle/MinecraftExtensionForProject.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.gradle.api.Action;
1414
import org.gradle.api.NamedDomainObjectContainer;
1515
import org.gradle.api.artifacts.ExternalModuleDependency;
16+
import org.gradle.api.provider.Provider;
1617

1718
/// [Project][org.gradle.api.Project]-specific additions for the Minecraft extension. These will be accessible from the
1819
/// `minecraft` DSL object within your project's buildscript.
@@ -27,7 +28,7 @@ public interface MinecraftExtensionForProject extends MinecraftExtension, Minecr
2728
/// @return The dependency
2829
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
2930
/// in Gradle</a>
30-
ExternalModuleDependency dependency(
31+
Provider<ExternalModuleDependency> dependency(
3132
Object value,
3233
@DelegatesTo(ExternalModuleDependency.class)
3334
@ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.ClosureOwner.MinecraftDependency")
@@ -42,7 +43,7 @@ ExternalModuleDependency dependency(
4243
/// @return The dependency
4344
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
4445
/// in Gradle</a>
45-
default ExternalModuleDependency dependency(Object value, Action<? super ClosureOwner.MinecraftDependency> action) {
46+
default Provider<ExternalModuleDependency> dependency(Object value, Action<? super ClosureOwner.MinecraftDependency> action) {
4647
return this.dependency(value, Closures.action(this, action));
4748
}
4849

@@ -52,7 +53,7 @@ default ExternalModuleDependency dependency(Object value, Action<? super Closure
5253
/// @return The dependency
5354
/// @see <a href="https://docs.gradle.org/current/userguide/declaring_dependencies.html">Declaring Dependencies
5455
/// in Gradle</a>
55-
default ExternalModuleDependency dependency(Object value) {
56+
default Provider<ExternalModuleDependency> dependency(Object value) {
5657
return this.dependency(value, Closures.empty(this));
5758
}
5859
}

src/main/java/net/minecraftforge/gradle/internal/Constants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class Constants {
1818
static final String SLIMELAUNCHER_MAIN = "net.minecraftforge.launcher.Main";
1919

2020
static final String MAVENIZER_NAME = "mavenizer";
21-
static final String MAVENIZER_VERSION = "0.4.19";
21+
static final String MAVENIZER_VERSION = "0.4.20";
2222
static final String MAVENIZER_DL_URL = "https://maven.minecraftforge.net/net/minecraftforge/minecraft-mavenizer/" + MAVENIZER_VERSION + "/minecraft-mavenizer-" + MAVENIZER_VERSION + ".jar";
2323
static final int MAVENIZER_JAVA_VERSION = 25;
2424
static final String MAVENIZER_MAIN = "net.minecraftforge.mcmaven.cli.Main";

src/main/java/net/minecraftforge/gradle/internal/ForgeGradleProblems.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ void mavenizerOutOfDate(boolean throwIt, Object dependency) {
140140
.severity(Severity.ERROR)
141141
.solution("Re-import your project in your IDE, as this will automatically synchronize the Mavenizer.")
142142
.solution("Run `gradlew` with no arguments, as this will automatically synchronize the Mavenizer.")
143-
.solution("Manually run the `syncMavenizer` task, located in the 'Build Setup' group.")
144143
.solution("Temporary revert any edits to the Minecraft dependency until the Mavenizer is re-run.")
145144
.solution(HELP_MESSAGE);
146145

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) Forge Development LLC and contributors
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*/
5+
package net.minecraftforge.gradle.internal;
6+
7+
import org.gradle.api.file.ConfigurableFileCollection;
8+
import org.gradle.api.file.RegularFileProperty;
9+
import org.gradle.api.provider.ListProperty;
10+
import org.gradle.api.provider.ValueSource;
11+
import org.gradle.api.provider.ValueSourceParameters;
12+
import org.gradle.process.ExecOperations;
13+
import org.jspecify.annotations.Nullable;
14+
import org.slf4j.Logger;
15+
import org.slf4j.LoggerFactory;
16+
17+
import javax.inject.Inject;
18+
19+
abstract class MavenizerValueSource implements ValueSource<Boolean, MavenizerValueSource.Parameters> {
20+
interface Parameters extends ValueSourceParameters {
21+
ConfigurableFileCollection getClasspath();
22+
RegularFileProperty getJavaLauncher();
23+
ListProperty<String> getArguments();
24+
}
25+
26+
private final ExecOperations execOps;
27+
private final static Logger logger = LoggerFactory.getLogger(MavenizerValueSource.class);
28+
29+
@Inject
30+
public MavenizerValueSource(ExecOperations execOps) {
31+
this.execOps = execOps;
32+
}
33+
34+
private void log(String line) {
35+
logger.info(line);
36+
}
37+
38+
@Override
39+
@Nullable
40+
public Boolean obtain() {
41+
this.execOps.javaexec(spec -> {
42+
var params = this.getParameters();
43+
spec.setClasspath(params.getClasspath());
44+
spec.setExecutable(params.getJavaLauncher().get());
45+
spec.setArgs(params.getArguments().get());
46+
47+
log("Executing Mavenizer: ");
48+
var itr = params.getClasspath().iterator();
49+
log(" Classpath: " + itr.next().getAbsolutePath());
50+
while (itr.hasNext())
51+
log(" " + itr.next().getAbsolutePath());
52+
53+
log(" Java: " + params.getJavaLauncher().get().getAsFile().getAbsolutePath());
54+
var args = params.getArguments().get();
55+
var prefix = " Arguments: ";
56+
for (int x = 0; x < args.size(); x++) {
57+
var current = args.get(x);
58+
var next = args.size() > x + 1 ? args.get(x + 1) : null;
59+
var line = current;
60+
if (current.startsWith("--") && next != null && !next.startsWith("--")) {
61+
x++;
62+
line += ' ' + next;
63+
}
64+
log(prefix + line);
65+
prefix = " ";
66+
}
67+
}).assertNormalExitValue();
68+
return false;
69+
}
70+
}

src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyImpl.java

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import net.minecraftforge.gradle.MinecraftMappings;
1111
import net.minecraftforge.gradle.SlimeLauncherOptions;
1212
import net.minecraftforge.gradleutils.shared.Closures;
13-
import org.gradle.api.Action;
1413
import org.gradle.api.InvalidUserCodeException;
1514
import org.gradle.api.NamedDomainObjectContainer;
1615
import org.gradle.api.NamedDomainObjectSet;
@@ -19,22 +18,17 @@
1918
import org.gradle.api.artifacts.Dependency;
2019
import org.gradle.api.artifacts.ExternalModuleDependency;
2120
import org.gradle.api.artifacts.ModuleIdentifier;
22-
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
23-
import org.gradle.api.attributes.Attribute;
24-
import org.gradle.api.attributes.AttributeContainer;
2521
import org.gradle.api.file.ConfigurableFileCollection;
2622
import org.gradle.api.file.Directory;
2723
import org.gradle.api.file.DirectoryProperty;
2824
import org.gradle.api.file.ProjectLayout;
29-
import org.gradle.api.file.RegularFileProperty;
3025
import org.gradle.api.flow.FlowProviders;
3126
import org.gradle.api.flow.FlowScope;
3227
import org.gradle.api.model.ObjectFactory;
3328
import org.gradle.api.plugins.ExtensionAware;
3429
import org.gradle.api.provider.Property;
3530
import org.gradle.api.provider.Provider;
3631
import org.gradle.api.tasks.SourceSet;
37-
import org.gradle.api.tasks.TaskProvider;
3832
import org.jspecify.annotations.Nullable;
3933

4034
import javax.inject.Inject;
@@ -44,7 +38,6 @@
4438
abstract class MinecraftDependencyImpl implements MinecraftDependencyInternal {
4539
// These can be nullable due to configuration caching.
4640
private transient @Nullable ExternalModuleDependency delegate;
47-
private transient @Nullable TaskProvider<SyncMavenizer> mavenizer;
4841
private transient @Nullable NamedDomainObjectContainer<SlimeLauncherOptionsImpl> runs;
4942

5043
// Minecraft extension
@@ -129,12 +122,6 @@ private boolean hasAccessTransformers() {
129122
return this.delegate;
130123
}
131124

132-
// Can be nullable due to configuration caching.
133-
@Override
134-
public @Nullable TaskProvider<SyncMavenizer> asTask() {
135-
return this.mavenizer;
136-
}
137-
138125
@Override
139126
public ExternalModuleDependency init(Object dependencyNotation, Closure<?> closure) {
140127
this.runs = getObjects().domainObjectContainer(SlimeLauncherOptionsImpl.class);
@@ -153,8 +140,6 @@ public ExternalModuleDependency init(Object dependencyNotation, Closure<?> closu
153140
return module;
154141
}));
155142

156-
this.mavenizer = SyncMavenizer.register(getProject(), dependency, this.mappings, this.accessTransformer, mavenizerOutput);
157-
158143
this.asString.set(dependency.toString());
159144
this.asPath.set(Util.pathify(dependency));
160145
this.module.set(dependency.getModule());
@@ -190,10 +175,6 @@ public void handle(Configuration configuration) {
190175

191176
@Override
192177
public void handle(NamedDomainObjectSet<SourceSet> sourceSets, NamedDomainObjectSet<SourceSet> allSourceSets) {
193-
allSourceSets.all(sourceSet ->
194-
getProject().getTasks().named(sourceSet.getTaskName("sync", "mavenizer"), task -> task.dependsOn(this.mavenizer))
195-
);
196-
197178
var asString = this.asString.get();
198179
var dependencyOutput = this.mavenizerOutput.dir(this.asPath);
199180
getFlowScope().always(ForgeGradleFlowAction.MavenizerSyncCheck.class, spec ->
@@ -221,6 +202,10 @@ public void handle(NamedDomainObjectSet<SourceSet> sourceSets, NamedDomainObject
221202
});
222203
});
223204

205+
finalizeAccessTransformers(sourceSets);
206+
}
207+
208+
void finalizeAccessTransformers(NamedDomainObjectSet<SourceSet> sourceSets) {
224209
if (this.accessTransformer.isEmpty() && !this.accessTransformerPath.isPresent()) {
225210
this.accessTransformer.convention(minecraft.getAccessTransformer());
226211
this.accessTransformerPath.convention(minecraft.getAccessTransformerPath());

src/main/java/net/minecraftforge/gradle/internal/MinecraftDependencyInternal.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ static boolean is(Dependency dependency) {
5555
// Can be nullable due to configuration caching.
5656
@Nullable ExternalModuleDependency asDependency();
5757

58-
// Can be nullable due to configuration caching.
59-
@Nullable TaskProvider<SyncMavenizer> asTask();
60-
6158
default <R> Closure<R> closure(Closure<R> closure) {
6259
return closure.rehydrate(closure.getDelegate(), new ClosureOwnerImpl.MinecraftDependencyImpl(closure.getOwner(), this), closure.getThisObject());
6360
}

src/main/java/net/minecraftforge/gradle/internal/MinecraftExtensionImpl.java

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@
3939
import org.gradle.api.plugins.ExtensionAware;
4040
import org.gradle.api.plugins.JavaPluginExtension;
4141
import org.gradle.api.provider.Property;
42+
import org.gradle.api.provider.Provider;
4243
import org.gradle.api.provider.ProviderFactory;
4344
import org.gradle.api.reflect.TypeOf;
4445
import org.gradle.api.tasks.TaskProvider;
46+
import org.gradle.jvm.toolchain.JavaLauncher;
47+
import org.gradle.jvm.toolchain.JavaToolchainService;
4548
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
4649
import org.jetbrains.annotations.UnmodifiableView;
4750
import org.jspecify.annotations.Nullable;
@@ -50,17 +53,22 @@
5053
import java.util.ArrayList;
5154
import java.util.Collections;
5255
import java.util.List;
56+
import java.util.Locale;
5357
import java.util.Objects;
5458
import java.util.function.Predicate;
5559
import java.util.stream.Collectors;
5660

5761
abstract class MinecraftExtensionImpl implements MinecraftExtensionInternal {
62+
private static final String MAVENIZER_REPO_NAME = "MinecraftMavenizer";
63+
5864
private final DirectoryProperty mavenizerOutput = getObjects().directoryProperty();
5965

6066
private final Property<MinecraftMappingsInternal> mappings = getObjects().property(MinecraftMappingsInternal.class);
6167

6268
private final ForgeGradleProblems problems = getObjects().newInstance(ForgeGradleProblems.class);
6369

70+
protected final ForgeGradlePlugin plugin;
71+
6472
protected abstract @Inject ObjectFactory getObjects();
6573

6674
protected abstract @Inject ProviderFactory getProviders();
@@ -85,6 +93,7 @@ static void register(
8593

8694
@Inject
8795
public MinecraftExtensionImpl(ForgeGradlePlugin plugin) {
96+
this.plugin = plugin;
8897
this.mavenizerOutput.convention(plugin.localCaches().dir("mavenizer/output").map(this.problems.ensureFileLocation()));
8998
}
9099

@@ -101,7 +110,7 @@ public DirectoryProperty getMavenizerOutput() {
101110
@Override
102111
public Action<MavenArtifactRepository> getMavenizer() {
103112
return maven -> {
104-
maven.setName("MinecraftMavenizer");
113+
maven.setName(MAVENIZER_REPO_NAME);
105114
maven.setUrl(this.getMavenizerOutput());
106115
};
107116
}
@@ -306,26 +315,12 @@ private void finish(Project project) {
306315
var sourceSetsDir = this.getObjects().directoryProperty().value(this.getProjectLayout().getBuildDirectory().dir("sourceSets"));
307316
var mergeSourceSets = this.problems.test("net.minecraftforge.gradle.merge-source-sets");
308317
sourceSets.all(sourceSet -> {
309-
var sourceSetName = sourceSet.getName();
310-
var syncMavenizer = Util.runFirst(project, project.getTasks().register(sourceSet.getTaskName("sync", "mavenizer"), task -> {
311-
task.setGroup("Build Setup");
312-
task.setDescription("Synchronizes the Mavenizer output for source set '" + sourceSetName + '.');
313-
}));
314-
315-
try {
316-
project.getTasks().named(sourceSet.getCompileJavaTaskName(), task -> task.dependsOn(syncMavenizer));
317-
} catch (UnknownTaskException ignored) { }
318-
319318
if (mergeSourceSets) {
320319
// This is documented in SourceSetOutput's javadoc comment
321320
var unifiedDir = sourceSetsDir.dir(sourceSet.getName());
322321
sourceSet.getOutput().setResourcesDir(unifiedDir);
323322
sourceSet.getJava().getDestinationDirectory().set(unifiedDir);
324323
}
325-
326-
project.getPluginManager().withPlugin("eclipse", appliedPlugin ->
327-
project.getExtensions().configure(EclipseModel.class, eclipse -> eclipse.synchronizationTasks(syncMavenizer))
328-
);
329324
});
330325

331326
project.getPluginManager().withPlugin("eclipse", eclipsePlugin -> {
@@ -375,8 +370,9 @@ public NamedDomainObjectContainer<? extends SlimeLauncherOptions> getRuns() {
375370
return this.runs;
376371
}
377372

373+
@SuppressWarnings({"UnstableApiUsage"})
378374
@Override
379-
public ExternalModuleDependency dependency(
375+
public Provider<ExternalModuleDependency> dependency(
380376
Object value,
381377
@DelegatesTo(ExternalModuleDependency.class)
382378
@ClosureParams(value = SimpleType.class, options = "net.minecraftforge.gradle.MinecraftDependency.ClosureOwner")
@@ -389,7 +385,64 @@ public ExternalModuleDependency dependency(
389385

390386
var minecraftDependency = this.getObjects().newInstance(MinecraftDependencyImpl.class, this.getMavenizerOutput());
391387
this.minecraftDependencies.add(minecraftDependency);
392-
return minecraftDependency.init(value, closure);
388+
var dep = minecraftDependency.init(value, closure);
389+
var mavenizer = this.getProviders().of(MavenizerValueSource.class, spec -> {
390+
spec.parameters(params -> {
391+
var tool = this.plugin.getTool(Tools.MAVENIZER);
392+
params.getClasspath().setFrom(tool.getClasspath());
393+
params.getJavaLauncher().set(tool.getJavaLauncher().map(JavaLauncher::getExecutablePath));
394+
params.getArguments().set(this.getProviders().provider(() -> {
395+
var toolCache = this.plugin.globalCaches()
396+
.dir(tool.getName().toLowerCase(Locale.ENGLISH))
397+
.map(this.problems.ensureFileLocation());
398+
var cache = toolCache.get().dir("caches").getAsFile().getAbsolutePath();
399+
400+
var ret = new ArrayList<String>();
401+
ret.addAll(List.of(
402+
"--maven",
403+
"--cache", cache,
404+
"--jdk-cache", cache,
405+
"--output", this.getMavenizerOutput().get().getAsFile().getAbsolutePath(),
406+
"--artifact", dep.getModule().toString(),
407+
"--version", Objects.requireNonNull(dep.getVersion()),
408+
"--global-auxiliary-variants"
409+
));
410+
411+
// If we are finding the access transformer from sourcesets, just find from any source set
412+
// We can't filter by configurations becase the config cache doesn't like that.
413+
// So if users fuck up, then we can output a warning, or they can manually set the AT file.
414+
// This is a 'best effort'
415+
var sourceSets = getProject().getExtensions().getByType(JavaPluginExtension.class).getSourceSets();
416+
minecraftDependency.finalizeAccessTransformers(sourceSets);
417+
418+
for (var at : minecraftDependency.getAccessTransformer()) {
419+
//System.out.println("Access Transformer: " + at);
420+
ret.add("--access-transformer");
421+
ret.add(at.getAbsolutePath());
422+
}
423+
424+
var mappings = minecraftDependency.getMappings();
425+
if ("parchment".equals(mappings.getChannel()))
426+
ret.addAll(List.of("--parchment", mappings.getVersion()));
427+
428+
for (var repo : this.getRepositories()) {
429+
if (MAVENIZER_REPO_NAME.equals(repo.getName()))
430+
continue;
431+
var url = repo.getUrl().toString();
432+
if (!url.endsWith("/"))
433+
url += '/';
434+
ret.add("--repository");
435+
ret.add(repo.getName() + ',' + url);
436+
}
437+
return ret;
438+
}));
439+
});
440+
});
441+
442+
return this.getProviders().provider(() -> {
443+
mavenizer.get();// Invoke mavenizer, it should be invoked already by gradle config cache, but Force it to be
444+
return dep;
445+
});
393446
}
394447

395448
private void checkRepos(List<? extends MavenArtifactRepository> repos) {

0 commit comments

Comments
 (0)