Better Mixin AP configuration (#423)

* [SPON-15] Apply Mixin AP config for all projects.

* Revert "[SPON-15] Apply Mixin AP config for all projects."

This reverts commit 93576e83b1221949d551b6307938f7dd6dc8fbbe.

* use setter & getter

* fix broken test introduced in 54d6ef7896

* initial commit for mixin extension

* refactor getConfiguration

* apply mixin extension

* [SPON-15] allow across project AP config

* [SPON-15] revert some changes

* [SPON-15] refactor codes

* [SPON-15] fix bugs

* [SPON-15] bring back cross-project apconfig

* [SPON-15] bug fix: move add default sourceSet earlier

* [SPON-15] fix style

* [SPON-15] refactor MixinAPExtension

* add test

* update test

* [SPON-15] fix test

* Update MixinAnnotationProcessorExtension.java

* [SPON-15] fix test

* fix deprecated gradle API

* [SPON-15] refactor ApInvoker

* [SPON-15] refactor ApInvoker

* allow change refmap name in sourceSet bases

* add new condition on test

* [SPON-15] fix wrong suffix

* Revert "[SPON-15] fix wrong suffix"

This reverts commit 98910392d91c26cd0454cca8cfc03c4e3d417fd6.

* fix mixinjson suffix

* use stream instead of collection for mixin json name

* change name for function

* use correct auto-refmap

* fix file name

* add with action

* add test

* refactor some codes

* refactor code

* update test

* fix checkstyle

* better error message

* fix checkstyle

* remove corss project option

* allow mixin inside loom

* remove project0

I should remove all project0. If I forget one please tell me.

* move `mixin` inside `loom`

* fix spotless

* merge attempt

* fix checkstyle

* seperate api & impl

* add experimental annotation for API

* use API

* Fix indentation

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* fix typo

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* fix typo

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* better javadoc

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>
dev/0.11
LogicFan 2021-07-18 09:13:47 -04:00 committed by GitHub
parent b4ac68825f
commit 81fa551382
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1342 additions and 104 deletions

View File

@ -44,6 +44,7 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider;
import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.extension.LoomFiles;
import net.fabricmc.loom.extension.LoomGradleExtensionImpl; import net.fabricmc.loom.extension.LoomGradleExtensionImpl;
import net.fabricmc.loom.extension.MixinApExtension;
public interface LoomGradleExtension extends LoomGradleExtensionAPI { public interface LoomGradleExtension extends LoomGradleExtensionAPI {
static LoomGradleExtension get(Project project) { static LoomGradleExtension get(Project project) {
@ -102,4 +103,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI {
// TODO reimplement a way to change this, was never really supported api anyway // TODO reimplement a way to change this, was never really supported api anyway
return String.format("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar", minecraftVersion); return String.format("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar", minecraftVersion);
} }
MixinApExtension getMixinApExtension();
} }

View File

@ -31,6 +31,7 @@ import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.ConfigurableFileCollection;
import org.jetbrains.annotations.ApiStatus;
import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.ide.RunConfigSettings;
@ -81,6 +82,9 @@ public interface LoomGradleExtensionAPI {
NamedDomainObjectContainer<RunConfigSettings> getRunConfigs(); NamedDomainObjectContainer<RunConfigSettings> getRunConfigs();
@ApiStatus.Experimental
void mixin(Action<MixinApExtensionAPI> action);
void setCustomManifest(String customManifest); void setCustomManifest(String customManifest);
String getCustomManifest(); String getCustomManifest();

View File

@ -0,0 +1,92 @@
/*
* 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.api;
import org.gradle.api.Action;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Experimental
public interface MixinApExtensionAPI {
/**
* 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 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}.
*/
void add(SourceSet sourceSet, String refmapName, Action<PatternSet> action);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSet the sourceSet that applies Mixin AP.
* @param refmapName the output ref-map name.
*/
void add(SourceSet sourceSet, String refmapName);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSet the sourceSet that applies Mixin AP.
* @param action used for filter the mixin json files.
*/
void add(SourceSet sourceSet, Action<PatternSet> action);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSet the sourceSet that applies Mixin AP.
*/
void add(SourceSet sourceSet);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSetName the name of sourceSet that applies Mixin AP.
* @param refmapName the output ref-map name.
* @param action used for filter the mixin json files.
*/
void add(String sourceSetName, String refmapName, Action<PatternSet> action);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSetName the name of sourceSet that applies Mixin AP.
* @param refmapName the output ref-map name.
*/
void add(String sourceSetName, String refmapName);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSetName the name of sourceSet that applies Mixin AP.
* @param action used for filter the mixin json files.
*/
void add(String sourceSetName, Action<PatternSet> action);
/**
* Apply Mixin AP to sourceSet. See {@link MixinApExtensionAPI#add(SourceSet, String, Action)} for more detail.
* @param sourceSetName the name of sourceSet that applies Mixin AP.
*/
void add(String sourceSetName);
}

View File

@ -25,71 +25,47 @@
package net.fabricmc.loom.build; package net.fabricmc.loom.build;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashSet; import java.util.Objects;
import java.util.Set; import java.util.stream.Stream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.gradle.api.Project;
import org.zeroturnaround.zip.ZipUtil; import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StringZipEntryTransformer; import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry; import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.extension.MixinApExtension;
public final class MixinRefmapHelper { public final class MixinRefmapHelper {
private MixinRefmapHelper() { } private MixinRefmapHelper() { }
public static boolean addRefmapName(String filename, Path outputPath) { public static boolean addRefmapName(Project project, Path outputPath) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
File output = outputPath.toFile(); File output = outputPath.toFile();
Set<String> mixinFilenames = findMixins(output, true);
if (mixinFilenames.size() > 0) { return mixin.getMixinSourceSetsStream().map(sourceSet -> {
return ZipUtil.transformEntries(output, mixinFilenames.stream().map((f) -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") { MixinApExtension.MixinInformationContainer container = Objects.requireNonNull(
MixinApExtension.getMixinInformationContainer(sourceSet)
);
Stream<String> mixinJsonNames = container.getMixinJsonNames();
String refmapName = container.getRefmapName();
return ZipUtil.transformEntries(output, mixinJsonNames.map(f -> new ZipEntryTransformerEntry(f, new StringZipEntryTransformer("UTF-8") {
@Override @Override
protected String transform(ZipEntry zipEntry, String input) throws IOException { protected String transform(ZipEntry zipEntry, String input) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class); JsonObject json = LoomGradlePlugin.GSON.fromJson(input, JsonObject.class);
if (!json.has("refmap")) { if (!json.has("refmap")) {
json.addProperty("refmap", filename); json.addProperty("refmap", refmapName);
} }
return LoomGradlePlugin.GSON.toJson(json); return LoomGradlePlugin.GSON.toJson(json);
} }
})).toArray(ZipEntryTransformerEntry[]::new)); })).toArray(ZipEntryTransformerEntry[]::new));
} else { }).reduce(false, Boolean::logicalOr);
return false;
}
}
private static Set<String> findMixins(File output, boolean onlyWithoutRefmap) {
// first, identify all of the mixin files
Set<String> mixinFilename = new HashSet<>();
// TODO: this is a lovely hack
ZipUtil.iterate(output, (stream, entry) -> {
if (!entry.isDirectory() && entry.getName().endsWith(".json") && !entry.getName().contains("/") && !entry.getName().contains("\\")) {
// JSON file in root directory
try (InputStreamReader inputStreamReader = new InputStreamReader(stream)) {
JsonObject json = LoomGradlePlugin.GSON.fromJson(inputStreamReader, JsonObject.class);
if (json != null) {
boolean hasMixins = json.has("mixins") && json.get("mixins").isJsonArray();
boolean hasClient = json.has("client") && json.get("client").isJsonArray();
boolean hasServer = json.has("server") && json.get("server").isJsonArray();
if (json.has("package") && (hasMixins || hasClient || hasServer)) {
if (!onlyWithoutRefmap || !json.has("refmap") || !json.has("minVersion")) {
mixinFilename.add(entry.getName());
}
}
}
} catch (Exception ignored) {
// ...
}
}
});
return mixinFilename;
} }
} }

