Allow applying kapt and using mixins in other source sets (#211)

* Fix kapt and source sets

* cleanup

* cleanup 2
This commit is contained in:
Fudge 2020-05-22 16:38:25 +03:00 committed by GitHub
parent 5d468efc48
commit cf13e4aa02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 373 additions and 64 deletions

View file

@ -62,6 +62,9 @@ dependencies {
// source code remapping
implementation ('org.cadixdev:mercury:0.1.0.fabric-SNAPSHOT')
// Kapt integration
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72")
// Testing
testImplementation(gradleTestKit())
testImplementation("org.spockframework:spock-core:1.3-groovy-2.4")

View file

@ -24,8 +24,6 @@
package net.fabricmc.loom;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
@ -47,9 +45,7 @@ import org.gradle.api.publish.PublishingExtension;
import org.gradle.api.publish.maven.MavenPublication;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.api.tasks.scala.ScalaCompile;
import org.gradle.plugins.ide.idea.model.IdeaModel;
import net.fabricmc.loom.providers.LaunchProvider;
@ -63,6 +59,9 @@ import net.fabricmc.loom.util.LoomDependencyManager;
import net.fabricmc.loom.util.NestedJars;
import net.fabricmc.loom.util.RemappedConfigurationEntry;
import net.fabricmc.loom.util.SetupIntelijRunConfigs;
import net.fabricmc.loom.util.mixin.JavaApInvoker;
import net.fabricmc.loom.util.mixin.KaptApInvoker;
import net.fabricmc.loom.util.mixin.ScalaApInvoker;
public class AbstractPlugin implements Plugin<Project> {
protected Project project;
@ -88,7 +87,6 @@ public class AbstractPlugin implements Plugin<Project> {
project.getExtensions().create("minecraft", LoomGradleExtension.class, project);
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
// Force add Mojang repository
addMavenRepo(target, "Mojang", "https://libraries.minecraft.net/");
@ -127,74 +125,43 @@ public class AbstractPlugin implements Plugin<Project> {
extendsFrom("compileClasspath", Constants.MINECRAFT_NAMED);
extendsFrom("runtimeClasspath", Constants.MINECRAFT_NAMED);
if (!extension.ideSync()) {
extendsFrom("annotationProcessor", Constants.MINECRAFT_NAMED);
extendsFrom("annotationProcessor", Constants.MOD_COMPILE_CLASSPATH_MAPPED);
}
extendsFrom(Constants.MINECRAFT_NAMED, Constants.MINECRAFT_DEPENDENCIES);
extendsFrom("compile", Constants.MAPPINGS_FINAL);
if (!extension.ideSync()) {
extendsFrom("annotationProcessor", Constants.MAPPINGS_FINAL);
}
configureIDEs();
configureCompile();
configureScala();
configureMixin();
configureMaven();
}
Map<Project, Set<Task>> taskMap = project.getAllTasks(true);
for (Map.Entry<Project, Set<Task>> entry : taskMap.entrySet()) {
Project project = entry.getKey();
Set<Task> taskSet = entry.getValue();
for (Task task : taskSet) {
if (task instanceof JavaCompile && !(task.getName().contains("Test")) && !(task.getName().contains("test"))) {
JavaCompile javaCompileTask = (JavaCompile) task;
javaCompileTask.doFirst(task1 -> {
project.getLogger().lifecycle(":setting java compiler args");
try {
javaCompileTask.getOptions().getCompilerArgs().add("-AinMapFileNamedIntermediary=" + extension.getMappingsProvider().tinyMappings.getCanonicalPath());
javaCompileTask.getOptions().getCompilerArgs().add("-AoutMapFileNamedIntermediary=" + extension.getMappingsProvider().mappingsMixinExport.getCanonicalPath());
javaCompileTask.getOptions().getCompilerArgs().add("-AoutRefMapFile=" + new File(javaCompileTask.getDestinationDir(), extension.getRefmapName()).getCanonicalPath());
javaCompileTask.getOptions().getCompilerArgs().add("-AdefaultObfuscationEnv=named:intermediary");
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
private void configureMixin() {
if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
// If loom is applied after kapt, then kapt will use the AP arguments too early for loom to pass the arguments we need for mixin.
throw new IllegalArgumentException("fabric-loom must be applied BEFORE kapt in the plugins { } block.");
}
configureMaven();
// Full plugin and mappings information is only available after evaluation
project.afterEvaluate((project) -> {
project.getLogger().lifecycle("Configuring mixins for Java plugin");
new JavaApInvoker(project).configureMixin();
if (project.getPluginManager().hasPlugin("scala")) {
project.getLogger().lifecycle("Configuring mixins for Scala plugin");
new ScalaApInvoker(project).configureMixin();
}
if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
project.getLogger().lifecycle("Configuring mixins for Kapt plugin");
new KaptApInvoker(project).configureMixin();
}
});
}
public Project getProject() {
return project;
}
protected void configureScala() {
project.afterEvaluate(proj -> {
if (project.getPluginManager().hasPlugin("scala")) {
ScalaCompile task = (ScalaCompile) project.getTasks().getByName("compileScala");
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
project.getLogger().warn(":configuring scala compilation processing");
try {
task.getOptions().getCompilerArgs().add("-AinMapFileNamedIntermediary=" + extension.getMappingsProvider().tinyMappings.getCanonicalPath());
task.getOptions().getCompilerArgs().add("-AoutMapFileNamedIntermediary=" + extension.getMappingsProvider().mappingsMixinExport.getCanonicalPath());
task.getOptions().getCompilerArgs().add("-AoutRefMapFile=" + new File(task.getDestinationDir(), extension.getRefmapName()).getCanonicalPath());
task.getOptions().getCompilerArgs().add("-AdefaultObfuscationEnv=named:intermediary");
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
/**
* Permit to add a Maven repository to a target project.
*
@ -234,11 +201,6 @@ public class AbstractPlugin implements Plugin<Project> {
Javadoc javadoc = (Javadoc) project.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME);
javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath()));
if (!project.getExtensions().getByType(LoomGradleExtension.class).ideSync()) {
// Add Mixin dependencies
project.getDependencies().add(JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME, "net.fabricmc:fabric-mixin-compile-extensions:" + Constants.MIXIN_COMPILE_EXTENSIONS_VERSION);
}
project.afterEvaluate(project1 -> {
LoomGradleExtension extension = project1.getExtensions().getByType(LoomGradleExtension.class);

View file

@ -311,7 +311,7 @@ public class LoomGradleExtension {
public String getRefmapName() {
if (refmapName == null || refmapName.isEmpty()) {
String defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json";
project.getLogger().warn("Could not find refmap definition, will be using default name: " + defaultRefmapName);
project.getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName);
refmapName = defaultRefmapName;
}

View file

@ -0,0 +1,118 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.mixin;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.TaskCollection;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants;
/**
* Normally javac invokes annotation processors, but when the scala or kapt plugin are installed they will want to invoke
* the annotation processor themselves.
* See Java and Kapt implementations for a more deep understanding of the things passed by the children.
*/
public abstract class AnnotationProcessorInvoker<T extends Task> {
protected final Project project;
private final Collection<Configuration> annotationProcessorConfigurations;
protected final TaskCollection<T> invokerTasks;
protected AnnotationProcessorInvoker(Project project,
Collection<Configuration> annotationProcessorConfigurations,
TaskCollection<T> invokerTasks) {
this.project = project;
this.annotationProcessorConfigurations = annotationProcessorConfigurations;
this.invokerTasks = invokerTasks;
}
protected abstract void passArgument(T compileTask, String key, String value);
protected abstract File getDestinationDir(T task);
protected final String getRefmapDestination(T task, LoomGradleExtension extension) throws IOException {
return new File(getDestinationDir(task), extension.getRefmapName()).getCanonicalPath();
}
private void passMixinArguments(T task) {
try {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
Map<String, String> args = new HashMap<String, String>() {{
put("inMapFileNamedIntermediary", extension.getMappingsProvider().tinyMappings.getCanonicalPath());
put("outMapFileNamedIntermediary", extension.getMappingsProvider().mappingsMixinExport.getCanonicalPath());
put("outRefMapFile", getRefmapDestination(task, extension));
put("defaultObfuscationEnv", "named:intermediary");
}};
project.getLogger().info("Outputting refmap to dir: " + getDestinationDir(task) + " for compile task: " + task);
args.forEach((k, v) -> passArgument(task, k, v));
} catch (IOException e) {
project.getLogger().error("Could not configure mixin annotation processors", e);
}
}
public void configureMixin() {
ConfigurationContainer configs = project.getConfigurations();
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
if (!extension.ideSync()) {
for (Configuration processorConfig : annotationProcessorConfigurations) {
project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName());
// Pass named MC classpath to mixin AP classpath
processorConfig.extendsFrom(
configs.getByName(Constants.MINECRAFT_NAMED),
configs.getByName(Constants.MOD_COMPILE_CLASSPATH_MAPPED),
configs.getByName(Constants.MAPPINGS_FINAL)
);
// Add Mixin and mixin extensions (fabric-mixin-compile-extensions pulls mixin itself too)
project.getDependencies().add(processorConfig.getName(),
"net.fabricmc:fabric-mixin-compile-extensions:" + Constants.MIXIN_COMPILE_EXTENSIONS_VERSION);
}
}
for (T task : invokerTasks) {
passMixinArguments(task);
}
}
static Stream<SourceSet> getNonTestSourceSets(Project project) {
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets()
.stream()
.filter(sourceSet -> !sourceSet.getName().equals("test"));
}
}

View file

@ -0,0 +1,63 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.mixin;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.compile.JavaCompile;
public class JavaApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
public JavaApInvoker(Project project) {
super(project, getConfigurations(project), project.getTasks().withType(JavaCompile.class));
}
@Override
protected void passArgument(JavaCompile compileTask, String key, String value) {
compileTask.getOptions().getCompilerArgs().add("-A" + key + "=" + value);
}
@Override
protected File getDestinationDir(JavaCompile task) {
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) {
// 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";
}
}

View file

@ -0,0 +1,113 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.mixin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import kotlin.Unit;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.tasks.compile.JavaCompile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.gradle.internal.Kapt3KotlinGradleSubplugin;
import org.jetbrains.kotlin.gradle.plugin.KaptExtension;
import net.fabricmc.loom.LoomGradleExtension;
public class KaptApInvoker extends AnnotationProcessorInvoker<JavaCompile> {
private final KaptExtension kaptExtension = project.getExtensions().getByType(KaptExtension.class);
// Refmap will be written to here with mixin, then moved after JavaCompile to the correct place
private final File dummyRefmapDirectory;
public KaptApInvoker(Project project) {
super(project, getConfigurations(project), project.getTasks().withType(JavaCompile.class));
try {
dummyRefmapDirectory = Files.createTempDirectory("temp_refmap").toFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
dummyRefmapDirectory.deleteOnExit();
// Needed for mixin AP to run
kaptExtension.setIncludeCompileClasspath(false);
}
@Override
public void configureMixin() {
super.configureMixin();
for (JavaCompile task : invokerTasks) {
// 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
task.doLast(t -> {
try {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
Path src = Paths.get(getRefmapDestination(task, extension));
Path dest = Paths.get(task.getDestinationDir().toString(), extension.getRefmapName());
// Possible that no mixin annotations exist
if (Files.exists(src)) {
project.getLogger().info("Copying refmap from " + src + " to " + dest);
Files.move(src, dest);
}
} catch (IOException e) {
project.getLogger().warn("Could not move refmap generated by kapt for task " + task, e);
}
});
}
}
@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(Kapt3KotlinGradleSubplugin.Companion.getKaptConfigurationName(sourceSet.getName()))
).collect(Collectors.toList());
}
@Override
protected void passArgument(JavaCompile compileTask, String key, String value) {
// Note: this MUST be run early on, before kapt uses this data, and there is only a point to setting the value once since
// kapt shares the options with all java compilers
kaptExtension.arguments(args -> {
args.arg(key, value);
return Unit.INSTANCE;
});
}
@Override
protected File getDestinationDir(JavaCompile task) {
return dummyRefmapDirectory;
}
}

View file

@ -0,0 +1,50 @@
/*
* This file is part of fabric-loom, licensed under the MIT License (MIT).
*
* Copyright (c) 2016, 2017, 2018 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.mixin;
import java.io.File;
import com.google.common.collect.ImmutableList;
import org.gradle.api.Project;
import org.gradle.api.tasks.scala.ScalaCompile;
public class ScalaApInvoker extends AnnotationProcessorInvoker<ScalaCompile> {
public ScalaApInvoker(Project project) {
super(project,
// Scala just uses the java AP configuration afaik. This of course assumes the java AP also gets configured.
ImmutableList.of(),
project.getTasks().withType(ScalaCompile.class));
}
@Override
protected void passArgument(ScalaCompile compileTask, String key, String value) {
compileTask.getOptions().getCompilerArgs().add("-A" + key + "=" + value);
}
@Override
protected File getDestinationDir(ScalaCompile task) {
return task.getDestinationDir();
}
}