Merge remote-tracking branch 'FabricMC/dev/0.9' into dev/0.9

# Conflicts:
#	build.gradle
#	src/main/java/net/fabricmc/loom/LoomGradleExtension.java
#	src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java
#	src/main/java/net/fabricmc/loom/build/JarRemapper.java
#	src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java
#	src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java
#	src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java
#	src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java
#	src/main/java/net/fabricmc/loom/task/AbstractLoomTask.java
#	src/main/java/net/fabricmc/loom/task/AbstractRunTask.java
#	src/main/java/net/fabricmc/loom/task/LoomTasks.java
#	src/main/java/net/fabricmc/loom/util/Constants.java
#	src/main/java/net/fabricmc/loom/util/SourceRemapper.java
#	src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy
#	src/test/resources/projects/kotlin/build.gradle.kts
This commit is contained in:
shedaniel
2021-08-14 02:31:16 +08:00
62 changed files with 1050 additions and 425 deletions

View File

@@ -50,12 +50,11 @@ import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider;
import net.fabricmc.loom.extension.LoomFiles;
import net.fabricmc.loom.extension.LoomGradleExtensionImpl;
import net.fabricmc.loom.extension.MixinApExtension;
public interface LoomGradleExtension extends LoomGradleExtensionAPI {
static LoomGradleExtension get(Project project) {
return project.getExtensions().getByType(LoomGradleExtensionImpl.class);
return (LoomGradleExtension) project.getExtensions().getByName("loom");
}
LoomFiles getFiles();
@@ -100,8 +99,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
boolean isRootProject();
boolean isShareCaches();
default boolean ideSync() {
return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"));
}
@@ -111,7 +108,8 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
return String.format("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar", minecraftVersion);
}
MixinApExtension getMixinApExtension();
@Override
MixinApExtension getMixin();
// ===================
// Architectury Loom

View File