View File

@ -29,16 +29,17 @@ import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream; import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.Task; import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskCollection;
import net.fabricmc.loom.extension.MixinApExtension;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.Constants;
@ -48,37 +49,46 @@ import net.fabricmc.loom.util.Constants;
* See Java and Kapt implementations for a more deep understanding of the things passed by the children. * See Java and Kapt implementations for a more deep understanding of the things passed by the children.
*/ */
public abstract class AnnotationProcessorInvoker<T extends Task> { public abstract class AnnotationProcessorInvoker<T extends Task> {
public static final String JAVA = "java";
public static final String SCALA = "scala";
protected final Project project; protected final Project project;
private final Collection<Configuration> annotationProcessorConfigurations; protected final Map<SourceSet, T> invokerTasks;
protected final TaskCollection<T> invokerTasks; private final Collection<Configuration> apConfigurations;
protected AnnotationProcessorInvoker(Project project, protected AnnotationProcessorInvoker(Project project,
Collection<Configuration> annotationProcessorConfigurations, Collection<Configuration> apConfigurations,
TaskCollection<T> invokerTasks) { Map<SourceSet, T> invokerTasks) {
this.project = project; this.project = project;
this.annotationProcessorConfigurations = annotationProcessorConfigurations; this.apConfigurations = apConfigurations;
this.invokerTasks = invokerTasks; this.invokerTasks = invokerTasks;
} }
protected static Collection<Configuration> getApConfigurations(Project project, Function<String, String> getApConfigNameFunc) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
return mixin.getApConfigurationsStream(getApConfigNameFunc).collect(Collectors.toList());
}
protected abstract void passArgument(T compileTask, String key, String value); protected abstract void passArgument(T compileTask, String key, String value);
protected abstract File getDestinationDir(T task); protected abstract File getRefmapDestinationDir(T task);
protected final String getRefmapDestination(T task, LoomGradleExtension extension) throws IOException { protected final String getRefmapDestination(T task, String refmapName) throws IOException {
return new File(getDestinationDir(task), extension.getRefmapName()).getCanonicalPath(); return new File(getRefmapDestinationDir(task), refmapName).getCanonicalPath();
} }
private void passMixinArguments(T task) { private void passMixinArguments(T task, SourceSet sourceSet) {
try { try {
LoomGradleExtension extension = LoomGradleExtension.get(project); LoomGradleExtension loom = LoomGradleExtension.get(project);
String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName();
Map<String, String> args = new HashMap<>() {{ Map<String, String> args = new HashMap<>() {{
put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, extension.getMappingsProvider().tinyMappings.getCanonicalPath()); put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.getCanonicalPath());
put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, extension.getNextMixinMappings().getCanonicalPath()); put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getNextMixinMappings().getCanonicalPath());
put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, extension)); put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, refmapName));
put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary"); put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary");
}}; }};
project.getLogger().debug("Outputting refmap to dir: " + getDestinationDir(task) + " for compile task: " + task); project.getLogger().debug("Outputting refmap to dir: " + getRefmapDestinationDir(task) + " for compile task: " + task);
args.forEach((k, v) -> passArgument(task, k, v)); args.forEach((k, v) -> passArgument(task, k, v));
} catch (IOException e) { } catch (IOException e) {
project.getLogger().error("Could not configure mixin annotation processors", e); project.getLogger().error("Could not configure mixin annotation processors", e);
@ -90,7 +100,7 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
LoomGradleExtension extension = LoomGradleExtension.get(project); LoomGradleExtension extension = LoomGradleExtension.get(project);
if (!extension.ideSync()) { if (!extension.ideSync()) {
for (Configuration processorConfig : annotationProcessorConfigurations) { for (Configuration processorConfig : apConfigurations) {
project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName()); project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName());
// Pass named MC classpath to mixin AP classpath // Pass named MC classpath to mixin AP classpath
processorConfig.extendsFrom( processorConfig.extendsFrom(
@ -105,14 +115,8 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
} }
} }
for (T task : invokerTasks) { for (Map.Entry<SourceSet, T> entry : invokerTasks.entrySet()) {
passMixinArguments(task); passMixinArguments(entry.getValue(), entry.getKey());
} }
} }
static Stream<SourceSet> getNonTestSourceSets(Project project) {
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
.stream()
.filter(sourceSet -> !sourceSet.getName().equals("test"));
}
} }

View File

@ -25,17 +25,30 @@
package net.fabricmc.loom.build.mixin; package net.fabricmc.loom.build.mixin;
import java.io.File; import java.io.File;
import java.util.List; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.extension.MixinApExtension;
public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> { public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
public JavaApInvoker(Project project) { public JavaApInvoker(Project project) {
super(project, getConfigurations(project), project.getTasks().withType(JavaCompile.class)); super(
project,
AnnotationProcessorInvoker.getApConfigurations(project, JavaApInvoker::getAptConfigurationName),
getInvokerTasks(project));
}
private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue())));
} }
@Override @Override
@ -44,18 +57,10 @@ public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
} }
@Override @Override
protected File getDestinationDir(JavaCompile task) { protected File getRefmapDestinationDir(JavaCompile task) {
return task.getDestinationDir(); return task.getDestinationDir();
} }
private static List<Configuration> getConfigurations(Project project) {
// java plugin generates an AP configuration for every source set based off of the getAptConfigurationName method.
return AnnotationProcessorInvoker.getNonTestSourceSets(project)
.map(sourceSet -> project.getConfigurations()
.getByName(getAptConfigurationName(sourceSet.getName()))
).collect(Collectors.toList());
}
private static String getAptConfigurationName(String sourceSet) { private static String getAptConfigurationName(String sourceSet) {
// This is documented by the gradle 4.6 release notes https://docs.gradle.org/4.6/release-notes.html#potential-breaking-changes // This is documented by the gradle 4.6 release notes https://docs.gradle.org/4.6/release-notes.html#potential-breaking-changes
return sourceSet.equals("main") ? JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME : sourceSet + "AnnotationProcessor"; return sourceSet.equals("main") ? JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME : sourceSet + "AnnotationProcessor";

View File

@ -29,18 +29,18 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import kotlin.Unit; import kotlin.Unit;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.compile.JavaCompile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.gradle.plugin.KaptExtension; import org.jetbrains.kotlin.gradle.plugin.KaptExtension;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.extension.MixinApExtension;
public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> { public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
private final KaptExtension kaptExtension = project.getExtensions().getByType(KaptExtension.class); private final KaptExtension kaptExtension = project.getExtensions().getByType(KaptExtension.class);
@ -48,7 +48,10 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
private final File dummyRefmapDirectory; private final File dummyRefmapDirectory;
public KaptApInvoker(Project project) { public KaptApInvoker(Project project) {
super(project, getConfigurations(project), project.getTasks().withType(JavaCompile.class)); super(
project,
AnnotationProcessorInvoker.getApConfigurations(project, KaptApInvoker::getKaptConfigurationName),
getInvokerTasks(project));
try { try {
dummyRefmapDirectory = Files.createTempDirectory("temp_refmap").toFile(); dummyRefmapDirectory = Files.createTempDirectory("temp_refmap").toFile();
@ -62,18 +65,26 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
kaptExtension.setIncludeCompileClasspath(false); kaptExtension.setIncludeCompileClasspath(false);
} }
private static Map<SourceSet, JavaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.JAVA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((JavaCompile) entry.getValue())));
}
@Override @Override
public void configureMixin() { public void configureMixin() {
super.configureMixin(); super.configureMixin();
for (JavaCompile task : invokerTasks) { for (Map.Entry<SourceSet, JavaCompile> entry : invokerTasks.entrySet()) {
// Kapt only allows specifying javac args to all annotation processors at once. So we need to specify some dummy // Kapt only allows specifying javac args to all annotation processors at once. So we need to specify some dummy
// target location for the refmap and then move it to the correct place for each sourceset // target location for the refmap and then move it to the correct place for each sourceset
JavaCompile task = entry.getValue();
SourceSet sourceSet = entry.getKey();
task.doLast(t -> { task.doLast(t -> {
try { try {
LoomGradleExtension extension = LoomGradleExtension.get(project); String refmapName = Objects.requireNonNull(MixinApExtension.getMixinInformationContainer(sourceSet)).getRefmapName();
Path src = Paths.get(getRefmapDestination(task, extension)); Path src = Paths.get(getRefmapDestination(task, refmapName));
Path dest = Paths.get(task.getDestinationDir().toString(), extension.getRefmapName()); Path dest = Paths.get(task.getDestinationDir().toString(), refmapName);
// Possible that no mixin annotations exist // Possible that no mixin annotations exist
if (Files.exists(src)) { if (Files.exists(src)) {
@ -87,15 +98,6 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
} }
} }
@NotNull
private static List<Configuration> getConfigurations(Project project) {
// Kapt generates an AP configuration for every source set based off of the getKaptConfigurationName method.
return AnnotationProcessorInvoker.getNonTestSourceSets(project)
.map(sourceSet -> project.getConfigurations()
.getByName(getKaptConfigurationName(sourceSet.getName()))
).collect(Collectors.toList());
}
// Pulled out from the internal class: https://github.com/JetBrains/kotlin/blob/33a0ec9b4f40f3d6f1f96b2db504ade4c2fafe03/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt#L92 // Pulled out from the internal class: https://github.com/JetBrains/kotlin/blob/33a0ec9b4f40f3d6f1f96b2db504ade4c2fafe03/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/kapt/Kapt3KotlinGradleSubplugin.kt#L92
private static String getKaptConfigurationName(String sourceSetName) { private static String getKaptConfigurationName(String sourceSetName) {
if (!sourceSetName.equals(SourceSet.MAIN_SOURCE_SET_NAME)) { if (!sourceSetName.equals(SourceSet.MAIN_SOURCE_SET_NAME)) {
@ -116,7 +118,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
} }
@Override @Override
protected File getDestinationDir(JavaCompile task) { protected File getRefmapDestinationDir(JavaCompile task) {
return dummyRefmapDirectory; return dummyRefmapDirectory;
} }
} }

View File

@ -25,17 +25,31 @@
package net.fabricmc.loom.build.mixin; package net.fabricmc.loom.build.mixin;
import java.io.File; import java.io.File;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.gradle.api.Project; import org.gradle.api.Project;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.scala.ScalaCompile; import org.gradle.api.tasks.scala.ScalaCompile;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.extension.MixinApExtension;
public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> { public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
public ScalaApInvoker(Project project) { public ScalaApInvoker(Project project) {
super(project, super(
// Scala just uses the java AP configuration afaik. This of course assumes the java AP also gets configured. project,
ImmutableList.of(), // Scala just uses the java AP configuration afaik. This of course assumes the java AP also gets configured.
project.getTasks().withType(ScalaCompile.class)); ImmutableList.of(),
getInvokerTasks(project));
}
private static Map<SourceSet, ScalaCompile> getInvokerTasks(Project project) {
MixinApExtension mixin = LoomGradleExtension.get(project).getMixinApExtension();
return mixin.getInvokerTasksStream(AnnotationProcessorInvoker.SCALA)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> Objects.requireNonNull((ScalaCompile) entry.getValue())));
} }
@Override @Override
@ -44,7 +58,7 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
} }
@Override @Override
protected File getDestinationDir(ScalaCompile task) { protected File getRefmapDestinationDir(ScalaCompile task) {
return task.getDestinationDir(); return task.getDestinationDir();
} }
} }

View File

@ -33,6 +33,7 @@ import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.jvm.tasks.Jar; import org.gradle.jvm.tasks.Jar;
import net.fabricmc.loom.extension.MixinApExtension;
import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.build.mixin.JavaApInvoker; import net.fabricmc.loom.build.mixin.JavaApInvoker;
import net.fabricmc.loom.build.mixin.KaptApInvoker; import net.fabricmc.loom.build.mixin.KaptApInvoker;
@ -143,6 +144,9 @@ public final class CompileConfiguration {
System.setProperty("log4j.skipJansi", "true"); System.setProperty("log4j.skipJansi", "true");
project.getLogger().info("Configuring compiler arguments for Java"); project.getLogger().info("Configuring compiler arguments for Java");
MixinApExtension mixinApExtension = LoomGradleExtension.get(project).getMixinApExtension();
mixinApExtension.init();
new JavaApInvoker(project).configureMixin(); new JavaApInvoker(project).configureMixin();
if (project.getPluginManager().hasPlugin("scala")) { if (project.getPluginManager().hasPlugin("scala")) {

View File

@ -36,6 +36,7 @@ import org.gradle.api.artifacts.Dependency;
import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.plugins.BasePluginConvention; import org.gradle.api.plugins.BasePluginConvention;
import net.fabricmc.loom.api.MixinApExtensionAPI;
import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.api.LoomGradleExtensionAPI;
import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.ide.RunConfigSettings;
@ -159,6 +160,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
return remapMod; return remapMod;
} }
@Override
public void mixin(Action<MixinApExtensionAPI> action) {
action.execute(getMixinApExtension());
}
@Override @Override
public void setCustomManifest(String customManifest) { public void setCustomManifest(String customManifest) {
Objects.requireNonNull(customManifest, "Custom manifest cannot be null"); Objects.requireNonNull(customManifest, "Custom manifest cannot be null");
@ -174,6 +180,8 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
protected abstract LoomFiles getFiles(); protected abstract LoomFiles getFiles();
protected abstract MixinApExtension getMixinApExtension();
// This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods // This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods
private final class EnsureCompile extends LoomGradleExtensionApiImpl { private final class EnsureCompile extends LoomGradleExtensionApiImpl {
private EnsureCompile() { private EnsureCompile() {
@ -190,5 +198,10 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
protected LoomFiles getFiles() { protected LoomFiles getFiles() {
throw new RuntimeException("Yeah... something is really wrong"); throw new RuntimeException("Yeah... something is really wrong");
} }
@Override
protected MixinApExtension getMixinApExtension() {
throw new RuntimeException("Yeah... something is really wrong");
}
} }
} }

View File

@ -47,6 +47,7 @@ import net.fabricmc.loom.configuration.processors.JarProcessorManager;
public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implements LoomGradleExtension { public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implements LoomGradleExtension {
private final Project project; private final Project project;
private final MixinApExtension mixinApExtension;
private final LoomFiles loomFiles; private final LoomFiles loomFiles;
private final ConfigurableFileCollection unmappedMods; private final ConfigurableFileCollection unmappedMods;
@ -62,6 +63,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
public LoomGradleExtensionImpl(Project project, LoomFiles files) { public LoomGradleExtensionImpl(Project project, LoomFiles files) {
super(project, files); super(project, files);
this.project = project; this.project = project;
this.mixinApExtension = new MixinApExtensionImpl(project);
this.loomFiles = files; this.loomFiles = files;
this.unmappedMods = project.files(); this.unmappedMods = project.files();
} }
@ -160,4 +162,9 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen
return provider; return provider;
} }
@Override
public MixinApExtension getMixinApExtension() {
return this.mixinApExtension;
}
} }

View File

@ -0,0 +1,126 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2017 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 java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Stream;
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.tasks.Input;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.api.MixinApExtensionAPI;
/**
* A gradle extension to configure mixin annotation processor.
*/
@ApiStatus.Experimental
public interface MixinApExtension extends MixinApExtensionAPI {
String MIXIN_INFORMATION_CONTAINER = "mixin";
/**
* An information container stores necessary information
* 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;
}
}
@Nullable
static MixinInformationContainer getMixinInformationContainer(SourceSet sourceSet) {
ExtraPropertiesExtension extra = sourceSet.getExtensions().getExtraProperties();
return extra.has(MIXIN_INFORMATION_CONTAINER) ? (MixinInformationContainer) extra.get(MIXIN_INFORMATION_CONTAINER) : null;
}
static void setMixinInformationContainer(SourceSet sourceSet, MixinInformationContainer container) {
ExtraPropertiesExtension extra = sourceSet.getExtensions().getExtraProperties();
if (extra.has(MIXIN_INFORMATION_CONTAINER)) {
throw new InvalidUserDataException("The sourceSet " + sourceSet.getName()
+ " has been configured for mixin annotation processor multiple times");
}
extra.set(MIXIN_INFORMATION_CONTAINER, container);
}
@NotNull
Stream<SourceSet> getMixinSourceSetsStream();
@NotNull
Stream<Configuration> getApConfigurationsStream(Function<String, String> getApConfigNameFunc);
@NotNull
Stream<Map.Entry<SourceSet, Task>> getInvokerTasksStream(String compileTaskLanguage);
@NotNull
@Input
Collection<SourceSet> getMixinSourceSets();
void init();
}

View File

@ -0,0 +1,110 @@
/*
* 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.InvalidUserDataException;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
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);
@Override
public void add(SourceSet sourceSet, String refmapName, Action<PatternSet> action) {
PatternSet pattern = add0(sourceSet, refmapName);
action.execute(pattern);
}
@Override
public void add(SourceSet sourceSet, String refmapName) {
add(sourceSet, refmapName, x -> { });
}
@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);
if (sourceSet == null) {
throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found");
}
PatternSet pattern = add0(sourceSet, refmapName);
action.execute(pattern);
}
@Override
public void add(String sourceSetName, String refmapName) {
add(sourceSetName, refmapName, x -> { });
}
@Override
public void add(SourceSet sourceSet, Action<PatternSet> action) {
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
add(sourceSet, extension.getRefmapName(), action);
}
@Override
public void add(SourceSet sourceSet) {
add(sourceSet, x -> { });
}
@Override
public void add(String sourceSetName, Action<PatternSet> action) {
LoomGradleExtension extension = LoomGradleExtension.get(getProject());
add(sourceSetName, extension.getRefmapName(), action);
}
@Override
public void add(String sourceSetName) {
add(sourceSetName, x -> { });
}
// This is here to ensure that LoomGradleExtensionApiImpl compiles without any unimplemented methods
private final class EnsureCompile extends MixinApExtensionApiImpl {
private EnsureCompile() {
super();
throw new RuntimeException();
}
@Override
protected Project getProject() {
throw new RuntimeException("Yeah... something is really wrong");
}
@Override
protected PatternSet add0(SourceSet sourceSet, String refmapName) {
throw new RuntimeException("Yeah... something is really wrong");
}
}
}

View File

@ -0,0 +1,125 @@
/*
* 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 java.io.File;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
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.JavaPluginConvention;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.util.PatternSet;
import org.jetbrains.annotations.NotNull;
public class MixinApExtensionImpl extends MixinApExtensionApiImpl implements MixinApExtension {
private boolean isDefault;
private final Project project;
public MixinApExtensionImpl(Project project) {
this.isDefault = true;
this.project = project;
}
@Override
public Project getProject() {
return this.project;
}
@Override
protected PatternSet add0(SourceSet sourceSet, String refmapName) {
PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.mixins.json"));
MixinApExtension.setMixinInformationContainer(sourceSet, new MixinApExtension.MixinInformationContainer(sourceSet, refmapName, pattern));
isDefault = false;
return pattern;
}
@Override
@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;
});
}
@Override
@NotNull
public Stream<Configuration> getApConfigurationsStream(Function<String, String> getApConfigNameFunc) {
return getMixinSourceSetsStream()
.map(sourceSet -> project.getConfigurations().getByName(getApConfigNameFunc.apply(sourceSet.getName())));
}
@Override
@NotNull
public Stream<Map.Entry<SourceSet, Task>> getInvokerTasksStream(String compileTaskLanguage) {
return getMixinSourceSetsStream()
.flatMap(sourceSet -> {
try {
Task task = project.getTasks().getByName(sourceSet.getCompileTaskName(compileTaskLanguage));
return Stream.of(new AbstractMap.SimpleEntry<>(sourceSet, task));
} catch (UnknownTaskException ignored) {
return Stream.empty();
}
});
}
@Override
@NotNull
@Input
public Collection<SourceSet> getMixinSourceSets() {
return getMixinSourceSetsStream().collect(Collectors.toList());
}
@Override
public void init() {
if (isDefault) {
project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().forEach(this::add);
}
isDefault = false;
}
}

View File

@ -170,7 +170,7 @@ public class RemapJarTask extends Jar {
throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!"); throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!");
} }
if (MixinRefmapHelper.addRefmapName(extension.getRefmapName(), output)) { if (MixinRefmapHelper.addRefmapName(project, output)) {
project.getLogger().debug("Transformed mixin reference maps in output JAR!"); project.getLogger().debug("Transformed mixin reference maps in output JAR!");
} }

View File

@ -0,0 +1,73 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2017 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.test.integration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import com.google.gson.JsonParser;
import java.util.jar.JarFile
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class MixinApAutoRefmapTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"mixinApAutoRefmap"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
// verify the ref-map name is correctly generated
def jar = new JarFile(getOutputFile("fabric-example-mod-1.0.0-universal.jar").absoluteFile)
jar.getEntry("refmap0000.json") == null
jar.getEntry("refmap0001.json") != null
jar.getEntry("refmap0002.json") != null
jar.getEntry("refmap0003.json") != null
def j1 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("main.mixins.json"))))
j1.asJsonObject.getAsJsonPrimitive("refmap").getAsString() == "refmap0001.json"
def j2 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("m0.mixins.json"))))
j2.asJsonObject.getAsJsonPrimitive("refmap").getAsString() == "refmap0002.json"
def j3 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("m1_1.mixins.json"))))
j3.asJsonObject.getAsJsonPrimitive("refmap").getAsString() == "refmap0003.json"
def j4 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("m1_2.mixins.json"))))
!j4.asJsonObject.has("refmap")
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016-2017 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.test.integration
import net.fabricmc.loom.test.util.ProjectTestTrait
import spock.lang.Specification
import spock.lang.Unroll
import java.util.jar.JarFile
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS
class MixinApSimpleTest extends Specification implements ProjectTestTrait {
@Override
String name() {
"mixinApSimple"
}
@Unroll
def "build (gradle #gradle)"() {
when:
def result = create("build", gradle)
then:
result.task(":build").outcome == SUCCESS
// verify the ref-map name is correctly generated
def main = new JarFile(getOutputFile("fabric-example-mod-1.0.0-dev.jar").absoluteFile)
main.getEntry("main-refmap0000.json") != null
def mixin = new JarFile(getOutputFile("fabric-example-mod-1.0.0-mixin.jar").absoluteFile)
mixin.getEntry("default-refmap0000.json") != null
def mixin1 = new JarFile(getOutputFile("fabric-example-mod-1.0.0-mixin1.jar").absoluteFile)
mixin1.getEntry("main-refmap0000.json") == null
mixin1.getEntry("default-refmap0000.json") == null
where:
gradle | _
DEFAULT_GRADLE | _
PRE_RELEASE_GRADLE | _
}
}

View File

@ -0,0 +1,139 @@
plugins {
id 'fabric-loom'
id 'com.github.johnrengelman.shadow' version '7.0.0'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
sourceSets.register("mixin") {
it.compileClasspath += sourceSets["main"].compileClasspath
it.runtimeClasspath += sourceSets["main"].runtimeClasspath
}
sourceSets.register("mixin1") {
it.compileClasspath += sourceSets["main"].compileClasspath
it.runtimeClasspath += sourceSets["main"].runtimeClasspath
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
// Minecraft 1.17 (21w19a) upwards uses Java 16.
it.options.release = 16
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
loom {
refmapName = "refmap0000.json"
mixin {
add(sourceSets["main"], "refmap0001.json")
add(sourceSets["mixin"], "refmap0002.json")
add(sourceSets["mixin1"], "refmap0003.json") {
it.exclude("m1_2.mixins.json")
}
}
}
shadowJar {
archiveClassifier.set("universal-dev")
configurations = []
from(sourceSets["main"].output)
from(sourceSets["mixin"].output)
from(sourceSets["mixin1"].output)
}
remapJar {
dependsOn(shadowJar)
archiveClassifier.set("universal")
input.fileValue(tasks["shadowJar"].outputs.files.singleFile)
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}
tasks.register("mixinJar0", Jar.class) {
it.archiveClassifier.set("mixin")
it.from(sourceSets["mixin"].output)
}
tasks.register("mixinJar1", Jar.class) {
it.archiveClassifier.set("mixin1")
it.from(sourceSets["mixin1"].output)
}
assemble {
dependsOn tasks["mixinJar0"]
dependsOn tasks["mixinJar1"]
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(remapJar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

View File

@ -0,0 +1,16 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/versions.html
minecraft_version=1.17
yarn_mappings=1.17+build.13
loader_version=0.11.6
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
fabric_version=0.36.0+1.17

View File

@ -0,0 +1,10 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
mavenLocal()
}
}

View File

@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.ChatScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChatScreen.class)
public class ExampleMixinMain {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

View File

@ -0,0 +1,37 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/modid/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
],
"depends": {
"fabricloader": ">=0.11.3",
"fabric": "*",
"minecraft": "1.17.x",
"java": ">=16"
},
"suggests": {
"another-mod": "*"
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixinMain"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin0 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin0"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin1_1 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin1_2 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin1_1"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin1_2"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,119 @@
plugins {
id 'fabric-loom' version '0.9.local'
id 'maven-publish'
}
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
archivesBaseName = project.archives_base_name
version = project.mod_version
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
sourceSets.register("mixin") {
it.compileClasspath += sourceSets["main"].compileClasspath
it.runtimeClasspath += sourceSets["main"].runtimeClasspath
}
sourceSets.register("mixin1") {
it.compileClasspath += sourceSets["main"].compileClasspath
it.runtimeClasspath += sourceSets["main"].runtimeClasspath
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
}
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
// Minecraft 1.17 (21w19a) upwards uses Java 16.
it.options.release = 16
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
loom {
refmapName = "default-refmap0000.json"
mixin {
add(sourceSets["main"], "main-refmap0000.json")
add(sourceSets["mixin"])
}
}
jar {
from("LICENSE") {
rename { "${it}_${project.archivesBaseName}"}
}
}
tasks.register("mixinJar0", Jar.class) {
it.archiveClassifier.set("mixin")
it.from(sourceSets["mixin"].output)
}
tasks.register("mixinJar1", Jar.class) {
it.archiveClassifier.set("mixin1")
it.from(sourceSets["mixin1"].output)
}
assemble {
dependsOn tasks["mixinJar0"]
dependsOn tasks["mixinJar1"]
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven
artifact(remapJar) {
builtBy remapJar
}
artifact(sourcesJar) {
builtBy remapSourcesJar
}
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

View File

@ -0,0 +1,16 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
# Fabric Properties
# check these on https://fabricmc.net/versions.html
minecraft_version=1.17
yarn_mappings=1.17+build.13
loader_version=0.11.6
# Mod Properties
mod_version = 1.0.0
maven_group = com.example
archives_base_name = fabric-example-mod
# Dependencies
fabric_version=0.36.0+1.17

View File

@ -0,0 +1,10 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
gradlePluginPortal()
mavenLocal()
}
}

View File

@ -0,0 +1,14 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.ChatScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChatScreen.class)
public class ExampleMixin1 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

View File

@ -0,0 +1,39 @@
{
"schemaVersion": 1,
"id": "modid",
"version": "${version}",
"name": "Example Mod",
"description": "This is an example description! Tell everyone what your mod is about!",
"authors": [
"Me!"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/modid/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"net.fabricmc.example.ExampleMod"
]
},
"mixins": [
"main.mixins.json",
"m0.mixins.json"
],
"depends": {
"fabricloader": ">=0.11.3",
"fabric": "*",
"minecraft": "1.17.x",
"java": ">=16"
},
"suggests": {
"another-mod": "*"
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin1"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin0 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin0"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,15 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(TitleScreen.class)
public class ExampleMixin1 {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.fabricmc.example.mixin",
"compatibilityLevel": "JAVA_16",
"mixins": [
],
"client": [
"ExampleMixin0"
],
"injectors": {
"defaultRequire": 1
}
}