@@ -38,6 +38,7 @@ import com.google.gson.GsonBuilder;
import org.gradle.api.Project;
import org.gradle.api.plugins.PluginAware;
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.bootstrap.BootstrappedPlugin;
import net.fabricmc.loom.configuration.CompileConfiguration;
import net.fabricmc.loom.configuration.FabricApiExtension;
@@ -47,6 +48,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingsCache;
import net.fabricmc.loom.decompilers.DecompilerConfiguration;
import net.fabricmc.loom.extension.LoomGradleExtensionImpl;
import net.fabricmc.loom.extension.LoomFilesImpl;
import net.fabricmc.loom.extension.MinecraftGradleExtension;
import net.fabricmc.loom.task.LoomTasks;
public class LoomGradlePlugin implements BootstrappedPlugin {
@@ -86,9 +88,9 @@ public class LoomGradlePlugin implements BootstrappedPlugin {
project.apply(ImmutableMap.of("plugin", "eclipse"));
project.apply(ImmutableMap.of("plugin", "idea"));
// Setup extensions, loom shadows minecraft
project.getExtensions().create(LoomGradleExtension.class, "minecraft", LoomGradleExtensionImpl.class, project, new LoomFilesImpl(project));
project.getExtensions().add("loom", project.getExtensions().getByName("minecraft"));
// Setup extensions, minecraft wraps loom
var extension = project.getExtensions().create(LoomGradleExtensionAPI.class, "loom", LoomGradleExtensionImpl.class, project, new LoomFilesImpl(project));
project.getExtensions().create(LoomGradleExtensionAPI.class, "minecraft", MinecraftGradleExtension.class, extension);
project.getExtensions().create("fabricApi", FabricApiExtension.class, project);
CompileConfiguration.setupConfigurations(project);

View File

@@ -34,6 +34,9 @@ import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
import org.jetbrains.annotations.ApiStatus;
@@ -44,31 +47,77 @@ import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.launch.LaunchProviderSettings;
import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder;
import net.fabricmc.loom.util.DeprecationHelper;
import net.fabricmc.loom.util.ModPlatform;
/**
* This is the public api available exposed to build scripts.
*/
public interface LoomGradleExtensionAPI {
File getAccessWidener();
@ApiStatus.Internal
DeprecationHelper getDeprecationHelper();
void setAccessWidener(Object file);
RegularFileProperty getAccessWidenerPath();
void setShareCaches(boolean shareCaches);
boolean isShareCaches();
default void shareCaches() {
setShareCaches(true);
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default File getAccessWidener() {
getDeprecationHelper().replaceWithInLoom0_11("accessWidener", "accessWidenerPath");
return getAccessWidenerPath().getAsFile().getOrNull();
}
List<LoomDecompiler> getDecompilers();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default void setAccessWidener(File file) {
getDeprecationHelper().replaceWithInLoom0_11("accessWidener", "accessWidenerPath");
getAccessWidenerPath().set(file);
}
void addDecompiler(LoomDecompiler decompiler);
Property<Boolean> getShareRemapCaches();
List<JarProcessor> getJarProcessors();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default void setShareCaches(boolean shareCaches) {
getDeprecationHelper().replaceWithInLoom0_11("shareCaches", "shareRemapCaches");
getShareRemapCaches().set(shareCaches);
}
void addJarProcessor(JarProcessor processor);
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default boolean isShareCaches() {
getDeprecationHelper().replaceWithInLoom0_11("shareCaches", "shareRemapCaches");
return getShareRemapCaches().get();
}
default void shareCaches() {
getShareRemapCaches().set(true);
}
ListProperty<LoomDecompiler> getGameDecompilers();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default List<LoomDecompiler> getDecompilers() {
getDeprecationHelper().replaceWithInLoom0_11("decompilers", "gameDecompilers");
return getGameDecompilers().get();
}
default void addDecompiler(LoomDecompiler decompiler) {
getGameDecompilers().add(decompiler);
}
ListProperty<JarProcessor> getGameJarProcessors();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default List<JarProcessor> getJarProcessors() {
getDeprecationHelper().replaceWithInLoom0_11("jarProcessors", "gameJarProcessors");
return getGameJarProcessors().get();
}
default void addJarProcessor(JarProcessor processor) {
getGameJarProcessors().add(processor);
}
ConfigurableFileCollection getLog4jConfigs();
@@ -78,13 +127,35 @@ public interface LoomGradleExtensionAPI {
Dependency layered(Action<LayeredMappingSpecBuilder> action);
String getRefmapName();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default String getRefmapName() {
getDeprecationHelper().replaceWithInLoom0_11("refmapName", "mixin.defaultRefmapName");
return getMixin().getDefaultRefmapName().get();
}
void setRefmapName(String refmapName);
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default void setRefmapName(String refmapName) {
getDeprecationHelper().replaceWithInLoom0_11("refmapName", "mixin.defaultRefmapName");
getMixin().getDefaultRefmapName().set(refmapName);
}
boolean isRemapMod();
Property<Boolean> getRemapArchives();
void setRemapMod(boolean remapMod);
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default boolean isRemapMod() {
getDeprecationHelper().replaceWithInLoom0_11("remapMod", "remapArchives");
return getRemapArchives().get();
}
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default void setRemapMod(boolean remapMod) {
getDeprecationHelper().replaceWithInLoom0_11("remapMod", "remapArchives");
getRemapArchives().set(remapMod);
}
void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action);
@@ -93,9 +164,24 @@ public interface LoomGradleExtensionAPI {
@ApiStatus.Experimental
void mixin(Action<MixinApExtensionAPI> action);
void setCustomManifest(String customManifest);
@ApiStatus.Experimental
MixinApExtensionAPI getMixin();
String getCustomManifest();
Property<String> getCustomMinecraftManifest();
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default void setCustomManifest(String customManifest) {
getDeprecationHelper().replaceWithInLoom0_11("customManifest", "customMinecraftManifest");
getCustomMinecraftManifest().set(customManifest);
}
@Deprecated(forRemoval = true)
@ApiStatus.ScheduledForRemoval(inVersion = "0.11")
default String getCustomManifest() {
getDeprecationHelper().replaceWithInLoom0_11("customManifest", "customMinecraftManifest");
return getCustomMinecraftManifest().getOrNull();
}
// ===================
// Architectury Loom

View File

@@ -25,17 +25,19 @@
package net.fabricmc.loom.api;
import org.gradle.api.Action;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
public interface MixinApExtensionAPI {
Property<String> getDefaultRefmapName();
/**
* Apply Mixin AP to sourceSet.
* @param sourceSet the sourceSet that applies Mixin AP.
* @param refmapName the output ref-map name. By default this will
* be {@link net.fabricmc.loom.LoomGradleExtension#getRefmapName()}
* @param refmapName the output ref-map name. By default this will be {@link #getDefaultRefmapName()}
* @param action used for filter the mixin json files. By default this will be all files
* with name {@code *.mixins.json} that is inside the {@code resources} folder
* of {@code sourceSet}.

View File

@@ -43,6 +43,7 @@ import org.gradle.api.Action;
import org.gradle.api.Project;
import org.objectweb.asm.commons.Remapper;
import net.fabricmc.loom.util.CloseableList;
import net.fabricmc.loom.util.LoggerFilter;
import net.fabricmc.stitch.util.Pair;
@@ -101,31 +102,35 @@ public class JarRemapper {
}
}
List<OutputConsumerPath> outputConsumers = new ArrayList<>();
//noinspection MismatchedQueryAndUpdateOfCollection
try (CloseableList<OutputConsumerPath> outputConsumers = new CloseableList<>()) {
for (RemapData data : remapData) {
OutputConsumerPath outputConsumer;
project.getLogger().info(":remapper output -> " + data.output.getFileName().toString());
for (RemapData data : remapData) {
OutputConsumerPath outputConsumer;
project.getLogger().info(":remapper output -> " + data.output.getFileName().toString());
try {
Files.deleteIfExists(data.output);
outputConsumer = new OutputConsumerPath.Builder(data.output).build();
} catch (Exception e) {
throw new RuntimeException("Failed to create remapper output " + data.output.getFileName().toString(), e);
}
try {
Files.deleteIfExists(data.output);
outputConsumer = new OutputConsumerPath.Builder(data.output).build();
} catch (Exception e) {
throw new RuntimeException("Failed to create remapper output " + data.output.getFileName().toString(), e);
outputConsumers.add(outputConsumer);
outputConsumer.addNonClassFiles(data.input);
data.processAccessWidener(remapper.getRemapper());
remapper.apply(outputConsumer, data.tag);
}
outputConsumers.add(outputConsumer);
remapper.finish();
} catch (Exception e) {
for (RemapData data : remapData) {
// Cleanup bad outputs
Files.deleteIfExists(data.output);
}
outputConsumer.addNonClassFiles(data.input);
data.processAccessWidener(remapper.getRemapper());
remapper.apply(outputConsumer, data.tag);
}
remapper.finish();
for (OutputConsumerPath outputConsumer : outputConsumers) {
outputConsumer.close();
throw new IOException("Failed to remap %s files".formatted(remapData.size()), e);
}
remapData.forEach(RemapData::complete);

View File

@@ -25,13 +25,23 @@
package net.fabricmc.loom.build;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.gradle.api.Project;
import org.jetbrains.annotations.NotNull;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
@@ -43,29 +53,78 @@ import net.fabricmc.loom.extension.MixinApExtension;
public final class MixinRefmapHelper {
private MixinRefmapHelper() { }
private static final String FABRIC_MOD_JSON = "fabric.mod.json";
public static boolean addRefmapName(Project project, Path outputPath) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
File output = outputPath.toFile();
try {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixin();
File output = outputPath.toFile();
return mixin.getMixinSourceSetsStream().map(sourceSet -> {
MixinApExtension.MixinInformationContainer container = Objects.requireNonNull(
MixinApExtension.getMixinInformationContainer(sourceSet)
);
Stream<String> mixinJsonNames = container.getMixinJsonNames();
String refmapName = container.getRefmapName();
Collection<String> allMixinConfigs = getMixinConfigurationFiles(readFabricModJson(output));
return ZipUtil.transformEntries(output, mixinJsonNames.map(f -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") {
@Override
protected String transform(ZipEntry zipEntry, String input) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class);
return mixin.getMixinSourceSetsStream().map(sourceSet -> {
MixinApExtension.MixinInformationContainer container = Objects.requireNonNull(
MixinApExtension.getMixinInformationContainer(sourceSet)
);
if (!json.has("refmap")) {
json.addProperty("refmap", refmapName);
Stream<String> mixinConfigs = sourceSet.getResources()
.matching(container.mixinConfigPattern())
.getFiles()
.stream()
.map(File::getName)
.filter(allMixinConfigs::contains);
String refmapName = container.refmapNameProvider().get();
return ZipUtil.transformEntries(output, mixinConfigs.map(f -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") {
@Override
protected String transform(ZipEntry zipEntry, String input) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class);
if (!json.has("refmap")) {
json.addProperty("refmap", refmapName);
}
return LoomGradlePlugin.GSON.toJson(json);
}
})).toArray(ZipEntryTransformerEntry[]::new));
}).reduce(false, Boolean::logicalOr);
} catch (Exception e) {
project.getLogger().error(e.getMessage());
return false;
}
}
return LoomGradlePlugin.GSON.toJson(json);
}
})).toArray(ZipEntryTransformerEntry[]::new));
}).reduce(false, Boolean::logicalOr);
@NotNull
private static JsonObject readFabricModJson(File output) {
try (ZipFile zip = new ZipFile(output)) {
ZipEntry entry = zip.getEntry(FABRIC_MOD_JSON);
try (InputStreamReader reader = new InputStreamReader(zip.getInputStream(entry))) {
return LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class);
}
} catch (IOException e) {
throw new RuntimeException("Cannot read file fabric.mod.json in the output jar.", e);
}
}
@NotNull
private static Collection<String> getMixinConfigurationFiles(JsonObject fabricModJson) {
JsonArray mixins = fabricModJson.getAsJsonArray("mixins");
if (mixins == null) {
return Collections.emptySet();
}
return StreamSupport.stream(mixins.spliterator(), false)
.map(e -> {
if (e instanceof JsonPrimitive str) {
return str.getAsString();
} else if (e instanceof JsonObject obj) {
return obj.get("config").getAsString();
} else {
throw new RuntimeException("Incorrect fabric.mod.json format");
}
}).collect(Collectors.toSet());
}
}

View File

@@ -65,7 +65,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
}
protected static Collection<Configuration> getApConfigurations(Project project, Function<String, String> getApConfigNameFunc) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
MixinApExtension mixin = LoomGradleExtension.get(project).getMixin();
return mixin.getApConfigurationsStream(getApConfigNameFunc).collect(Collectors.toList());
}
@@ -80,7 +80,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
private void passMixinArguments(T task, SourceSet sourceSet) {
try {
LoomGradleExtension loom = LoomGradleExtension.get(project);
String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName();
String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get();
Map<String, String> args = new HashMap<>() {{
put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.getCanonicalPath());
put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getNextMixinMappings().getCanonicalPath());

View File

@@ -46,7 +46,7 @@ public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
}
private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
MixinApExtension mixin = LoomGradleExtension.get(project).getMixin();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue())));
}

View File

@@ -66,7 +66,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
}
private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
MixinApExtension mixin = LoomGradleExtension.get(project).getMixin();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue())));
}
@@ -82,7 +82,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
SourceSet sourceSet = entry.getKey();
task.doLast(t -> {
try {
String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName();
String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get();
Path src = Paths.get(getRefmapDestination(task, refmapName));
Path dest = Paths.get(task.getDestinationDir().toString(), refmapName);

View File

@@ -47,7 +47,7 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
}
private static Map<SourceSet, ScalaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
MixinApExtension mixin = LoomGradleExtension.get(project).getMixin();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.SCALA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((ScalaCompile) entry.getValue())));
}

View File

@@ -29,7 +29,6 @@ import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.jvm.tasks.Jar;
@@ -147,11 +146,6 @@ public final class CompileConfiguration {
Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
p.getTasks().withType(JavaCompile.class).configureEach(compile -> {
// Fork the java compiler to ensure that it does not keep any files open.
compile.getOptions().setFork(true);
});
p.afterEvaluate(project -> {
LoomGradleExtension extension = LoomGradleExtension.get(project);
@@ -186,9 +180,10 @@ public final class CompileConfiguration {
SetupIntelijRunConfigs.setup(project);
GenVsCodeProjectTask.generate(project);
extension.getRemapArchives().finalizeValue();
// Enables the default mod remapper
if (extension.isRemapMod()) {
if (extension.getRemapArchives().get()) {
RemapConfiguration.setupDefaultRemap(project);
} else {
Jar jarTask = (Jar) project.getTasks().getByName("jar");
@@ -201,7 +196,7 @@ public final class CompileConfiguration {
System.setProperty("log4j.skipJansi", "true");
project.getLogger().info("Configuring compiler arguments for Java");
MixinApExtension mixinApExtension = LoomGradleExtension.get(project).getMixinApExtension();
MixinApExtension mixinApExtension = LoomGradleExtension.get(project).getMixin();
mixinApExtension.init();
new JavaApInvoker(project).configureMixin();

View File

@@ -25,6 +25,7 @@
package net.fabricmc.loom.configuration;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -170,7 +171,15 @@ public class LoomDependencyManager {
ModCompileRemapper.remapDependencies(project, mappingsKey, extension, sourceRemapper);
sourceRemapper.remapAll();
long start = System.currentTimeMillis();
try {
sourceRemapper.remapAll();
} catch (IOException exception) {
throw new RuntimeException("Failed to remap mod sources", exception);
}
project.getLogger().info("Source remapping took: %dms".formatted(System.currentTimeMillis() - start));
for (Runnable runnable : afterTasks) {
runnable.run();

View File

@@ -105,7 +105,7 @@ public class RemapConfiguration {
// TODO what is this for?
Task parentTask = project.getTasks().getByName("build");
if (extension.isShareCaches()) {
if (extension.getShareRemapCaches().get()) {
Project rootProject = project.getRootProject();
if (extension.isRootProject()) {
@@ -116,7 +116,6 @@ public class RemapConfiguration {
rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> {
task.sourceRemapper = sourceRemapper;
task.doLast(t -> sourceRemapper.remapAll());
});
parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName);
@@ -165,7 +164,7 @@ public class RemapConfiguration {
});
}
if (extension.isShareCaches()) {
if (extension.getShareRemapCaches().get()) {
remapSourcesJarTask.setSourceRemapper(remapper);
}

View File

@@ -70,17 +70,23 @@ public class AccessWidenerJarProcessor implements JarProcessor {
this.project = project;
}
@Override
public String getId() {
return "loom:access_widener";
}
@Override
public void setup() {
LoomGradleExtension loomGradleExtension = LoomGradleExtension.get(project);
File awPath = loomGradleExtension.getAccessWidenerPath().get().getAsFile();
if (!loomGradleExtension.getAccessWidener().exists()) {
throw new RuntimeException("Could not find access widener file @ " + loomGradleExtension.getAccessWidener().getAbsolutePath());
if (!awPath.exists()) {
throw new RuntimeException("Could not find access widener file @ " + awPath.getAbsolutePath());
}
inputHash = Checksum.sha256(loomGradleExtension.getAccessWidener());
inputHash = Checksum.sha256(awPath);
try (BufferedReader reader = new BufferedReader(new FileReader(loomGradleExtension.getAccessWidener()))) {
try (BufferedReader reader = new BufferedReader(new FileReader(awPath))) {
accessWidenerReader.read(reader);
} catch (IOException e) {
throw new RuntimeException("Failed to read project access widener file");

View File

@@ -52,6 +52,7 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.InstallerData;
import net.fabricmc.loom.util.OperatingSystem;
public class RunConfig {
@@ -269,7 +270,13 @@ public class RunConfig {
}
private static String getMainClass(String side, LoomGradleExtension extension, String defaultMainClass) {
JsonObject installerJson = extension.getInstallerData() == null ? null : extension.getInstallerData().installerJson();
InstallerData installerData = extension.getInstallerData() == null ? null : extension.getInstallerData();
if (installerData == null) {
return defaultMainClass;
}
JsonObject installerJson = installerData.installerJson();
if (installerJson != null && installerJson.has("mainClass")) {
JsonElement mainClassJson = installerJson.get("mainClass");

View File

@@ -27,6 +27,18 @@ package net.fabricmc.loom.configuration.processors;
import java.io.File;
public interface JarProcessor {
/**
* Returns a unique ID for this jar processor, containing all configuration details.
*
* <p>If the jar processor implementation class supports creating multiple jar processors with different effects,
* the needed configuration should also be included in this ID. Example: {@code path.to.MyJarProcessor#someOption}.
*
* @return the ID of this jar processor
*/
default String getId() {
return getClass().getName();
}
void setup();
/**

View File

@@ -25,9 +25,27 @@
package net.fabricmc.loom.configuration.processors;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import com.google.common.hash.Hashing;
import com.google.common.io.CharSource;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StreamZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
public class JarProcessorManager {
private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF";
private static final String JAR_PROCESSOR_HASH_ATTRIBUTE = "Loom-Jar-Processor-Hash";
private final List<JarProcessor> jarProcessors;
public JarProcessorManager(List<JarProcessor> jarProcessors) {
@@ -47,13 +65,56 @@ public class JarProcessorManager {
return true;
}
String jarProcessorHash = getJarProcessorHash();
try (JarFile jar = new JarFile(file)) {
Attributes attributes = jar.getManifest().getMainAttributes();
if (!jarProcessorHash.equals(attributes.getValue(JAR_PROCESSOR_HASH_ATTRIBUTE))) {
return true;
}
} catch (IOException e) {
throw new UncheckedIOException("Could not check jar manifest of " + file, e);
}
return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.isInvalid(file));
}
private String getJarProcessorHash() {
String jarProcessorIds = jarProcessors.stream()
.map(JarProcessor::getId)
.sorted()
.collect(Collectors.joining(";"));
try {
return CharSource.wrap(jarProcessorIds)
.asByteSource(StandardCharsets.UTF_8)
.hash(Hashing.sha256())
.toString();
} catch (IOException e) {
throw new UncheckedIOException("Could not hash jar processor IDs", e);
}
}
public void process(File file) {
for (JarProcessor jarProcessor : jarProcessors) {
jarProcessor.process(file);
}
boolean manifestTransformed = ZipUtil.transformEntries(file, new ZipEntryTransformerEntry[] {
new ZipEntryTransformerEntry(MANIFEST_PATH, new StreamZipEntryTransformer() {
@Override
protected void transform(ZipEntry zipEntry, InputStream in, OutputStream out) throws IOException {
Manifest manifest = new Manifest(in);
manifest.getMainAttributes().putValue(JAR_PROCESSOR_HASH_ATTRIBUTE, getJarProcessorHash());
manifest.write(out);
}
})
});
if (!manifestTransformed) {
throw new RuntimeException("Could not add data to jar manifest in " + file);
}
}
public <T extends JarProcessor> T getByType(Class<T> tClass) {

View File

@@ -135,7 +135,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
}
private void downloadMcJson(boolean offline) throws IOException {
if (getExtension().isShareCaches() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) {
if (getExtension().getShareRemapCaches().get() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) {
return;
}
@@ -159,10 +159,10 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
Optional<ManifestVersion.Versions> optionalVersion = Optional.empty();
if (getExtension().getCustomManifest() != null) {
if (getExtension().getCustomMinecraftManifest().isPresent()) {
ManifestVersion.Versions customVersion = new ManifestVersion.Versions();
customVersion.id = minecraftVersion;
customVersion.url = getExtension().getCustomManifest();
customVersion.url = getExtension().getCustomMinecraftManifest().get();
optionalVersion = Optional.of(customVersion);
getProject().getLogger().lifecycle("Using custom minecraft manifest");
}
@@ -226,7 +226,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
}
private boolean hasRecentValidManifest() throws IOException {
if (getExtension().getCustomManifest() != null) {
if (getExtension().getCustomMinecraftManifest().isPresent()) {
return false;
}
@@ -247,7 +247,7 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra
}
private void downloadJars(Logger logger) throws IOException {
if (getExtension().isShareCaches() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) {
if (getExtension().getShareRemapCaches().get() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) {
return;
}

View File

@@ -25,8 +25,9 @@
package net.fabricmc.loom.configuration.providers.mappings;
import java.io.File;
import java.util.function.Supplier;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.logging.Logger;
@@ -37,12 +38,11 @@ import net.fabricmc.loom.configuration.providers.MinecraftProvider;
public class GradleMappingContext implements MappingContext {
private final Project project;
private final LoomGradleExtension extension;
private final Supplier<String> workingDirName;
private File workingDir;
public GradleMappingContext(Project project, Supplier<String> workingDirName) {
public GradleMappingContext(Project project) {
this.project = project;
this.extension = LoomGradleExtension.get(project);
this.workingDirName = workingDirName;
}
@Override
@@ -61,11 +61,29 @@ public class GradleMappingContext implements MappingContext {
return extension.getMinecraftProvider();
}
@Override
public File workingDirectory() {
if (workingDir == null) {
workingDir = new File(mappingsProvider().getMappingsDir().toFile(), "layered/" + minecraftProvider().minecraftVersion());
if (workingDir.exists()) {
try {
FileUtils.deleteDirectory(workingDir);
} catch (IOException e) {
getLogger().warn("Failed to cleanup layered mappings working directory: {}", e.getMessage());
}
}
}
return workingDir;
}
@Override
public File workingDirectory(String name) {
File tempDir = new File(mappingsProvider().getMappingsDir().toFile(), workingDirName.get());
tempDir.mkdirs();
return new File(tempDir, name);
File file = new File(workingDirectory(), name);
file.mkdirs();
return file;
}
@Override

View File

@@ -27,8 +27,8 @@ package net.fabricmc.loom.configuration.providers.mappings;
import java.util.List;
public record LayeredMappingSpec(List<MappingsSpec<?>> layers) {
public String getVersion() {
public String getVersion(MappingContext context) {
// TODO something better?
return "layered+hash.%d".formatted(Math.abs(hashCode()));
return "layered+hash.%d.minecraft.%s".formatted(Math.abs(hashCode()), context.minecraftVersion());
}
}

View File

@@ -54,17 +54,16 @@ public class LayeredMappingsDependency implements SelfResolvingDependency {
private final MappingContext mappingContext;
private final LayeredMappingSpec layeredMappingSpec;
private final String version;
private String version = null;
public LayeredMappingsDependency(MappingContext mappingContext, LayeredMappingSpec layeredMappingSpec, String version) {
public LayeredMappingsDependency(MappingContext mappingContext, LayeredMappingSpec layeredMappingSpec) {
this.mappingContext = mappingContext;
this.layeredMappingSpec = layeredMappingSpec;
this.version = version;
}
@Override
public Set<File> resolve() {
Path mappingsDir = mappingContext.mappingsProvider().getMappingsDir();
Path mappingsDir = mappingContext.workingDirectory().toPath();
Path mappingsFile = mappingsDir.resolve(String.format("%s.%s-%s.tiny", GROUP, MODULE, getVersion()));
if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) {
@@ -115,6 +114,10 @@ public class LayeredMappingsDependency implements SelfResolvingDependency {
@Override
public String getVersion() {
if (version == null) {
version = layeredMappingSpec.getVersion(mappingContext);
}
return version;
}
@@ -129,7 +132,7 @@ public class LayeredMappingsDependency implements SelfResolvingDependency {
@Override
public Dependency copy() {
return new LayeredMappingsDependency(mappingContext, layeredMappingSpec, version);
return new LayeredMappingsDependency(mappingContext, layeredMappingSpec);
}
@Override

View File

@@ -41,6 +41,8 @@ public interface MappingContext {
return minecraftProvider().minecraftVersion();
}
File workingDirectory();
/**
* Creates a temporary working dir to be used to store working files.
*/

View File

@@ -277,11 +277,13 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings
LoomGradleExtension extension = getExtension();
if (extension.getAccessWidener() != null) {
extension.addJarProcessor(new AccessWidenerJarProcessor(getProject()));
if (extension.getAccessWidenerPath().isPresent()) {
extension.getGameJarProcessors().add(new AccessWidenerJarProcessor(getProject()));
}
JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors());
extension.getAccessWidenerPath().finalizeValue();
extension.getGameJarProcessors().finalizeValue();
JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get());
extension.setJarProcessorManager(processorManager);
processorManager.setupProcessors();

View File

@@ -44,15 +44,16 @@ import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
import net.fabricmc.mappingio.format.ProGuardReader;
public record MojangMappingLayer(MinecraftVersionMeta.Download clientDownload,
public record MojangMappingLayer(String minecraftVersion,
MinecraftVersionMeta.Download clientDownload,
MinecraftVersionMeta.Download serverDownload,
File workingDir,
Logger logger,
MojangMappingsSpec.SilenceLicenseOption silenceLicense) implements MappingLayer {
@Override
public void visit(MappingVisitor mappingVisitor) throws IOException {
var clientMappings = new File(workingDir(), "client.txt");
var serverMappings = new File(workingDir(), "server.txt");
var clientMappings = new File(workingDir(), "%s.client.txt".formatted(minecraftVersion));
var serverMappings = new File(workingDir(), "%s.server.txt".formatted(minecraftVersion));
download(clientMappings, serverMappings);

View File

@@ -78,6 +78,7 @@ public record MojangMappingsSpec(SilenceLicenseOption silenceLicense) implements
}
return new MojangMappingLayer(
context.minecraftVersion(),
versionInfo.download(MANIFEST_CLIENT_MAPPINGS),
versionInfo.download(MANIFEST_SERVER_MAPPINGS),
context.workingDirectory("mojang"),

View File

@@ -36,7 +36,7 @@ public final class DecompilerConfiguration {
public static void setup(Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
extension.addDecompiler(new FabricFernFlowerDecompiler(project));
extension.addDecompiler(new FabricCFRDecompiler(project));
extension.getGameDecompilers().add(new FabricFernFlowerDecompiler(project));
extension.getGameDecompilers().add(new FabricCFRDecompiler(project));
}
}

View File

@@ -24,15 +24,6 @@
package net.fabricmc.loom.extension;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -42,7 +33,10 @@ import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.plugins.BasePluginConvention;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.provider.Property;
import org.gradle.api.tasks.SourceSet;
@@ -58,6 +52,7 @@ import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency;
import net.fabricmc.loom.util.DeprecationHelper;
import net.fabricmc.loom.util.ModPlatform;
import net.fabricmc.loom.util.function.LazyBool;
@@ -69,15 +64,14 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
private static final String PLATFORM_PROPERTY = "loom.platform";
private static final String INCLUDE_PROPERTY = "loom.forge.include";
protected final List<LoomDecompiler> decompilers = new ArrayList<>();
protected final List<JarProcessor> jarProcessors = new ArrayList<>();
protected final DeprecationHelper deprecationHelper;
protected final ListProperty<LoomDecompiler> decompilers;
protected final ListProperty<JarProcessor> jarProcessors;
protected final ConfigurableFileCollection log4jConfigs;
protected File accessWidener = null;
protected boolean shareCaches = false;
protected String refmapName = null;
protected boolean remapMod = true;
protected String customManifest;
protected final RegularFileProperty accessWidener;
protected final Property<Boolean> shareCaches;
protected final Property<Boolean> remapArchives;
protected final Property<String> customManifest;
private NamedDomainObjectContainer<RunConfigSettings> runConfigs;
@@ -101,7 +95,19 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) {
this.runConfigs = project.container(RunConfigSettings.class,
baseName -> new RunConfigSettings(project, baseName));
this.decompilers = project.getObjects().listProperty(LoomDecompiler.class)
.empty();
this.jarProcessors = project.getObjects().listProperty(JarProcessor.class)
.empty();
this.log4jConfigs = project.files(directories.getDefaultLog4jConfigFile());
this.accessWidener = project.getObjects().fileProperty();
this.shareCaches = project.getObjects().property(Boolean.class)
.convention(false);
this.remapArchives = project.getObjects().property(Boolean.class)
.convention(true);
this.customManifest = project.getObjects().property(String.class);
this.deprecationHelper = new DeprecationHelper.ProjectBased(project);
this.platform = project.getObjects().property(ModPlatform.class).convention(project.provider(Suppliers.memoize(() -> {
Object platformProperty = project.findProperty(PLATFORM_PROPERTY);
@@ -124,60 +130,45 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
}
@Override
public File getAccessWidener() {
public DeprecationHelper getDeprecationHelper() {
return deprecationHelper;
}
@Override
public RegularFileProperty getAccessWidenerPath() {
return accessWidener;
}
@Override
public void setAccessWidener(Object file) {
Objects.requireNonNull(file, "Access widener file cannot be null");
this.accessWidener = getProject().file(file);
}
@Override
public void setShareCaches(boolean shareCaches) {
this.shareCaches = shareCaches;
}
@Override
public boolean isShareCaches() {
public Property<Boolean> getShareRemapCaches() {
return shareCaches;
}
@Override
public List<LoomDecompiler> getDecompilers() {
public ListProperty<LoomDecompiler> getGameDecompilers() {
return decompilers;
}
@Override
public void addDecompiler(LoomDecompiler decompiler) {
Objects.requireNonNull(decompiler, "Decompiler cannot be null");
decompilers.add(decompiler);
}
@Override
public List<JarProcessor> getJarProcessors() {
public ListProperty<JarProcessor> getGameJarProcessors() {
return jarProcessors;
}
@Override
public void addJarProcessor(JarProcessor processor) {
Objects.requireNonNull(processor, "Jar processor cannot be null");
jarProcessors.add(processor);
}
@Override
public Dependency layered(Action<LayeredMappingSpecBuilder> action) {
LayeredMappingSpecBuilder builder = new LayeredMappingSpecBuilder(this);
action.execute(builder);
LayeredMappingSpec builtSpec = builder.build();
return new LayeredMappingsDependency(new GradleMappingContext(getProject(), () -> {
return "layers/" + getMinecraftVersion() + "_" + builtSpec.getVersion().replace("+", "_").replace(".", "_");
}), builtSpec, builtSpec.getVersion());
return new LayeredMappingsDependency(new GradleMappingContext(getProject()), builtSpec);
}
protected abstract String getMinecraftVersion();
@Override
public Property<Boolean> getRemapArchives() {
return remapArchives;
}
@Override
public String getRefmapName() {
if (refmapName == null || refmapName.isEmpty()) {
@@ -196,16 +187,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
return refmapName;
}
@Override
public void setRefmapName(String refmapName) {
this.refmapName = refmapName;
}
@Override
public void setRemapMod(boolean remapMod) {
this.remapMod = remapMod;
}
@Override
public void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action) {
action.execute(runConfigs);
@@ -221,24 +202,13 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
return log4jConfigs;
}
@Override
public boolean isRemapMod() {
return remapMod;
}
@Override
public void mixin(Action<MixinApExtensionAPI> action) {
action.execute(getMixinApExtension());
action.execute(getMixin());
}
@Override
public void setCustomManifest(String customManifest) {
Objects.requireNonNull(customManifest, "Custom manifest cannot be null");
this.customManifest = customManifest;
}
@Override
public String getCustomManifest() {
public Property<String> getCustomMinecraftManifest() {
return customManifest;
}
@@ -246,8 +216,6 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
protected abstract LoomFiles getFiles();
protected abstract MixinApExtension getMixinApExtension();
@Override
public void silentMojangMappingsLicense() {
this.silentMojangMappingsLicense = true;
@@ -389,6 +357,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
throw new RuntimeException();
}
@Override
public DeprecationHelper getDeprecationHelper() {
throw new RuntimeException("Yeah... something is really wrong");
}
@Override
protected Project getProject() {
throw new RuntimeException("Yeah... something is really wrong");
@@ -400,7 +373,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
}
@Override
protected MixinApExtension getMixinApExtension() {
public MixinApExtension getMixin() {
throw new RuntimeException("Yeah... something is really wrong");
}

View File

@@ -63,7 +63,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
public LoomGradleExtensionImpl(Project project, LoomFiles files) {
super(project, files);
this.project = project;
this.mixinApExtension = new MixinApExtensionImpl(project);
// Initiate with newInstance to allow gradle to decorate our extension
this.mixinApExtension = project.getObjects().newInstance(MixinApExtensionImpl.class, project);
this.loomFiles = files;
this.unmappedMods = project.files();
}
@@ -166,7 +167,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
}
@Override
public MixinApExtension getMixinApExtension() {
public MixinApExtension getMixin() {
return this.mixinApExtension;
}

View File

@@ -0,0 +1,134 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.extension;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.api.MixinApExtensionAPI;
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder;
import net.fabricmc.loom.util.DeprecationHelper;
public class MinecraftGradleExtension implements LoomGradleExtensionAPI {
private final LoomGradleExtensionAPI parent;
private boolean deprecationReported = false;
public MinecraftGradleExtension(LoomGradleExtensionAPI parent) {
this.parent = parent;
}
private void reportDeprecation() {
if (!deprecationReported) {
getDeprecationHelper().replaceWithInLoom0_11("minecraft", "loom");
deprecationReported = true;
}
}
@Override
public DeprecationHelper getDeprecationHelper() {
return parent.getDeprecationHelper();
}
@Override
public RegularFileProperty getAccessWidenerPath() {
reportDeprecation();
return parent.getAccessWidenerPath();
}
@Override
public Property<Boolean> getShareRemapCaches() {
reportDeprecation();
return parent.getShareRemapCaches();
}
@Override
public ListProperty<LoomDecompiler> getGameDecompilers() {
reportDeprecation();
return parent.getGameDecompilers();
}
@Override
public ListProperty<JarProcessor> getGameJarProcessors() {
reportDeprecation();
return parent.getGameJarProcessors();
}
@Override
public ConfigurableFileCollection getLog4jConfigs() {
reportDeprecation();
return parent.getLog4jConfigs();
}
@Override
public Dependency layered(Action<LayeredMappingSpecBuilder> action) {
reportDeprecation();
return parent.layered(action);
}
@Override
public Property<Boolean> getRemapArchives() {
reportDeprecation();
return parent.getRemapArchives();
}
@Override
public void runs(Action<NamedDomainObjectContainer<RunConfigSettings>> action) {
reportDeprecation();
parent.runs(action);
}
@Override
public NamedDomainObjectContainer<RunConfigSettings> getRunConfigs() {
reportDeprecation();
return parent.getRunConfigs();
}
@Override
public void mixin(Action<MixinApExtensionAPI> action) {
reportDeprecation();
parent.mixin(action);
}
@Override
public MixinApExtensionAPI getMixin() {
reportDeprecation();
return parent.getMixin();
}
@Override
public Property<String> getCustomMinecraftManifest() {
reportDeprecation();
return parent.getCustomMinecraftManifest();
}
}

View File

@@ -26,7 +26,6 @@ package net.fabricmc.loom.extension;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -34,6 +33,7 @@ import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
@@ -55,42 +55,7 @@ public interface MixinApExtension extends MixinApExtensionAPI {
* for configuring the mixin annotation processor. It's stored
* in [SourceSet].ext.mixin.
*/
final class MixinInformationContainer {
private final SourceSet sourceSet;
private final String refmapName;
private Stream<String> mixinJsonNames;
final PatternSet mixinJsonPattern;
public MixinInformationContainer(@NotNull SourceSet sourceSet,
@NotNull String refmapName,
@NotNull PatternSet mixinJsonPattern) {
this.sourceSet = sourceSet;
this.refmapName = refmapName;
this.mixinJsonPattern = mixinJsonPattern;
}
void setMixinJsonNames(@NotNull Stream<String> mixinJsonNames) {
if (this.mixinJsonNames == null) {
this.mixinJsonNames = mixinJsonNames;
}
}
@NotNull
public Stream<String> getMixinJsonNames() {
return Objects.requireNonNull(mixinJsonNames);
}
@NotNull
public SourceSet getSourceSet() {
return sourceSet;
}
@NotNull
public String getRefmapName() {
return refmapName;
}
}
record MixinInformationContainer(SourceSet sourceSet, Provider<String> refmapNameProvider, PatternSet mixinConfigPattern) { }
@Nullable
static MixinInformationContainer getMixinInformationContainer(SourceSet sourceSet) {

View File

@@ -28,15 +28,21 @@ import org.gradle.api.Action;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.api.MixinApExtensionAPI;
public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
protected abstract Project getProject();
protected abstract PatternSet add0(SourceSet sourceSet, String refmapName);
protected final PatternSet add0(SourceSet sourceSet, String refmapName) {
return add0(sourceSet, getProject().provider(() -> refmapName));
}
protected abstract PatternSet add0(SourceSet sourceSet, Provider<String> refmapName);
@Override
public void add(SourceSet sourceSet, String refmapName, Action<PatternSet> action) {
@@ -51,14 +57,14 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
@Override
public void add(String sourceSetName, String refmapName, Action<PatternSet> action) {
// try to find sourceSet with name sourceSetName in this project
SourceSet sourceSet = getProject().getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets().findByName(sourceSetName);
add(sourceSetName, getProject().provider(() -> refmapName), action);
}
if (sourceSet == null) {
throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found");
}
public void add(String sourceSetName, Provider<String> refmapName, Action<PatternSet> action) {
add(resolveSourceSet(sourceSetName), refmapName, action);
}
public void add(SourceSet sourceSet, Provider<String> refmapName, Action<PatternSet> action) {
PatternSet pattern = add0(sourceSet, refmapName);
action.execute(pattern);
}
@@ -70,8 +76,7 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
@Override
public void add(SourceSet sourceSet, Action<PatternSet> action) {
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
add(sourceSet, extension.getRefmapName(), action);
add(sourceSet, getDefaultRefmapName(), action);
}
@Override
@@ -81,8 +86,7 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
@Override
public void add(String sourceSetName, Action<PatternSet> action) {
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
add(sourceSetName, extension.getRefmapName(), action);
add(sourceSetName, getDefaultRefmapName(), action);
}
@Override
@@ -90,6 +94,18 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
add(sourceSetName, x -> { });
}
private SourceSet resolveSourceSet(String sourceSetName) {
// try to find sourceSet with name sourceSetName in this project
SourceSet sourceSet = getProject().getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets().findByName(sourceSetName);
if (sourceSet == null) {
throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found");
}
return sourceSet;
}
// This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods
private final class EnsureCompile extends MixinApExtensionApiImpl {
private EnsureCompile() {
@@ -103,7 +119,12 @@ public abstract class MixinApExtensionApiImpl implements MixinApExtensionAPI {
}
@Override
protected PatternSet add0(SourceSet sourceSet, String refmapName) {
public Property<String> getDefaultRefmapName() {
throw new RuntimeException("Yeah... something is really wrong");
}
@Override
protected PatternSet add0(SourceSet sourceSet, Provider<String> refmapName) {
throw new RuntimeException("Yeah... something is really wrong");
}
}

View File

@@ -24,7 +24,6 @@
package net.fabricmc.loom.extension;
import java.io.File;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
@@ -33,11 +32,16 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.BasePluginConvention;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
@@ -46,10 +50,14 @@ import org.jetbrains.annotations.NotNull;
public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements MixinApExtension {
private boolean isDefault;
private final Project project;
private final Property<String> defaultRefmapName;
@Inject
public MixinApExtensionImpl(Project project) {
this.isDefault = true;
this.project = project;
this.defaultRefmapName = project.getObjects().property(String.class)
.convention(project.provider(this::getDefaultMixinRefmapName));
}
@Override
@@ -58,8 +66,19 @@ public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements Mix
}
@Override
protected PatternSet add0(SourceSet sourceSet, String refmapName) {
PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.mixins.json"));
public Property<String> getDefaultRefmapName() {
return defaultRefmapName;
}
private String getDefaultMixinRefmapName() {
String defaultRefmapName = getProject().getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json";
getProject().getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName);
return defaultRefmapName;
}
@Override
protected PatternSet add0(SourceSet sourceSet, Provider<String> refmapName) {
PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.json"));
MixinApExtension.setMixinInformationContainer(sourceSet, new MixinApExtension.MixinInformationContainer(sourceSet, refmapName, pattern));
isDefault = false;
@@ -71,19 +90,7 @@ public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements Mix
@NotNull
public Stream<SourceSet> getMixinSourceSetsStream() {
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream()
.filter(sourceSet -> {
MixinApExtension.MixinInformationContainer container = MixinApExtension.getMixinInformationContainer(sourceSet);
if (container != null) {
PatternSet pattern = container.mixinJsonPattern;
Stream<String> mixinJsonNames = sourceSet.getResources()
.matching(pattern).getFiles().stream().map(File::getName);
container.setMixinJsonNames(mixinJsonNames);
return true;
}
return false;
});
.filter(sourceSet -> MixinApExtension.getMixinInformationContainer(sourceSet) != null);
}
@Override

View File

@@ -42,7 +42,7 @@ public abstract class AbstractRunTask extends JavaExec {
public AbstractRunTask(Function<Project, RunConfig> configProvider) {
super();
setGroup(Constants.TASK_CATEGORY);
setGroup(Constants.TaskGroup.FABRIC);
this.config = configProvider.apply(getProject());
setClasspath(config.sourceSet.getRuntimeClasspath());

View File

@@ -51,7 +51,7 @@ public final class LoomTasks {
tasks.register("remapJar", RemapJarTask.class, t -> {
t.setDescription("Remaps the built project jar to intermediary mappings.");
t.setGroup(Constants.TASK_CATEGORY);
t.setGroup(Constants.TaskGroup.FABRIC);
});
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric."));
@@ -67,24 +67,24 @@ public final class LoomTasks {
tasks.register("genIdeaWorkspace", GenIdeaProjectTask.class, t -> {
t.setDescription("Generates an IntelliJ IDEA workspace from this project.");
t.dependsOn("idea", "downloadAssets");
t.setGroup("ide");
t.setGroup(Constants.TaskGroup.IDE);
});
tasks.register("genEclipseRuns", GenEclipseRunsTask.class, t -> {
t.setDescription("Generates Eclipse run configurations for this project.");
t.dependsOn("downloadAssets");
t.setGroup("ide");
t.setGroup(Constants.TaskGroup.IDE);
});
tasks.register("cleanEclipseRuns", CleanEclipseRunsTask.class, t -> {
t.setDescription("Removes Eclipse run configurations for this project.");
t.setGroup("ide");
t.setGroup(Constants.TaskGroup.IDE);
});
tasks.register("vscode", GenVsCodeProjectTask.class, t -> {
t.setDescription("Generates VSCode launch configurations.");
t.dependsOn("downloadAssets");
t.setGroup("ide");
t.setGroup(Constants.TaskGroup.IDE);
});
}
@@ -148,7 +148,9 @@ public final class LoomTasks {
inputJar = outputJar;
}
for (LoomDecompiler decompiler : extension.getDecompilers()) {
extension.getGameDecompilers().finalizeValue();
for (LoomDecompiler decompiler : extension.getGameDecompilers().get()) {
String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name();
// decompiler will be passed to the constructor of GenerateSourcesTask
GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get();

View File

@@ -24,7 +24,10 @@
package net.fabricmc.loom.task;
import java.io.IOException;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.util.SourceRemapper;
@@ -35,4 +38,13 @@ public class RemapAllSourcesTask extends AbstractLoomTask {
public SourceRemapper getSourceRemapper() {
return sourceRemapper;
}
@TaskAction
public void remap() {
try {
sourceRemapper.remapAll();
} catch (IOException exception) {
throw new RuntimeException("Failed to remap mod", exception);
}
}
}

View File

@@ -248,7 +248,7 @@ public class RemapJarTask extends Jar {
jarRemapper.scheduleRemap(input, output)
.supplyAccessWidener((remapData, remapper) -> {
if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidener() != null) {
if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidenerPath().isPresent()) {
AccessWidenerJarProcessor accessWidenerJarProcessor = extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class);
byte[] data;

View File

@@ -0,0 +1,52 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
public class CloseableList<T extends Closeable> extends ArrayList<T> implements Closeable {
@Override
public void close() throws IOException {
IOException exception = null;
for (T t : this) {
try {
t.close();
} catch (IOException e) {
if (exception == null) {
exception = new IOException("Failed to close list");
}
exception.addSuppressed(e);
}
}
if (exception != null) {
throw exception;
}
}
}

View File

@@ -40,7 +40,6 @@ public class Constants {
public static final String EXPERIMENTAL_VERSIONS = "https://maven.fabricmc.net/net/minecraft/experimental_versions.json";
public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32";
public static final String TASK_CATEGORY = "loom";
public static final int ASM_VERSION = Opcodes.ASM9;
@@ -136,6 +135,14 @@ public class Constants {
}
}
public static final class TaskGroup {
public static final String FABRIC = "loom";
public static final String IDE = "ide";
private TaskGroup() {
}
}
public static final class ForgeUserDev {
public static final String LAUNCH_TESTING = "net.minecraftforge.userdev.LaunchTesting";

View File

@@ -0,0 +1,86 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 FabricMC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.fabricmc.loom.util;
import java.util.concurrent.atomic.AtomicBoolean;
import org.gradle.api.Project;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.configuration.WarningMode;
public interface DeprecationHelper {
default void replaceWithInLoom0_10(String currentName, String newName) {
toBeRemovedIn(currentName, newName, "Loom 0.10");
}
default void replaceWithInLoom0_11(String currentName, String newName) {
toBeRemovedIn(currentName, newName, "Loom 0.11");
}
default void replaceWithInLoom0_12(String currentName, String newName) {
toBeRemovedIn(currentName, newName, "Loom 0.12");
}
default void toBeRemovedIn(String currentName, String newName, String removalVersion) {
warn("The '%s' property has been deprecated, and has been replaced with '%s'. This is scheduled to be removed in %s.".formatted(currentName, newName, removalVersion));
}
Project getProject();
void warn(String warning);
class ProjectBased implements DeprecationHelper {
private final Project project;
private final AtomicBoolean usingDeprecatedApi = new AtomicBoolean(false);
public ProjectBased(Project project) {
this.project = project;
project.getGradle().buildFinished(buildResult -> {
if (usingDeprecatedApi.get()) {
project.getLogger().lifecycle("Deprecated Loom APIs were used in this build, making it incompatible with future versions of Loom. "
+ "Use Gradle warning modes to control the verbosity of the warnings.");
}
});
}
@Override
public Project getProject() {
return project;
}
@Override
public void warn(String warning) {
WarningMode warningMode = getProject().getGradle().getStartParameter().getWarningMode();
getProject().getLogger().log(warningMode == WarningMode.None ? LogLevel.INFO : LogLevel.WARN, warning);
if (warningMode == WarningMode.Fail) {
throw new UnsupportedOperationException(warning);
}
usingDeprecatedApi.set(true);
}
}
}

View File

@@ -29,15 +29,23 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.common.base.Stopwatch;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.cadixdev.mercury.SourceProcessor;
import org.cadixdev.mercury.remapper.MercuryRemapper;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
@@ -52,9 +60,8 @@ public class SourceRemapper {
private final Project project;
private String from;
private String to;
private final List<Consumer<Mercury>> remapTasks = new ArrayList<>();
private Mercury mercury;
private final List<RemapTask> remapTasks = new ArrayList<>();
private boolean hasStartedRemap = false;
public SourceRemapper(Project project, boolean named) {
this(project, named ? intermediary(project) : "named", !named ? intermediary(project) : "named");
@@ -71,106 +78,165 @@ public class SourceRemapper {
this.to = to;
}
public static void remapSources(Project project, File input, File output, String from, String to, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
public static void remapSources(Project project, File input, File output, String from, String to, boolean reproducibleFileOrder, boolean preserveFileTimestamps) throws IOException {
SourceRemapper sourceRemapper = new SourceRemapper(project, from, to);
sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps);
sourceRemapper.remapAll();
}
public void scheduleRemapSources(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
remapTasks.add((mercury) -> {
try {
remapSourcesInner(mercury, source, destination);
ZipReprocessorUtil.reprocessZip(destination, reproducibleFileOrder, preserveFileTimestamps);
// Set the remapped sources creation date to match the sources if we're likely succeeded in making it
destination.setLastModified(source.lastModified());
} catch (Exception e) {
// Failed to remap, lets clean up to ensure we try again next time
destination.delete();
throw new RuntimeException("Failed to remap sources for " + source, e);
}
});
this.scheduleRemapSources(new RemapTask(source, destination, reproducibleFileOrder, preserveFileTimestamps));
}
public void remapAll() {
public synchronized void scheduleRemapSources(RemapTask data) {
if (hasStartedRemap) {
throw new UnsupportedOperationException("Cannot add data after remapping has started");
}
this.remapTasks.add(data);
}
public void remapAll() throws IOException {
hasStartedRemap = true;
if (remapTasks.isEmpty()) {
return;
}
Stopwatch stopwatch = Stopwatch.createStarted();
project.getLogger().lifecycle(":remapping " + remapTasks.size() + " sources");
int threads = Math.min(remapTasks.size(), Math.max(2, Runtime.getRuntime().availableProcessors()));
ExecutorService ioExecutor = Executors.newFixedThreadPool(2);
ExecutorService remapExecutor = Executors.newFixedThreadPool(threads);
Mercury mercury = getMercuryInstance();
ThreadingUtils.run(remapTasks, consumer -> consumer.accept(MercuryUtils.copyMercury(mercury)));
List<CompletableFuture<Void>> completableFutures = new ArrayList<>();
project.getLogger().lifecycle(":remapped " + remapTasks.size() + " sources in " + stopwatch.stop());
{
// We have to build the Mercury instances on the main thread as createMercuryRemapper resolves gradle stuff
// TODO refactor this a bit to not do that.
var mercuryQueue = new ConcurrentLinkedDeque<Mercury>();
final var remapper = createMercuryRemapper();
final var inputClasspath = getInputClasspath(project);
for (int i = 0; i < threads; i++) {
Mercury mercury = createMercuryWithClassPath(project, toNamed);
mercury.getProcessors().add(remapper);
mercury.getClassPath().addAll(inputClasspath);
mercuryQueue.add(mercury);
}
ThreadLocal<Mercury> mercuryThreadLocal = ThreadLocal.withInitial(() -> Objects.requireNonNull(mercuryQueue.pop(), "No Mercury instances left"));
for (RemapTask remapTask : this.remapTasks) {
completableFutures.add(
CompletableFuture.supplyAsync(() ->
prepareForRemap(remapTask), ioExecutor)
.thenApplyAsync(remapData -> doRemap(mercuryThreadLocal.get(), remapData), remapExecutor)
.thenAcceptAsync(remapData -> completeRemap(remapData, remapTask), ioExecutor)
);
}
}
for (CompletableFuture<Void> future : completableFutures) {
try {
future.join();
} catch (CompletionException e) {
try {
throw e.getCause();
} catch (IOException ioe) {
throw ioe;
} catch (Throwable unknown) {
throw new RuntimeException("An unknown exception occured when remapping", unknown);
}
}
}
ioExecutor.shutdown();
remapExecutor.shutdown();
// TODO: FIXME - WORKAROUND https://github.com/FabricMC/fabric-loom/issues/45
System.gc();
}
private void remapSourcesInner(Mercury mercury, File source, File destination) throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
project.getLogger().info(":remapping source jar " + source.getName() + " from " + from + " to " + to);
if (source.equals(destination)) {
if (source.isDirectory()) {
throw new RuntimeException("Directories must differ!");
}
source = new File(destination.getAbsolutePath().substring(0, destination.getAbsolutePath().lastIndexOf('.')) + "-dev.jar");
try {
com.google.common.io.Files.move(destination, source);
} catch (IOException e) {
throw new RuntimeException("Could not rename " + destination.getName() + "!", e);
}
}
Path srcPath = source.toPath();
boolean isSrcTmp = false;
if (!source.isDirectory()) {
// create tmp directory
isSrcTmp = true;
srcPath = Files.createTempDirectory("fabric-loom-src");
ZipUtil.unpack(source, srcPath.toFile());
}
if (!destination.isDirectory() && destination.exists()) {
if (!destination.delete()) {
throw new RuntimeException("Could not delete " + destination.getName() + "!");
}
}
StitchUtil.FileSystemDelegate dstFs = destination.isDirectory() ? null : StitchUtil.getJarFileSystem(destination, true);
Path dstPath = dstFs != null ? dstFs.get().getPath("/") : destination.toPath();
private RemapData prepareForRemap(RemapTask remapTask) {
try {
mercury.rewrite(srcPath, dstPath);
} catch (Exception e) {
project.getLogger().warn("Could not remap " + source.getName() + " fully!", e);
File source = remapTask.source();
final File destination = remapTask.destination();
if (source.equals(destination)) {
if (source.isDirectory()) {
throw new RuntimeException("Directories must differ!");
}
source = new File(destination.getAbsolutePath().substring(0, destination.getAbsolutePath().lastIndexOf('.')) + "-dev.jar");
try {
com.google.common.io.Files.move(destination, source);
} catch (IOException e) {
throw new RuntimeException("Could not rename " + destination.getName() + "!", e);
}
}
Path srcPath = source.toPath();
boolean isSrcTmp = false;
if (!source.isDirectory()) {
// create tmp directory
isSrcTmp = true;
srcPath = Files.createTempDirectory("fabric-loom-src");
ZipUtil.unpack(source, srcPath.toFile());
}
if (!destination.isDirectory() && destination.exists()) {
if (!destination.delete()) {
throw new RuntimeException("Could not delete " + destination.getName() + "!");
}
}
StitchUtil.FileSystemDelegate dstFs = remapTask.destination().isDirectory() ? null : StitchUtil.getJarFileSystem(remapTask.destination(), true);
Path dstPath = dstFs != null ? dstFs.get().getPath("/") : remapTask.destination().toPath();
return new RemapData(Objects.requireNonNull(srcPath, "srcPath"), Objects.requireNonNull(dstPath, "dstPath"), dstFs, isSrcTmp);
} catch (IOException e) {
throw new CompletionException("Failed to prepare for remap", e);
}
copyNonJavaFiles(srcPath, dstPath, project, source);
if (dstFs != null) {
dstFs.close();
}
if (isSrcTmp) {
Files.walkFileTree(srcPath, new DeletingFileVisitor());
}
project.getLogger().info(":remapped source jar " + source.getName() + " from " + from + " to " + to + " in " + stopwatch.stop());
}
private Mercury getMercuryInstance() {
if (this.mercury != null) {
return this.mercury;
private RemapData doRemap(Mercury mercury, RemapData remapData) {
try {
mercury.rewrite(remapData.source(), remapData.destination());
} catch (Exception e) {
project.getLogger().warn("Could not remap " + remapData.source().toString() + " fully!", e);
// TODO do something more with this error? delete the dst file in complete remap?
}
return remapData;
}
private void completeRemap(RemapData remapData, RemapTask remapTask) {
try {
copyNonJavaFiles(remapData.source(), remapData.destination(), project, remapTask.source());
if (remapData.dstFs() != null) {
remapData.dstFs().close();
}
if (remapData.isSrcTmp()) {
Files.walkFileTree(remapData.source(), new DeletingFileVisitor());
}
ZipReprocessorUtil.reprocessZip(remapTask.destination(), remapTask.reproducibleFileOrder(), remapTask.preserveFileTimestamps());
// Set the remapped sources creation date to match the sources if we're likely succeeded in making it
remapTask.destination().setLastModified(remapTask.source().lastModified());
} catch (IOException e) {
throw new CompletionException("Failed to complete remap", e);
}
}
private SourceProcessor createMercuryRemapper() {
LoomGradleExtension extension = LoomGradleExtension.get(project);
MappingsProviderImpl mappingsProvider = extension.getMappingsProvider();
@@ -193,42 +259,7 @@ public class SourceRemapper {
}
});
Mercury mercury = extension.getOrCreateSrcMercuryCache(id, () -> {
Mercury m = createMercuryWithClassPath(project, to.equals("named"));
for (File file : extension.getUnmappedModCollection()) {
Path path = file.toPath();
if (Files.isRegularFile(path)) {
m.getClassPath().add(path);
}
}
m.getClassPath().add(extension.getMinecraftMappedProvider().getMappedJar().toPath());
m.getClassPath().add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath());
if (extension.isForge()) {
m.getClassPath().add(extension.getMinecraftMappedProvider().getSrgJar().toPath());
m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeMappedJar().toPath());
m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeIntermediaryJar().toPath());
m.getClassPath().add(extension.getMinecraftMappedProvider().getForgeSrgJar().toPath());
}
Set<File> files = project.getConfigurations()
.detachedConfiguration(project.getDependencies().create(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS))
.resolve();
for (File file : files) {
m.getClassPath().add(file.toPath());
}
m.getProcessors().add(MercuryRemapper.create(mappings));
return m;
});
this.mercury = mercury;
return mercury;
return MercuryRemapper.create(mappings);
}
private static void copyNonJavaFiles(Path from, Path to, Project project, File source) throws IOException {
@@ -246,26 +277,62 @@ public class SourceRemapper {
}
public static Mercury createMercuryWithClassPath(Project project, boolean toNamed) {
Mercury m = new Mercury();
m.setGracefulClasspathChecks(true);
var mercury = new Mercury();
mercury.setGracefulClasspathChecks(true);
var classpath = mercury.getClassPath();
classpath.addAll(getCompileClasspath(project, toNamed));
return mercury;
}
private static Set<Path> getCompileClasspath(Project project, boolean toNamed) {
var classpath = new HashSet<Path>();
for (File file : project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles()) {
m.getClassPath().add(file.toPath());
classpath.add(file.toPath());
}
if (!toNamed) {
for (File file : project.getConfigurations().getByName("compileClasspath").getFiles()) {
m.getClassPath().add(file.toPath());
for (File file : project.getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).getFiles()) {
classpath.add(file.toPath());
}
} else {
for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) {
for (File inputFile : project.getConfigurations().getByName(entry.sourceConfiguration()).getFiles()) {
m.getClassPath().add(inputFile.toPath());
classpath.add(inputFile.toPath());
}
}
}
return m;
return classpath;
}
private static Set<Path> getInputClasspath(Project project) {
LoomGradleExtension extension = LoomGradleExtension.get(project);
var classpath = new HashSet<Path>();
for (File file : extension.getUnmappedModCollection()) {
Path path = file.toPath();
if (Files.isRegularFile(path)) {
classpath.add(path);
}
}
classpath.add(extension.getMinecraftMappedProvider().getMappedJar().toPath());
classpath.add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath());
Set<File> files = project.getConfigurations()
.detachedConfiguration(project.getDependencies().create(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS))
.resolve();
for (File file : files) {
classpath.add(file.toPath());
}
return classpath;
}
private static boolean isJavaFile(Path path) {
@@ -273,4 +340,10 @@ public class SourceRemapper {
// ".java" is not a valid java file
return name.endsWith(".java") && name.length() != 5;
}
public static record RemapTask(File source, File destination, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
}
public static record RemapData(Path source, Path destination, StitchUtil.FileSystemDelegate dstFs, boolean isSrcTmp) {
}
}