mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-04-02 21:47:42 -05:00
Merge remote-tracking branch 'upstream/dev/0.5' into forge
# Conflicts: # src/main/java/net/fabricmc/loom/AbstractPlugin.java # src/main/java/net/fabricmc/loom/LoomGradleExtension.java # src/main/java/net/fabricmc/loom/providers/MappingsProvider.java # src/main/java/net/fabricmc/loom/task/RemapJarTask.java # src/main/java/net/fabricmc/loom/util/Constants.java
This commit is contained in:
@@ -38,6 +38,8 @@ import org.gradle.api.Task;
|
||||
import org.gradle.api.UnknownTaskException;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.ExcludeRule;
|
||||
import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
@@ -114,40 +116,40 @@ public class AbstractPlugin implements Plugin<Project> {
|
||||
addMavenRepo(target, "Mojang", "https://libraries.minecraft.net/");
|
||||
addMavenRepo(target, "Forge", "https://files.minecraftforge.net/maven/");
|
||||
|
||||
Configuration modCompileClasspathConfig = project.getConfigurations().maybeCreate(Constants.MOD_COMPILE_CLASSPATH);
|
||||
Configuration modCompileClasspathConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MOD_COMPILE_CLASSPATH);
|
||||
modCompileClasspathConfig.setTransitive(true);
|
||||
Configuration modCompileClasspathMappedConfig = project.getConfigurations().maybeCreate(Constants.MOD_COMPILE_CLASSPATH_MAPPED);
|
||||
Configuration modCompileClasspathMappedConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED);
|
||||
modCompileClasspathMappedConfig.setTransitive(false);
|
||||
|
||||
Configuration minecraftNamedConfig = project.getConfigurations().maybeCreate(Constants.MINECRAFT_NAMED);
|
||||
Configuration minecraftNamedConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MINECRAFT_NAMED);
|
||||
minecraftNamedConfig.setTransitive(false); // The launchers do not recurse dependencies
|
||||
Configuration minecraftDependenciesConfig = project.getConfigurations().maybeCreate(Constants.MINECRAFT_DEPENDENCIES);
|
||||
Configuration minecraftDependenciesConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MINECRAFT_DEPENDENCIES);
|
||||
minecraftDependenciesConfig.setTransitive(false);
|
||||
Configuration minecraftConfig = project.getConfigurations().maybeCreate(Constants.MINECRAFT);
|
||||
Configuration minecraftConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MINECRAFT);
|
||||
minecraftConfig.setTransitive(false);
|
||||
|
||||
if (project.getExtensions().getByType(LoomGradleExtension.class).isForge()) {
|
||||
Configuration forgeConfig = project.getConfigurations().maybeCreate(Constants.FORGE);
|
||||
Configuration forgeConfig = project.getConfigurations().maybeCreate(Constants.Configurations.FORGE);
|
||||
forgeConfig.setTransitive(false);
|
||||
Configuration forgeUserdevConfig = project.getConfigurations().maybeCreate(Constants.FORGE_USERDEV);
|
||||
Configuration forgeUserdevConfig = project.getConfigurations().maybeCreate(Constants.Configurations.FORGE_USERDEV);
|
||||
forgeUserdevConfig.setTransitive(false);
|
||||
Configuration forgeInstallerConfig = project.getConfigurations().maybeCreate(Constants.FORGE_INSTALLER);
|
||||
Configuration forgeInstallerConfig = project.getConfigurations().maybeCreate(Constants.Configurations.FORGE_INSTALLER);
|
||||
forgeInstallerConfig.setTransitive(false);
|
||||
Configuration forgeUniversalConfig = project.getConfigurations().maybeCreate(Constants.FORGE_UNIVERSAL);
|
||||
Configuration forgeUniversalConfig = project.getConfigurations().maybeCreate(Constants.Configurations.FORGE_UNIVERSAL);
|
||||
forgeUniversalConfig.setTransitive(false);
|
||||
Configuration forgeDependencies = project.getConfigurations().maybeCreate(Constants.FORGE_DEPENDENCIES);
|
||||
Configuration forgeDependencies = project.getConfigurations().maybeCreate(Constants.Configurations.FORGE_DEPENDENCIES);
|
||||
forgeDependencies.setTransitive(false);
|
||||
Configuration mcpConfig = project.getConfigurations().maybeCreate(Constants.MCP_CONFIG);
|
||||
Configuration mcpConfig = project.getConfigurations().maybeCreate(Constants.Configurations.MCP_CONFIG);
|
||||
mcpConfig.setTransitive(false);
|
||||
|
||||
extendsFrom(Constants.MINECRAFT_DEPENDENCIES, Constants.FORGE_DEPENDENCIES);
|
||||
extendsFrom(Constants.Configurations.MINECRAFT_DEPENDENCIES, Constants.Configurations.FORGE_DEPENDENCIES);
|
||||
}
|
||||
|
||||
Configuration includeConfig = project.getConfigurations().maybeCreate(Constants.INCLUDE);
|
||||
Configuration includeConfig = project.getConfigurations().maybeCreate(Constants.Configurations.INCLUDE);
|
||||
includeConfig.setTransitive(false); // Dont get transitive deps
|
||||
|
||||
project.getConfigurations().maybeCreate(Constants.MAPPINGS);
|
||||
project.getConfigurations().maybeCreate(Constants.MAPPINGS_FINAL);
|
||||
project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS);
|
||||
project.getConfigurations().maybeCreate(Constants.Configurations.MAPPINGS_FINAL);
|
||||
|
||||
for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) {
|
||||
Configuration compileModsConfig = project.getConfigurations().maybeCreate(entry.getSourceConfiguration());
|
||||
@@ -158,19 +160,19 @@ public class AbstractPlugin implements Plugin<Project> {
|
||||
extendsFrom(entry.getTargetConfiguration(project.getConfigurations()), entry.getRemappedConfiguration());
|
||||
|
||||
if (entry.isOnModCompileClasspath()) {
|
||||
extendsFrom(Constants.MOD_COMPILE_CLASSPATH, entry.getSourceConfiguration());
|
||||
extendsFrom(Constants.MOD_COMPILE_CLASSPATH_MAPPED, entry.getRemappedConfiguration());
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH, entry.getSourceConfiguration());
|
||||
extendsFrom(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, entry.getRemappedConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
extendsFrom("compileClasspath", Constants.MINECRAFT_NAMED);
|
||||
extendsFrom("runtimeClasspath", Constants.MINECRAFT_NAMED);
|
||||
extendsFrom("testCompileClasspath", Constants.MINECRAFT_NAMED);
|
||||
extendsFrom("testRuntimeClasspath", Constants.MINECRAFT_NAMED);
|
||||
extendsFrom("compileClasspath", Constants.Configurations.MINECRAFT_NAMED);
|
||||
extendsFrom("runtimeClasspath", Constants.Configurations.MINECRAFT_NAMED);
|
||||
extendsFrom("testCompileClasspath", Constants.Configurations.MINECRAFT_NAMED);
|
||||
extendsFrom("testRuntimeClasspath", Constants.Configurations.MINECRAFT_NAMED);
|
||||
|
||||
extendsFrom(Constants.MINECRAFT_NAMED, Constants.MINECRAFT_DEPENDENCIES);
|
||||
extendsFrom(Constants.Configurations.MINECRAFT_NAMED, Constants.Configurations.MINECRAFT_DEPENDENCIES);
|
||||
|
||||
extendsFrom("compile", Constants.MAPPINGS_FINAL);
|
||||
extendsFrom("compile", Constants.Configurations.MAPPINGS_FINAL);
|
||||
|
||||
configureIDEs();
|
||||
configureCompile();
|
||||
@@ -342,17 +344,15 @@ public class AbstractPlugin implements Plugin<Project> {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
for (Project subProject : rootProject.getAllprojects()) {
|
||||
subProject.getTasks().getByName("build").dependsOn(parentTask);
|
||||
subProject.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName("remapAllJars"));
|
||||
rootProject.getTasks().getByName("remapAllJars").dependsOn(subProject.getTasks().getByName("remapJar"));
|
||||
}
|
||||
} else {
|
||||
parentTask = rootProject.getTasks().getByName("remapAllSources");
|
||||
remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper;
|
||||
|
||||
remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName("remapJar")).jarRemapper;
|
||||
|
||||
project1.getTasks().getByName("build").dependsOn(parentTask);
|
||||
project1.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName("remapAllJars"));
|
||||
rootProject.getTasks().getByName("remapAllJars").dependsOn(project1.getTasks().getByName("remapJar"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,16 +378,20 @@ public class AbstractPlugin implements Plugin<Project> {
|
||||
extension.getUnmappedModCollection().from(jarTask);
|
||||
}
|
||||
|
||||
project.getLogger().lifecycle("Configuring compiler arguments for Java");
|
||||
// Disable some things used by log4j via the mixin AP that prevent it from being garbage collected
|
||||
System.setProperty("log4j2.disable.jmx", "true");
|
||||
System.setProperty("log4j.shutdownHookEnabled", "false");
|
||||
|
||||
project.getLogger().info("Configuring compiler arguments for Java");
|
||||
new JavaApInvoker(project).configureMixin();
|
||||
|
||||
if (project.getPluginManager().hasPlugin("scala")) {
|
||||
project.getLogger().lifecycle("Configuring compiler arguments for Scala");
|
||||
project.getLogger().info("Configuring compiler arguments for Scala");
|
||||
new ScalaApInvoker(project).configureMixin();
|
||||
}
|
||||
|
||||
if (project.getPluginManager().hasPlugin("org.jetbrains.kotlin.kapt")) {
|
||||
project.getLogger().lifecycle("Configuring compiler arguments for Kapt plugin");
|
||||
project.getLogger().info("Configuring compiler arguments for Kapt plugin");
|
||||
new KaptApInvoker(project).configureMixin();
|
||||
}
|
||||
});
|
||||
@@ -437,6 +441,20 @@ public class AbstractPlugin implements Plugin<Project> {
|
||||
depNode.appendNode("artifactId", dependency.getName());
|
||||
depNode.appendNode("version", dependency.getVersion());
|
||||
depNode.appendNode("scope", entry.getMavenScope());
|
||||
|
||||
if (dependency instanceof ModuleDependency) {
|
||||
final Set<ExcludeRule> exclusions = ((ModuleDependency) dependency).getExcludeRules();
|
||||
|
||||
if (!exclusions.isEmpty()) {
|
||||
Node exclusionsNode = depNode.appendNode("exclusions");
|
||||
|
||||
for (ExcludeRule rule : exclusions) {
|
||||
Node exclusionNode = exclusionsNode.appendNode("exclusion");
|
||||
exclusionNode.appendNode("groupId", rule.getGroup() == null ? "*" : rule.getGroup());
|
||||
exclusionNode.appendNode("artifactId", rule.getModule() == null ? "*" : rule.getModule());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -28,8 +28,11 @@ import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
@@ -47,6 +50,7 @@ import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.plugins.BasePluginConvention;
|
||||
|
||||
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
|
||||
import net.fabricmc.loom.processors.JarProcessor;
|
||||
import net.fabricmc.loom.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.providers.ForgeUniversalProvider;
|
||||
import net.fabricmc.loom.providers.ForgeUserdevProvider;
|
||||
@@ -57,6 +61,7 @@ import net.fabricmc.loom.providers.PatchProvider;
|
||||
import net.fabricmc.loom.providers.McpConfigProvider;
|
||||
import net.fabricmc.loom.util.function.LazyBool;
|
||||
import net.fabricmc.loom.util.LoomDependencyManager;
|
||||
import net.fabricmc.loom.util.mappings.MojangMappingsDependency;
|
||||
|
||||
public class LoomGradleExtension {
|
||||
private static final String FORGE_PROPERTY = "loom.forge";
|
||||
@@ -74,6 +79,7 @@ public class LoomGradleExtension {
|
||||
private final ConfigurableFileCollection unmappedMods;
|
||||
|
||||
final List<LoomDecompiler> decompilers = new ArrayList<>();
|
||||
private final List<JarProcessor> jarProcessors = new ArrayList<>();
|
||||
|
||||
// Not to be set in the build.gradle
|
||||
private final Project project;
|
||||
@@ -83,6 +89,7 @@ public class LoomGradleExtension {
|
||||
private MappingSet[] srcMappingCache = new MappingSet[2];
|
||||
private Mercury[] srcMercuryCache = new Mercury[2];
|
||||
private final LazyBool forge;
|
||||
private Set<File> mixinMappings = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
/**
|
||||
* Loom will generate a new genSources task (with a new name, based off of {@link LoomDecompiler#name()})
|
||||
@@ -92,6 +99,17 @@ public class LoomGradleExtension {
|
||||
decompilers.add(decompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a transformation over the mapped mc jar.
|
||||
* Adding any jar processor will cause mapped mc jars to be stored per-project so that
|
||||
* different transformation can be applied in different projects.
|
||||
* This means remapping will need to be done individually per-project, which is slower when developing
|
||||
* more than one project using the same minecraft version.
|
||||
*/
|
||||
public void addJarProcessor(JarProcessor processor) {
|
||||
jarProcessors.add(processor);
|
||||
}
|
||||
|
||||
public MappingSet getOrCreateSrcMappingCache(int id, Supplier<MappingSet> factory) {
|
||||
return srcMappingCache[id] != null ? srcMappingCache[id] : (srcMappingCache[id] = factory.get());
|
||||
}
|
||||
@@ -100,6 +118,10 @@ public class LoomGradleExtension {
|
||||
return srcMercuryCache[id] != null ? srcMercuryCache[id] : (srcMercuryCache[id] = factory.get());
|
||||
}
|
||||
|
||||
public Dependency officialMojangMappings() {
|
||||
return new MojangMappingsDependency(project, this);
|
||||
}
|
||||
|
||||
public LoomGradleExtension(Project project) {
|
||||
this.project = project;
|
||||
this.autoGenIDERuns = AbstractPlugin.isRootProject(project);
|
||||
@@ -363,6 +385,10 @@ public class LoomGradleExtension {
|
||||
this.jarProcessorManager = jarProcessorManager;
|
||||
}
|
||||
|
||||
public List<JarProcessor> getJarProcessors() {
|
||||
return jarProcessors;
|
||||
}
|
||||
|
||||
public String getRefmapName() {
|
||||
if (refmapName == null || refmapName.isEmpty()) {
|
||||
String defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json";
|
||||
@@ -406,4 +432,16 @@ public class LoomGradleExtension {
|
||||
public boolean isForge() {
|
||||
return forge.getAsBoolean();
|
||||
}
|
||||
|
||||
// Creates a new file each time its called, this is then held onto later when remapping the output jar
|
||||
// Required as now when using parallel builds the old single file could be written by another sourceset compile task
|
||||
public synchronized File getNextMixinMappings() {
|
||||
File mixinMapping = new File(getProjectBuildCache(), "mixin-map-" + getMinecraftProvider().getMinecraftVersion() + "-" + getMappingsProvider().mappingsVersion + "." + mixinMappings.size() + ".tiny");
|
||||
mixinMappings.add(mixinMapping);
|
||||
return mixinMapping;
|
||||
}
|
||||
|
||||
public Set<File> getAllMixinMappings() {
|
||||
return Collections.unmodifiableSet(mixinMappings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,16 @@ package net.fabricmc.loom.processors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public interface JarProcessor {
|
||||
void setup(Project project);
|
||||
void setup();
|
||||
|
||||
/**
|
||||
* Currently this is a destructive process that replaces the existing jar.
|
||||
*/
|
||||
void process(File file);
|
||||
|
||||
/**
|
||||
* Return true to make all jar processors run again, return false to use the existing results of jar processing.
|
||||
*/
|
||||
boolean isInvalid(File file);
|
||||
}
|
||||
|
||||
@@ -25,37 +25,17 @@
|
||||
package net.fabricmc.loom.processors;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
|
||||
import net.fabricmc.loom.util.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
|
||||
public class JarProcessorManager {
|
||||
private final Project project;
|
||||
private final LoomGradleExtension extension;
|
||||
|
||||
private final List<JarProcessor> jarProcessors;
|
||||
|
||||
public JarProcessorManager(Project project) {
|
||||
this.project = project;
|
||||
this.extension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
jarProcessors = setupProcessors();
|
||||
public JarProcessorManager(List<JarProcessor> jarProcessors) {
|
||||
this.jarProcessors = jarProcessors;
|
||||
}
|
||||
|
||||
//TODO possibly expand via an API?
|
||||
private List<JarProcessor> setupProcessors() {
|
||||
List<JarProcessor> jarProcessors = new ArrayList<>();
|
||||
|
||||
if (extension.accessWidener != null) {
|
||||
jarProcessors.add(new AccessWidenerJarProcessor());
|
||||
}
|
||||
|
||||
jarProcessors.forEach(jarProcessor -> jarProcessor.setup(project));
|
||||
return Collections.unmodifiableList(jarProcessors);
|
||||
public void setupProcessors() {
|
||||
jarProcessors.forEach(JarProcessor::setup);
|
||||
}
|
||||
|
||||
public boolean active() {
|
||||
|
||||
@@ -65,7 +65,7 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider {
|
||||
|
||||
getProject().getRepositories().flatDir(repository -> repository.dir(getJarDirectory(getExtension().getProjectPersistentCache(), PROJECT_MAPPED_CLASSIFIER)));
|
||||
|
||||
getProject().getDependencies().add(Constants.MINECRAFT_NAMED,
|
||||
getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED,
|
||||
getProject().getDependencies().module("net.minecraft:minecraft:" + getJarVersionString(PROJECT_MAPPED_CLASSIFIER)));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,14 +36,19 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DependencyProvider;
|
||||
import net.fabricmc.loom.util.RemappedConfigurationEntry;
|
||||
|
||||
public class LaunchProvider extends DependencyProvider {
|
||||
public Dependency annotationDependency;
|
||||
|
||||
public LaunchProvider(Project project) {
|
||||
super(project);
|
||||
}
|
||||
@@ -52,6 +57,7 @@ public class LaunchProvider extends DependencyProvider {
|
||||
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws IOException {
|
||||
final LaunchConfig launchConfig = new LaunchConfig()
|
||||
.property("fabric.development", "true")
|
||||
.property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath())
|
||||
.property("log4j.configurationFile", getLog4jConfigFile().getAbsolutePath())
|
||||
|
||||
.property("client", "java.library.path", getExtension().getNativesDirectory().getAbsolutePath())
|
||||
@@ -72,15 +78,21 @@ public class LaunchProvider extends DependencyProvider {
|
||||
writeLog4jConfig();
|
||||
FileUtils.writeStringToFile(getExtension().getDevLauncherConfig(), launchConfig.asString(), StandardCharsets.UTF_8);
|
||||
|
||||
addDependency("net.fabricmc:dev-launch-injector:" + Constants.DEV_LAUNCH_INJECTOR_VERSION, "runtimeOnly");
|
||||
addDependency("net.minecrell:terminalconsoleappender:" + Constants.TERMINAL_CONSOLE_APPENDER_VERSION, "runtimeOnly");
|
||||
addDependency("org.jetbrains:annotations:" + Constants.JETBRAINS_ANNOTATIONS_VERSION, "compileOnly");
|
||||
addDependency(Constants.Dependencies.DEV_LAUNCH_INJECTOR + Constants.Dependencies.Versions.DEV_LAUNCH_INJECTOR, "runtimeOnly");
|
||||
addDependency(Constants.Dependencies.TERMINAL_CONSOLE_APPENDER + Constants.Dependencies.Versions.TERMINAL_CONSOLE_APPENDER, "runtimeOnly");
|
||||
annotationDependency = addDependency(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS, "compileOnly");
|
||||
|
||||
postPopulationScheduler.accept(this::writeRemapClassPath);
|
||||
}
|
||||
|
||||
private File getLog4jConfigFile() {
|
||||
return new File(getExtension().getDevLauncherConfig().getParentFile(), "log4j.xml");
|
||||
}
|
||||
|
||||
private File getRemapClasspathFile() {
|
||||
return new File(getExtension().getDevLauncherConfig().getParentFile(), "remapClasspath.txt");
|
||||
}
|
||||
|
||||
private void writeLog4jConfig() {
|
||||
try (InputStream is = LaunchProvider.class.getClassLoader().getResourceAsStream("log4j2.fabric.xml")) {
|
||||
Files.deleteIfExists(getLog4jConfigFile().toPath());
|
||||
@@ -90,9 +102,33 @@ public class LaunchProvider extends DependencyProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRemapClassPath() {
|
||||
List<String> inputConfigurations = new ArrayList<>();
|
||||
inputConfigurations.add(Constants.Configurations.MINECRAFT_DEPENDENCIES);
|
||||
inputConfigurations.addAll(Constants.MOD_COMPILE_ENTRIES.stream().map(RemappedConfigurationEntry::getSourceConfiguration).collect(Collectors.toList()));
|
||||
|
||||
List<File> remapClasspath = new ArrayList<>();
|
||||
|
||||
for (String inputConfiguration : inputConfigurations) {
|
||||
remapClasspath.addAll(getProject().getConfigurations().getByName(inputConfiguration).getFiles());
|
||||
}
|
||||
|
||||
remapClasspath.add(getExtension().getMinecraftMappedProvider().getIntermediaryJar());
|
||||
|
||||
String str = remapClasspath.stream()
|
||||
.map(File::getAbsolutePath)
|
||||
.collect(Collectors.joining(File.pathSeparator));
|
||||
|
||||
try {
|
||||
Files.write(getRemapClasspathFile().toPath(), str.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to generate remap classpath", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.MINECRAFT_NAMED;
|
||||
return Constants.Configurations.MINECRAFT_NAMED;
|
||||
}
|
||||
|
||||
public static class LaunchConfig {
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.net.UrlEscapers;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.tools.ant.util.StringUtils;
|
||||
@@ -44,19 +45,21 @@ import org.zeroturnaround.zip.FileSource;
|
||||
import org.zeroturnaround.zip.ZipEntrySource;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.processors.MinecraftProcessedProvider;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
import net.fabricmc.loom.util.DependencyProvider;
|
||||
import net.fabricmc.loom.util.DownloadUtil;
|
||||
import net.fabricmc.loom.util.srg.SrgMerger;
|
||||
import net.fabricmc.loom.util.accesswidener.AccessWidenerJarProcessor;
|
||||
import net.fabricmc.mapping.reader.v2.TinyV2Factory;
|
||||
import net.fabricmc.mapping.tree.TinyTree;
|
||||
import net.fabricmc.stitch.Command;
|
||||
import net.fabricmc.stitch.commands.CommandProposeFieldNames;
|
||||
import net.fabricmc.stitch.commands.tinyv2.CommandMergeTinyV2;
|
||||
import net.fabricmc.stitch.commands.tinyv2.CommandReorderTinyV2;
|
||||
import net.fabricmc.loom.processors.JarProcessorManager;
|
||||
import net.fabricmc.loom.processors.MinecraftProcessedProvider;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
|
||||
public class MappingsProvider extends DependencyProvider {
|
||||
public MinecraftMappedProvider mappedProvider;
|
||||
@@ -65,8 +68,10 @@ public class MappingsProvider extends DependencyProvider {
|
||||
public String minecraftVersion;
|
||||
public String mappingsVersion;
|
||||
|
||||
private Path mappingsDir;
|
||||
private Path mappingsStepsDir;
|
||||
private final Path mappingsDir;
|
||||
private final Path mappingsStepsDir;
|
||||
private Path intermediaryTiny;
|
||||
private boolean hasRefreshed = false;
|
||||
// The mappings that gradle gives us
|
||||
private Path baseTinyMappings;
|
||||
// The mappings we use in practice
|
||||
@@ -77,6 +82,8 @@ public class MappingsProvider extends DependencyProvider {
|
||||
|
||||
public MappingsProvider(Project project) {
|
||||
super(project);
|
||||
mappingsDir = getExtension().getUserCache().toPath().resolve("mappings");
|
||||
mappingsStepsDir = mappingsDir.resolve("steps");
|
||||
}
|
||||
|
||||
public void clean() throws IOException {
|
||||
@@ -153,10 +160,17 @@ public class MappingsProvider extends DependencyProvider {
|
||||
SrgMerger.mergeSrg(getExtension().getMcpConfigProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg);
|
||||
}
|
||||
|
||||
addDependency(tinyMappingsJar, Constants.MAPPINGS_FINAL);
|
||||
addDependency(tinyMappingsJar, Constants.Configurations.MAPPINGS_FINAL);
|
||||
|
||||
JarProcessorManager processorManager = new JarProcessorManager(getProject());
|
||||
getExtension().setJarProcessorManager(processorManager);
|
||||
LoomGradleExtension extension = getExtension();
|
||||
|
||||
if (extension.accessWidener != null) {
|
||||
extension.addJarProcessor(new AccessWidenerJarProcessor(getProject()));
|
||||
}
|
||||
|
||||
JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors());
|
||||
extension.setJarProcessorManager(processorManager);
|
||||
processorManager.setupProcessors();
|
||||
|
||||
if (processorManager.active()) {
|
||||
mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager);
|
||||
@@ -178,14 +192,7 @@ public class MappingsProvider extends DependencyProvider {
|
||||
|
||||
if (baseMappingsAreV2()) {
|
||||
// These are unmerged v2 mappings
|
||||
|
||||
// Download and extract intermediary
|
||||
String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftVersion);
|
||||
String intermediaryArtifactUrl = getExtension().getIntermediaryUrl().apply(encodedMinecraftVersion);
|
||||
Path intermediaryJar = mappingsStepsDir.resolve("v2-intermediary-" + minecraftVersion + ".jar");
|
||||
DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar.toFile(), project.getLogger());
|
||||
|
||||
mergeAndSaveMappings(project, intermediaryJar, yarnJar);
|
||||
mergeAndSaveMappings(project, yarnJar);
|
||||
} else {
|
||||
// These are merged v1 mappings
|
||||
if (tinyMappings.exists()) {
|
||||
@@ -222,14 +229,15 @@ public class MappingsProvider extends DependencyProvider {
|
||||
Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private void mergeAndSaveMappings(Project project, Path unmergedIntermediaryJar, Path unmergedYarnJar) throws IOException {
|
||||
Path unmergedIntermediary = Paths.get(mappingsStepsDir.toString(), "unmerged-intermediary.tiny");
|
||||
project.getLogger().info(":extracting " + unmergedIntermediaryJar.getFileName());
|
||||
private void extractIntermediary(Path intermediaryJar, Path intermediaryTiny) throws IOException {
|
||||
getProject().getLogger().info(":extracting " + intermediaryJar.getFileName());
|
||||
|
||||
try (FileSystem unmergedIntermediaryFs = FileSystems.newFileSystem(unmergedIntermediaryJar, (ClassLoader) null)) {
|
||||
extractMappings(unmergedIntermediaryFs, unmergedIntermediary);
|
||||
try (FileSystem unmergedIntermediaryFs = FileSystems.newFileSystem(intermediaryJar, (ClassLoader) null)) {
|
||||
extractMappings(unmergedIntermediaryFs, intermediaryTiny);
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeAndSaveMappings(Project project, Path unmergedYarnJar) throws IOException {
|
||||
Path unmergedYarn = Paths.get(mappingsStepsDir.toString(), "unmerged-yarn.tiny");
|
||||
project.getLogger().info(":extracting " + unmergedYarnJar.getFileName());
|
||||
|
||||
@@ -238,7 +246,7 @@ public class MappingsProvider extends DependencyProvider {
|
||||
}
|
||||
|
||||
Path invertedIntermediary = Paths.get(mappingsStepsDir.toString(), "inverted-intermediary.tiny");
|
||||
reorderMappings(unmergedIntermediary, invertedIntermediary, "intermediary", "official");
|
||||
reorderMappings(getIntermediaryTiny(), invertedIntermediary, "intermediary", "official");
|
||||
Path unorderedMergedMappings = Paths.get(mappingsStepsDir.toString(), "unordered-merged.tiny");
|
||||
project.getLogger().info(":merging");
|
||||
mergeMappings(invertedIntermediary, unmergedYarn, unorderedMergedMappings);
|
||||
@@ -283,11 +291,7 @@ public class MappingsProvider extends DependencyProvider {
|
||||
}
|
||||
|
||||
private void initFiles() {
|
||||
mappingsDir = getExtension().getUserCache().toPath().resolve("mappings");
|
||||
mappingsStepsDir = mappingsDir.resolve("steps");
|
||||
|
||||
baseTinyMappings = mappingsDir.resolve(mappingsName + "-tiny-" + minecraftVersion + "-" + mappingsVersion + "-base");
|
||||
mappingsMixinExport = new File(getExtension().getProjectBuildCache(), "mixin-map-" + minecraftVersion + "-" + mappingsVersion + ".tiny");
|
||||
}
|
||||
|
||||
public void cleanFiles() {
|
||||
@@ -300,8 +304,6 @@ public class MappingsProvider extends DependencyProvider {
|
||||
Files.deleteIfExists(baseTinyMappings);
|
||||
}
|
||||
|
||||
mappingsMixinExport.delete();
|
||||
|
||||
if (tinyMappings != null) {
|
||||
tinyMappings.delete();
|
||||
}
|
||||
@@ -316,6 +318,33 @@ public class MappingsProvider extends DependencyProvider {
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.MAPPINGS;
|
||||
return Constants.Configurations.MAPPINGS;
|
||||
}
|
||||
|
||||
public Path getMappingsDir() {
|
||||
return mappingsDir;
|
||||
}
|
||||
|
||||
public Path getIntermediaryTiny() throws IOException {
|
||||
if (intermediaryTiny == null) {
|
||||
minecraftVersion = getExtension().getMinecraftProvider().getMinecraftVersion();
|
||||
Preconditions.checkNotNull(minecraftVersion, "Minecraft version cannot be null");
|
||||
|
||||
intermediaryTiny = mappingsDir.resolve(String.format("intermediary-%s-v2.tiny", minecraftVersion));
|
||||
|
||||
if (!Files.exists(intermediaryTiny) || (isRefreshDeps() && !hasRefreshed)) {
|
||||
hasRefreshed = true;
|
||||
|
||||
// Download and extract intermediary
|
||||
String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftVersion);
|
||||
String intermediaryArtifactUrl = getExtension().getIntermediaryUrl().apply(encodedMinecraftVersion);
|
||||
Path intermediaryJar = mappingsDir.resolve("v2-intermediary-" + minecraftVersion + ".jar");
|
||||
DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar.toFile(), getProject().getLogger());
|
||||
|
||||
extractIntermediary(intermediaryJar, intermediaryTiny);
|
||||
}
|
||||
}
|
||||
|
||||
return intermediaryTiny;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class MinecraftLibraryProvider {
|
||||
isClientOnly = true;
|
||||
} */
|
||||
|
||||
project.getDependencies().add(Constants.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getArtifactName()));
|
||||
project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.getArtifactName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
|
||||
protected void addDependencies(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) {
|
||||
getProject().getRepositories().flatDir(repository -> repository.dir(getJarDirectory(getExtension().getUserCache(), "mapped")));
|
||||
|
||||
getProject().getDependencies().add(Constants.MINECRAFT_NAMED,
|
||||
getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED,
|
||||
getProject().getDependencies().module("net.minecraft:minecraft:" + getJarVersionString("mapped")));
|
||||
}
|
||||
|
||||
@@ -173,6 +173,6 @@ public class MinecraftMappedProvider extends DependencyProvider {
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.MINECRAFT_NAMED;
|
||||
return Constants.Configurations.MINECRAFT_NAMED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ public class MinecraftProvider extends DependencyProvider {
|
||||
}
|
||||
} else {
|
||||
getProject().getLogger().debug("Downloading version manifests");
|
||||
DownloadUtil.downloadIfChanged(new URL("https://launchermeta.mojang.com/mc/game/version_manifest.json"), manifests, getProject().getLogger());
|
||||
DownloadUtil.downloadIfChanged(new URL(Constants.VERSION_MANIFESTS), manifests, getProject().getLogger());
|
||||
}
|
||||
|
||||
String versionManifest = Files.asCharSource(manifests, StandardCharsets.UTF_8).read();
|
||||
@@ -444,6 +444,6 @@ public class MinecraftProvider extends DependencyProvider {
|
||||
|
||||
@Override
|
||||
public String getTargetConfig() {
|
||||
return Constants.MINECRAFT;
|
||||
return Constants.Configurations.MINECRAFT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.gradle.api.GradleException;
|
||||
import org.gradle.api.IllegalDependencyNotation;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.options.Option;
|
||||
@@ -51,6 +52,7 @@ import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.providers.MappingsProvider;
|
||||
import net.fabricmc.loom.providers.MinecraftMappedProvider;
|
||||
import net.fabricmc.loom.util.SourceRemapper;
|
||||
import net.fabricmc.loom.util.mappings.MojangMappingsDependency;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsJoiner;
|
||||
import net.fabricmc.mapping.tree.TinyMappingFactory;
|
||||
import net.fabricmc.mapping.tree.TinyTree;
|
||||
@@ -116,7 +118,16 @@ public class MigrateMappingsTask extends AbstractLoomTask {
|
||||
Set<File> files;
|
||||
|
||||
try {
|
||||
files = project.getConfigurations().detachedConfiguration(project.getDependencies().create(mappings)).resolve();
|
||||
if (mappings.startsWith(MojangMappingsDependency.GROUP + ':' + MojangMappingsDependency.MODULE + ':') || mappings.startsWith("net.mojang.minecraft:mappings:")) {
|
||||
if (!mappings.endsWith(":" + project.getExtensions().getByType(LoomGradleExtension.class).getMinecraftProvider().getMinecraftVersion())) {
|
||||
throw new UnsupportedOperationException("Migrating Mojang mappings is currently only supported for the specified minecraft version");
|
||||
}
|
||||
|
||||
files = new MojangMappingsDependency(project, getExtension()).resolve();
|
||||
} else {
|
||||
Dependency dependency = project.getDependencies().create(mappings);
|
||||
files = project.getConfigurations().detachedConfiguration(dependency).resolve();
|
||||
}
|
||||
} catch (IllegalDependencyNotation ignored) {
|
||||
project.getLogger().info("Could not locate mappings, presuming V2 Yarn");
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
@@ -104,17 +105,16 @@ public class RemapJarTask extends Jar {
|
||||
);
|
||||
Path[] classpath = classpathFiles.stream().map(File::toPath).filter((p) -> !input.equals(p) && Files.exists(p)).toArray(Path[]::new);
|
||||
|
||||
File mixinMapFile = mappingsProvider.mappingsMixinExport;
|
||||
Path mixinMapPath = mixinMapFile.toPath();
|
||||
|
||||
TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper();
|
||||
|
||||
remapperBuilder = remapperBuilder.withMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false));
|
||||
|
||||
// FIXME: The mixin map is named->intermediary, but I think we need named->srg?
|
||||
if (mixinMapFile.exists()) {
|
||||
for (File mixinMapFile : extension.getAllMixinMappings()) {
|
||||
if ("intermediary".equals(toM)) {
|
||||
remapperBuilder = remapperBuilder.withMappings(TinyUtils.createTinyMappingProvider(mixinMapPath, fromM, toM));
|
||||
if (mixinMapFile.exists()) {
|
||||
remapperBuilder = remapperBuilder.withMappings(TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, toM));
|
||||
}
|
||||
} else {
|
||||
project.getLogger().error("Mixins in Forge projects are currently not supported.");
|
||||
}
|
||||
@@ -204,14 +204,13 @@ public class RemapJarTask extends Jar {
|
||||
jarRemapper.addMappings(TinyRemapperMappingsHelper.create(extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings(), fromM, toM, false));
|
||||
}
|
||||
|
||||
File mixinMapFile = mappingsProvider.mappingsMixinExport;
|
||||
Path mixinMapPath = mixinMapFile.toPath();
|
||||
|
||||
if (mixinMapFile.exists()) {
|
||||
for (File mixinMapFile : extension.getAllMixinMappings()) {
|
||||
if ("intermediary".equals(toM)) {
|
||||
jarRemapper.addMappings(TinyUtils.createTinyMappingProvider(mixinMapPath, fromM, toM));
|
||||
} else {
|
||||
project.getLogger().error("Mixins in Forge projects are currently not supported.");
|
||||
if (mixinMapFile.exists()) {
|
||||
jarRemapper.addMappings(TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, toM));
|
||||
} else {
|
||||
project.getLogger().error("Mixins in Forge projects are currently not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +226,10 @@ public class RemapJarTask extends Jar {
|
||||
throw new RuntimeException("Failed to remap access widener");
|
||||
}
|
||||
|
||||
return Pair.of(accessWidenerJarProcessor.getAccessWidenerPath(remapData.output), data);
|
||||
String awPath = accessWidenerJarProcessor.getAccessWidenerPath(remapData.input);
|
||||
Preconditions.checkNotNull(awPath, "Failed to find accessWidener in fabric.mod.json: " + remapData.input);
|
||||
|
||||
return Pair.of(awPath, data);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -248,7 +250,8 @@ public class RemapJarTask extends Jar {
|
||||
}
|
||||
|
||||
if (accessWidener != null) {
|
||||
ZipUtil.replaceEntry(data.output.toFile(), accessWidener.getLeft(), accessWidener.getRight());
|
||||
boolean replaced = ZipUtil.replaceEntry(data.output.toFile(), accessWidener.getLeft(), accessWidener.getRight());
|
||||
Preconditions.checkArgument(replaced, "Failed to remap access widener");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,18 +27,17 @@ package net.fabricmc.loom.util;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class Constants {
|
||||
public static final String DEFAULT_FABRIC_CLIENT_TWEAKER = "net.fabricmc.loader.launch.FabricClientTweaker";
|
||||
public static final String DEFAULT_FABRIC_SERVER_TWEAKER = "net.fabricmc.loader.launch.FabricServerTweaker";
|
||||
|
||||
public static final String LIBRARIES_BASE = "https://libraries.minecraft.net/";
|
||||
public static final String RESOURCES_BASE = "http://resources.download.minecraft.net/";
|
||||
public static final String VERSION_MANIFESTS = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||
|
||||
public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32";
|
||||
|
||||
public static final String MOD_COMPILE_CLASSPATH = "modCompileClasspath";
|
||||
public static final String MOD_COMPILE_CLASSPATH_MAPPED = "modCompileClasspathMapped";
|
||||
public static final int ASM_VERSION = Opcodes.ASM9;
|
||||
|
||||
public static final List<RemappedConfigurationEntry> MOD_COMPILE_ENTRIES = ImmutableList.of(
|
||||
new RemappedConfigurationEntry("modCompile", "compile", true, "compile"),
|
||||
new RemappedConfigurationEntry("modApi", "api", true, "compile"),
|
||||
@@ -47,21 +46,73 @@ public class Constants {
|
||||
new RemappedConfigurationEntry("modCompileOnly", "compileOnly", true, "")
|
||||
);
|
||||
|
||||
public static final String INCLUDE = "include";
|
||||
public static final String MINECRAFT = "minecraft";
|
||||
public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries";
|
||||
public static final String MINECRAFT_NAMED = "minecraftNamed";
|
||||
public static final String MAPPINGS = "mappings";
|
||||
public static final String MAPPINGS_FINAL = "mappingsFinal";
|
||||
public static final String MCP_CONFIG = "mcp";
|
||||
public static final String FORGE = "forge";
|
||||
public static final String FORGE_USERDEV = "forgeUserdev";
|
||||
public static final String FORGE_INSTALLER = "forgeInstaller";
|
||||
public static final String FORGE_UNIVERSAL = "forgeUniversal";
|
||||
public static final String FORGE_DEPENDENCIES = "forgeDependencies";
|
||||
private Constants() {
|
||||
}
|
||||
|
||||
public static final String MIXIN_COMPILE_EXTENSIONS_VERSION = "0.3.1.5";
|
||||
public static final String DEV_LAUNCH_INJECTOR_VERSION = "0.2.1+build.8";
|
||||
public static final String TERMINAL_CONSOLE_APPENDER_VERSION = "1.2.0";
|
||||
public static final String JETBRAINS_ANNOTATIONS_VERSION = "19.0.0";
|
||||
/**
|
||||
* Constants related to configurations.
|
||||
*/
|
||||
public static final class Configurations {
|
||||
public static final String MOD_COMPILE_CLASSPATH = "modCompileClasspath";
|
||||
public static final String MOD_COMPILE_CLASSPATH_MAPPED = "modCompileClasspathMapped";
|
||||
public static final String INCLUDE = "include";
|
||||
public static final String MINECRAFT = "minecraft";
|
||||
public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries";
|
||||
public static final String MINECRAFT_NAMED = "minecraftNamed";
|
||||
public static final String MAPPINGS = "mappings";
|
||||
public static final String MAPPINGS_FINAL = "mappingsFinal";
|
||||
public static final String MCP_CONFIG = "mcp";
|
||||
public static final String FORGE = "forge";
|
||||
public static final String FORGE_USERDEV = "forgeUserdev";
|
||||
public static final String FORGE_INSTALLER = "forgeInstaller";
|
||||
public static final String FORGE_UNIVERSAL = "forgeUniversal";
|
||||
public static final String FORGE_DEPENDENCIES = "forgeDependencies";
|
||||
|
||||
private Configurations() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to dependencies.
|
||||
*/
|
||||
public static final class Dependencies {
|
||||
public static final String MIXIN_COMPILE_EXTENSIONS = "net.fabricmc:fabric-mixin-compile-extensions:";
|
||||
public static final String DEV_LAUNCH_INJECTOR = "net.fabricmc:dev-launch-injector:";
|
||||
public static final String TERMINAL_CONSOLE_APPENDER = "net.minecrell:terminalconsoleappender:";
|
||||
public static final String JETBRAINS_ANNOTATIONS = "org.jetbrains:annotations:";
|
||||
|
||||
private Dependencies() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants for versions of dependencies.
|
||||
*/
|
||||
public static final class Versions {
|
||||
public static final String MIXIN_COMPILE_EXTENSIONS = "0.3.2.6";
|
||||
public static final String DEV_LAUNCH_INJECTOR = "0.2.1+build.8";
|
||||
public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0";
|
||||
public static final String JETBRAINS_ANNOTATIONS = "19.0.0";
|
||||
|
||||
private Versions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MixinArguments {
|
||||
public static final String IN_MAP_FILE_NAMED_INTERMEDIARY = "inMapFileNamedIntermediary";
|
||||
public static final String OUT_MAP_FILE_NAMED_INTERMEDIARY = "outMapFileNamedIntermediary";
|
||||
public static final String OUT_REFMAP_FILE = "outRefMapFile";
|
||||
public static final String DEFAULT_OBFUSCATION_ENV = "defaultObfuscationEnv";
|
||||
|
||||
private MixinArguments() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class LaunchWrapper {
|
||||
public static final String DEFAULT_FABRIC_CLIENT_TWEAKER = "net.fabricmc.loader.launch.FabricClientTweaker";
|
||||
public static final String DEFAULT_FABRIC_SERVER_TWEAKER = "net.fabricmc.loader.launch.FabricServerTweaker";
|
||||
|
||||
private LaunchWrapper() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ public abstract class DependencyProvider {
|
||||
addDependency(object, "compile");
|
||||
}
|
||||
|
||||
public void addDependency(Object object, String target) {
|
||||
public Dependency addDependency(Object object, String target) {
|
||||
if (object instanceof File) {
|
||||
object = project.files(object);
|
||||
}
|
||||
|
||||
project.getDependencies().add(target, object);
|
||||
return project.getDependencies().add(target, object);
|
||||
}
|
||||
|
||||
public void register(LoomDependencyManager dependencyManager) {
|
||||
@@ -133,6 +133,10 @@ public abstract class DependencyProvider {
|
||||
}
|
||||
|
||||
public Set<File> resolve() {
|
||||
if (dependency instanceof SelfResolvingDependency) {
|
||||
return ((SelfResolvingDependency) dependency).resolve();
|
||||
}
|
||||
|
||||
return sourceConfiguration.files(dependency);
|
||||
}
|
||||
|
||||
@@ -171,12 +175,14 @@ public abstract class DependencyProvider {
|
||||
|
||||
public static class FileDependencyInfo extends DependencyInfo {
|
||||
protected final Map<String, File> classifierToFile = new HashMap<>();
|
||||
protected final Set<File> resolvedFiles;
|
||||
protected final String group, name, version;
|
||||
|
||||
FileDependencyInfo(Project project, SelfResolvingDependency dependency, Configuration configuration) {
|
||||
super(project, dependency, configuration);
|
||||
|
||||
Set<File> files = dependency.resolve();
|
||||
this.resolvedFiles = files;
|
||||
switch (files.size()) {
|
||||
case 0: //Don't think Gradle would ever let you do this
|
||||
throw new IllegalStateException("Empty dependency?");
|
||||
@@ -259,5 +265,10 @@ public abstract class DependencyProvider {
|
||||
public String getResolvedDepString() {
|
||||
return getDepString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<File> resolve() {
|
||||
return this.resolvedFiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +92,6 @@ public class FabricApiExtension {
|
||||
throw new RuntimeException("Failed to find artifact or version");
|
||||
}
|
||||
|
||||
project.getLogger().lifecycle(artifact.getTextContent() + " : " + version.getTextContent());
|
||||
|
||||
versionMap.put(artifact.getTextContent(), version.getTextContent());
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import net.fabricmc.loom.util.progress.ProgressLogger;
|
||||
|
||||
@@ -124,7 +123,7 @@ public class LineNumberRemapper {
|
||||
ClassReader reader = new ClassReader(is);
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
|
||||
reader.accept(new LineNumberVisitor(Opcodes.ASM7, writer, lineMap.get(idx)), 0);
|
||||
reader.accept(new LineNumberVisitor(Constants.ASM_VERSION, writer, lineMap.get(idx)), 0);
|
||||
Files.write(dst, writer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class LoomDependencyManager {
|
||||
if (extension.getInstallerJson() == null) {
|
||||
//If we've not found the installer JSON we've probably skipped remapping Fabric loader, let's go looking
|
||||
project.getLogger().info("Searching through modCompileClasspath for installer JSON");
|
||||
final Configuration configuration = project.getConfigurations().getByName(Constants.MOD_COMPILE_CLASSPATH);
|
||||
final Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH);
|
||||
|
||||
for (File input : configuration.resolve()) {
|
||||
JsonObject jsonObject = ModProcessor.readInstallerJson(input, project);
|
||||
@@ -173,7 +173,7 @@ public class LoomDependencyManager {
|
||||
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
|
||||
JsonObject libraries = jsonObject.get("libraries").getAsJsonObject();
|
||||
Configuration mcDepsConfig = project.getConfigurations().getByName(Constants.MINECRAFT_DEPENDENCIES);
|
||||
Configuration mcDepsConfig = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES);
|
||||
Configuration apDepsConfig = project.getConfigurations().getByName("annotationProcessor");
|
||||
|
||||
libraries.get("common").getAsJsonArray().forEach(jsonElement -> {
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.ModuleDependency;
|
||||
import org.gradle.api.artifacts.ResolvedArtifact;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler;
|
||||
import org.gradle.api.artifacts.query.ArtifactResolutionQuery;
|
||||
import org.gradle.api.artifacts.result.ArtifactResult;
|
||||
@@ -66,25 +65,16 @@ public class ModCompileRemapper {
|
||||
List<ModDependencyInfo> modDependencies = new ArrayList<>();
|
||||
|
||||
for (ResolvedArtifact artifact : sourceConfig.getResolvedConfiguration().getResolvedArtifacts()) {
|
||||
String group;
|
||||
String name;
|
||||
String version;
|
||||
// TODO: This collection doesn't appear to include FileCollection dependencies
|
||||
// Might have to go based on the dependencies, rather than their resolved form?
|
||||
// File dependencies use SelfResolvingDependency, which appears to be handled differently
|
||||
String group = artifact.getModuleVersion().getId().getGroup();
|
||||
String name = artifact.getModuleVersion().getId().getName();
|
||||
String version = artifact.getModuleVersion().getId().getVersion();
|
||||
String classifierSuffix = artifact.getClassifier() == null ? "" : (":" + artifact.getClassifier());
|
||||
|
||||
if (artifact.getId().getComponentIdentifier() instanceof ModuleComponentIdentifier) {
|
||||
group = ((ModuleComponentIdentifier) artifact.getId().getComponentIdentifier()).getGroup();
|
||||
name = ((ModuleComponentIdentifier) artifact.getId().getComponentIdentifier()).getModule();
|
||||
version = ((ModuleComponentIdentifier) artifact.getId().getComponentIdentifier()).getVersion();
|
||||
} else {
|
||||
group = "net.fabricmc.synthetic";
|
||||
name = artifact.getId().getComponentIdentifier().getDisplayName().replace('.', '-').replace(" :", "-");
|
||||
version = "0.1.0";
|
||||
}
|
||||
|
||||
final String notation = group + ":" + name + ":" + version + classifierSuffix;
|
||||
|
||||
if (!isFabricMod(logger, artifact, notation)) {
|
||||
addToRegularCompile(project, regularConfig, notation);
|
||||
if (!isFabricMod(logger, artifact)) {
|
||||
addToRegularCompile(project, regularConfig, artifact);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -122,12 +112,12 @@ public class ModCompileRemapper {
|
||||
/**
|
||||
* Checks if an artifact is a fabric mod, according to the presence of a fabric.mod.json.
|
||||
*/
|
||||
private static boolean isFabricMod(Logger logger, ResolvedArtifact artifact, String notation) {
|
||||
private static boolean isFabricMod(Logger logger, ResolvedArtifact artifact) {
|
||||
File input = artifact.getFile();
|
||||
|
||||
try (ZipFile zipFile = new ZipFile(input)) {
|
||||
if (zipFile.getEntry("fabric.mod.json") != null) {
|
||||
logger.info("Found Fabric mod in modCompile: {}", notation);
|
||||
logger.info("Found Fabric mod in modCompile: {}", artifact.getId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,10 +127,11 @@ public class ModCompileRemapper {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addToRegularCompile(Project project, Configuration regularCompile, String notation) {
|
||||
project.getLogger().info(":providing " + notation);
|
||||
private static void addToRegularCompile(Project project, Configuration regularCompile, ResolvedArtifact artifact) {
|
||||
project.getLogger().info(":providing " + artifact);
|
||||
DependencyHandler dependencies = project.getDependencies();
|
||||
Dependency dep = dependencies.module(notation);
|
||||
Dependency dep = dependencies.module(artifact.getModuleVersion().toString()
|
||||
+ (artifact.getClassifier() == null ? "" : ':' + artifact.getClassifier())); // the owning module of the artifact
|
||||
|
||||
if (dep instanceof ModuleDependency) {
|
||||
((ModuleDependency) dep).setTransitive(false);
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -50,11 +51,13 @@ import org.zeroturnaround.zip.ZipUtil;
|
||||
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
|
||||
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
|
||||
|
||||
import net.fabricmc.accesswidener.AccessWidener;
|
||||
import net.fabricmc.accesswidener.AccessWidenerReader;
|
||||
import net.fabricmc.accesswidener.AccessWidenerRemapper;
|
||||
import net.fabricmc.accesswidener.AccessWidenerWriter;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.providers.MappingsProvider;
|
||||
import net.fabricmc.loom.providers.MinecraftMappedProvider;
|
||||
import net.fabricmc.loom.util.accesswidener.AccessWidener;
|
||||
import net.fabricmc.loom.util.accesswidener.AccessWidenerRemapper;
|
||||
import net.fabricmc.loom.processors.dependency.ModDependencyInfo;
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
import net.fabricmc.tinyremapper.InputTag;
|
||||
@@ -68,9 +71,15 @@ public class ModProcessor {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<ModDependencyInfo> remapList = new ArrayList<>();
|
||||
|
||||
for (ModDependencyInfo info : processList) {
|
||||
if (info.requiresRemapping() && info.getRemappedOutput().exists()) {
|
||||
info.getRemappedOutput().delete();
|
||||
if (info.requiresRemapping()) {
|
||||
if (info.getRemappedOutput().exists()) {
|
||||
info.getRemappedOutput().delete();
|
||||
}
|
||||
|
||||
remapList.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,12 +87,12 @@ public class ModProcessor {
|
||||
|
||||
for (ModDependencyInfo info : processList) {
|
||||
if (!info.getRemappedOutput().exists()) {
|
||||
throw new RuntimeException("Failed to remap mod" + info);
|
||||
throw new RuntimeException("Failed to find remapped mod" + info);
|
||||
}
|
||||
}
|
||||
|
||||
if (info.requiresRemapping()) {
|
||||
stripNestedJars(info.getRemappedOutput());
|
||||
}
|
||||
for (ModDependencyInfo info : remapList) {
|
||||
stripNestedJars(info.getRemappedOutput());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,13 +111,15 @@ public class ModProcessor {
|
||||
private static byte[] remapAccessWidener(byte[] input, Remapper remapper) {
|
||||
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(input), StandardCharsets.UTF_8))) {
|
||||
AccessWidener accessWidener = new AccessWidener();
|
||||
accessWidener.read(bufferedReader);
|
||||
AccessWidenerReader accessWidenerReader = new AccessWidenerReader(accessWidener);
|
||||
accessWidenerReader.read(bufferedReader);
|
||||
|
||||
AccessWidenerRemapper accessWidenerRemapper = new AccessWidenerRemapper(accessWidener, remapper, "named");
|
||||
AccessWidener remapped = accessWidenerRemapper.remap();
|
||||
AccessWidenerWriter accessWidenerWriter = new AccessWidenerWriter(remapped);
|
||||
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
remapped.write(writer);
|
||||
accessWidenerWriter.write(writer);
|
||||
return writer.toString().getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@@ -99,14 +100,20 @@ public class NestedJars {
|
||||
private static List<File> getContainedJars(Project project) {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
|
||||
ResolvedConfiguration configuration = project.getConfigurations().getByName(Constants.INCLUDE).getResolvedConfiguration();
|
||||
Set<ResolvedDependency> dependencies = configuration.getFirstLevelModuleDependencies();
|
||||
Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE);
|
||||
ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration();
|
||||
Set<ResolvedDependency> dependencies = resolvedConfiguration.getFirstLevelModuleDependencies();
|
||||
|
||||
for (ResolvedDependency dependency : dependencies) {
|
||||
// Bit ugly doing this, id guess there is a better way but this works.
|
||||
Set<String> projectDeps = new HashSet<>();
|
||||
|
||||
for (Dependency dependency : configuration.getDependencies()) {
|
||||
if (dependency instanceof ProjectDependency) {
|
||||
ProjectDependency projectDependency = (ProjectDependency) dependency;
|
||||
Project dependencyProject = projectDependency.getDependencyProject();
|
||||
|
||||
projectDeps.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion());
|
||||
|
||||
// TODO change this to allow just normal jar tasks, so a project can have a none loom sub project
|
||||
Collection<Task> remapJarTasks = dependencyProject.getTasksByName("remapJar", false);
|
||||
Collection<Task> jarTasks = dependencyProject.getTasksByName("jar", false);
|
||||
@@ -118,6 +125,12 @@ public class NestedJars {
|
||||
fileList.add(((AbstractArchiveTask) task).getArchivePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ResolvedDependency dependency : dependencies) {
|
||||
if (projectDeps.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) {
|
||||
continue;
|
||||
} else {
|
||||
fileList.addAll(prepareForNesting(
|
||||
dependency
|
||||
@@ -147,7 +160,7 @@ public class NestedJars {
|
||||
public static List<RemapJarTask> getRequiredTasks(Project project) {
|
||||
List<RemapJarTask> remapTasks = new ArrayList<>();
|
||||
|
||||
Configuration configuration = project.getConfigurations().getByName(Constants.INCLUDE);
|
||||
Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE);
|
||||
DependencySet dependencies = configuration.getDependencies();
|
||||
|
||||
for (Dependency dependency : dependencies) {
|
||||
@@ -211,6 +224,10 @@ public class NestedJars {
|
||||
jsonObject.addProperty("version", dependency.getModuleVersion());
|
||||
jsonObject.addProperty("name", dependency.getModuleName());
|
||||
|
||||
JsonObject custom = new JsonObject();
|
||||
custom.addProperty("fabric-loom:generated", true);
|
||||
jsonObject.add("custom", custom);
|
||||
|
||||
return GSON.toJson(jsonObject);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ public class RunConfig {
|
||||
|
||||
if ("launchwrapper".equals(extension.getLoaderLaunchMethod())) {
|
||||
runConfig.mainClass = "net.minecraft.launchwrapper.Launch";
|
||||
runConfig.programArgs = "--tweakClass " + ("client".equals(mode) ? Constants.DEFAULT_FABRIC_CLIENT_TWEAKER : Constants.DEFAULT_FABRIC_SERVER_TWEAKER);
|
||||
runConfig.programArgs = "--tweakClass " + ("client".equals(mode) ? Constants.LaunchWrapper.DEFAULT_FABRIC_CLIENT_TWEAKER : Constants.LaunchWrapper.DEFAULT_FABRIC_SERVER_TWEAKER);
|
||||
} else {
|
||||
runConfig.mainClass = "net.fabricmc.devlaunchinjector.Main";
|
||||
runConfig.programArgs = "";
|
||||
|
||||
@@ -30,16 +30,19 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
import org.cadixdev.mercury.Mercury;
|
||||
import org.cadixdev.mercury.remapper.MercuryRemapper;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.providers.MappingsProvider;
|
||||
import net.fabricmc.loom.providers.LaunchProvider;
|
||||
import net.fabricmc.loom.util.progress.ProgressLogger;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsReader;
|
||||
import net.fabricmc.mapping.tree.TinyTree;
|
||||
@@ -176,6 +179,14 @@ public class SourceRemapper {
|
||||
m.getClassPath().add(extension.getMinecraftMappedProvider().getMappedJar().toPath());
|
||||
m.getClassPath().add(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath());
|
||||
|
||||
Dependency annotationDependency = extension.getDependencyManager().getProvider(LaunchProvider.class).annotationDependency;
|
||||
Set<File> files = project.getConfigurations().getByName("compileOnly")
|
||||
.files(annotationDependency);
|
||||
|
||||
for (File file : files) {
|
||||
m.getClassPath().add(file.toPath());
|
||||
}
|
||||
|
||||
m.getProcessors().add(MercuryRemapper.create(mappings));
|
||||
|
||||
return m;
|
||||
@@ -201,8 +212,9 @@ public class SourceRemapper {
|
||||
|
||||
public static Mercury createMercuryWithClassPath(Project project, boolean toNamed) {
|
||||
Mercury m = new Mercury();
|
||||
m.setGracefulClasspathChecks(true);
|
||||
|
||||
for (File file : project.getConfigurations().getByName(Constants.MINECRAFT_DEPENDENCIES).getFiles()) {
|
||||
for (File file : project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles()) {
|
||||
m.getClassPath().add(file.toPath());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,477 +0,0 @@
|
||||
/*
|
||||
* 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.accesswidener;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import net.fabricmc.mappings.EntryTriple;
|
||||
|
||||
public class AccessWidener {
|
||||
public String namespace;
|
||||
public Map<String, Access> classAccess = new HashMap<>();
|
||||
public Map<EntryTriple, Access> methodAccess = new HashMap<>();
|
||||
public Map<EntryTriple, Access> fieldAccess = new HashMap<>();
|
||||
private final Set<String> classes = new LinkedHashSet<>();
|
||||
|
||||
public void read(BufferedReader reader) throws IOException {
|
||||
String headerStr = reader.readLine();
|
||||
|
||||
if (headerStr == null) {
|
||||
throw new RuntimeException("Cannot read empty or invalid access widener");
|
||||
}
|
||||
|
||||
String[] header = headerStr.split("\\s+");
|
||||
|
||||
if (header.length != 3 || !header[0].equals("accessWidener")) {
|
||||
throw new UnsupportedOperationException("Invalid access access widener header");
|
||||
}
|
||||
|
||||
if (!header[1].equals("v1")) {
|
||||
throw new RuntimeException(String.format("Unsupported access widener format (%s)", header[1]));
|
||||
}
|
||||
|
||||
if (namespace != null) {
|
||||
if (!namespace.equals(header[2])) {
|
||||
throw new RuntimeException(String.format("Namespace mismatch, expected %s got %s", namespace, header[2]));
|
||||
}
|
||||
}
|
||||
|
||||
namespace = header[2];
|
||||
|
||||
String line;
|
||||
|
||||
Set<String> targets = new LinkedHashSet<>();
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
// Comment handling
|
||||
int commentPos = line.indexOf('#');
|
||||
|
||||
if (commentPos >= 0) {
|
||||
line = line.substring(0, commentPos).trim();
|
||||
}
|
||||
|
||||
if (line.isEmpty()) continue;
|
||||
|
||||
String[] split = line.split("\\s+");
|
||||
|
||||
if (split.length != 3 && split.length != 5) {
|
||||
throw new RuntimeException(String.format("Invalid line (%s)", line));
|
||||
}
|
||||
|
||||
String access = split[0];
|
||||
|
||||
targets.add(split[2].replaceAll("/", "."));
|
||||
|
||||
switch (split[1]) {
|
||||
case "class":
|
||||
if (split.length != 3) {
|
||||
throw new RuntimeException(String.format("Expected (<access>\tclass\t<className>) got (%s)", line));
|
||||
}
|
||||
|
||||
classAccess.put(split[2], applyAccess(access, classAccess.getOrDefault(split[2], ClassAccess.DEFAULT), null));
|
||||
break;
|
||||
case "field":
|
||||
if (split.length != 5) {
|
||||
throw new RuntimeException(String.format("Expected (<access>\tfield\t<className>\t<fieldName>\t<fieldDesc>) got (%s)", line));
|
||||
}
|
||||
|
||||
addOrMerge(fieldAccess, new EntryTriple(split[2], split[3], split[4]), access, FieldAccess.DEFAULT);
|
||||
break;
|
||||
case "method":
|
||||
if (split.length != 5) {
|
||||
throw new RuntimeException(String.format("Expected (<access>\tmethod\t<className>\t<methodName>\t<methodDesc>) got (%s)", line));
|
||||
}
|
||||
|
||||
addOrMerge(methodAccess, new EntryTriple(split[2], split[3], split[4]), access, MethodAccess.DEFAULT);
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported type " + split[1]);
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> parentClasses = new LinkedHashSet<>();
|
||||
|
||||
//Also transform all parent classes
|
||||
for (String clazz : targets) {
|
||||
while (clazz.contains("$")) {
|
||||
clazz = clazz.substring(0, clazz.lastIndexOf("$"));
|
||||
parentClasses.add(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
classes.addAll(targets);
|
||||
classes.addAll(parentClasses);
|
||||
}
|
||||
|
||||
// Could possibly be cleaner but should do its job for now
|
||||
public void write(StringWriter writer) {
|
||||
writer.write("accessWidener\tv1\t");
|
||||
writer.write(namespace);
|
||||
writer.write("\n");
|
||||
|
||||
for (Map.Entry<String, Access> entry : classAccess.entrySet()) {
|
||||
for (String s : getAccesses(entry.getValue())) {
|
||||
writer.write(s);
|
||||
writer.write("\tclass\t");
|
||||
writer.write(entry.getKey());
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, Access> entry : methodAccess.entrySet()) {
|
||||
writeEntry(writer, "method", entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, Access> entry : fieldAccess.entrySet()) {
|
||||
writeEntry(writer, "field", entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEntry(StringWriter writer, String type, EntryTriple entryTriple, Access access) {
|
||||
for (String s : getAccesses(access)) {
|
||||
writer.write(s);
|
||||
writer.write("\t");
|
||||
writer.write(type);
|
||||
writer.write("\t");
|
||||
writer.write(entryTriple.getOwner());
|
||||
writer.write("\t");
|
||||
writer.write(entryTriple.getName());
|
||||
writer.write("\t");
|
||||
writer.write(entryTriple.getDesc());
|
||||
writer.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getAccesses(Access access) {
|
||||
List<String> accesses = new ArrayList<>();
|
||||
|
||||
if (access == ClassAccess.ACCESSIBLE || access == MethodAccess.ACCESSIBLE || access == FieldAccess.ACCESSIBLE || access == MethodAccess.ACCESSIBLE_EXTENDABLE || access == ClassAccess.ACCESSIBLE_EXTENDABLE || access == FieldAccess.ACCESSIBLE_MUTABLE) {
|
||||
accesses.add("accessible");
|
||||
}
|
||||
|
||||
if (access == ClassAccess.EXTENDABLE || access == MethodAccess.EXTENDABLE || access == MethodAccess.ACCESSIBLE_EXTENDABLE || access == ClassAccess.ACCESSIBLE_EXTENDABLE) {
|
||||
accesses.add("extendable");
|
||||
}
|
||||
|
||||
if (access == FieldAccess.MUTABLE || access == FieldAccess.ACCESSIBLE_MUTABLE) {
|
||||
accesses.add("mutable");
|
||||
}
|
||||
|
||||
return accesses;
|
||||
}
|
||||
|
||||
void addOrMerge(Map<EntryTriple, Access> map, EntryTriple entry, Access access) {
|
||||
if (entry == null || access == null) {
|
||||
throw new RuntimeException("Input entry or access is null");
|
||||
}
|
||||
|
||||
Access merged = null;
|
||||
|
||||
if (access instanceof ClassAccess) {
|
||||
merged = ClassAccess.DEFAULT;
|
||||
} else if (access instanceof MethodAccess) {
|
||||
merged = MethodAccess.DEFAULT;
|
||||
} else if (access instanceof FieldAccess) {
|
||||
merged = FieldAccess.DEFAULT;
|
||||
}
|
||||
|
||||
merged = mergeAccess(merged, access);
|
||||
|
||||
map.put(entry, merged);
|
||||
}
|
||||
|
||||
void addOrMerge(Map<EntryTriple, Access> map, EntryTriple entry, String access, Access defaultAccess) {
|
||||
if (entry == null || access == null) {
|
||||
throw new RuntimeException("Input entry or access is null");
|
||||
}
|
||||
|
||||
map.put(entry, applyAccess(access, map.getOrDefault(entry, defaultAccess), entry));
|
||||
}
|
||||
|
||||
public void merge(AccessWidener other) {
|
||||
if (namespace == null) {
|
||||
namespace = other.namespace;
|
||||
} else if (!namespace.equals(other.namespace)) {
|
||||
throw new RuntimeException("Namespace mismatch");
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Access> entry : other.classAccess.entrySet()) {
|
||||
if (classAccess.containsKey(entry.getKey())) {
|
||||
classAccess.replace(entry.getKey(), mergeAccess(classAccess.get(entry.getKey()), entry.getValue()));
|
||||
} else {
|
||||
classAccess.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, Access> entry : other.methodAccess.entrySet()) {
|
||||
addOrMerge(methodAccess, entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, Access> entry : other.fieldAccess.entrySet()) {
|
||||
addOrMerge(fieldAccess, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private Access applyAccess(String input, Access access, EntryTriple entryTriple) {
|
||||
switch (input.toLowerCase(Locale.ROOT)) {
|
||||
case "accessible":
|
||||
makeClassAccessible(entryTriple);
|
||||
return access.makeAccessible();
|
||||
case "extendable":
|
||||
makeClassExtendable(entryTriple);
|
||||
return access.makeExtendable();
|
||||
case "mutable":
|
||||
return access.makeMutable();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unknown access type:" + input);
|
||||
}
|
||||
}
|
||||
|
||||
private void makeClassAccessible(EntryTriple entryTriple) {
|
||||
if (entryTriple == null) return;
|
||||
classAccess.put(entryTriple.getOwner(), applyAccess("accessible", classAccess.getOrDefault(entryTriple.getOwner(), ClassAccess.DEFAULT), null));
|
||||
}
|
||||
|
||||
private void makeClassExtendable(EntryTriple entryTriple) {
|
||||
if (entryTriple == null) return;
|
||||
classAccess.put(entryTriple.getOwner(), applyAccess("extendable", classAccess.getOrDefault(entryTriple.getOwner(), ClassAccess.DEFAULT), null));
|
||||
}
|
||||
|
||||
private static Access mergeAccess(Access a, Access b) {
|
||||
Access access = a;
|
||||
|
||||
if (b == ClassAccess.ACCESSIBLE || b == MethodAccess.ACCESSIBLE || b == FieldAccess.ACCESSIBLE || b == MethodAccess.ACCESSIBLE_EXTENDABLE || b == ClassAccess.ACCESSIBLE_EXTENDABLE || b == FieldAccess.ACCESSIBLE_MUTABLE) {
|
||||
access = access.makeAccessible();
|
||||
}
|
||||
|
||||
if (b == ClassAccess.EXTENDABLE || b == MethodAccess.EXTENDABLE || b == MethodAccess.ACCESSIBLE_EXTENDABLE || b == ClassAccess.ACCESSIBLE_EXTENDABLE) {
|
||||
access = access.makeExtendable();
|
||||
}
|
||||
|
||||
if (b == FieldAccess.MUTABLE || b == FieldAccess.ACCESSIBLE_MUTABLE) {
|
||||
access = access.makeMutable();
|
||||
}
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
public Access getClassAccess(String className) {
|
||||
return classAccess.getOrDefault(className, ClassAccess.DEFAULT);
|
||||
}
|
||||
|
||||
public Access getFieldAccess(EntryTriple entryTriple) {
|
||||
return fieldAccess.getOrDefault(entryTriple, FieldAccess.DEFAULT);
|
||||
}
|
||||
|
||||
public Access getMethodAccess(EntryTriple entryTriple) {
|
||||
return methodAccess.getOrDefault(entryTriple, MethodAccess.DEFAULT);
|
||||
}
|
||||
|
||||
public Set<String> getTargets() {
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static int makePublic(int i) {
|
||||
return (i & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC;
|
||||
}
|
||||
|
||||
private static int makeProtected(int i) {
|
||||
if ((i & Opcodes.ACC_PUBLIC) != 0) {
|
||||
// Return i if public
|
||||
return i;
|
||||
}
|
||||
|
||||
return (i & ~(Opcodes.ACC_PRIVATE)) | Opcodes.ACC_PROTECTED;
|
||||
}
|
||||
|
||||
private static int makeFinalIfPrivate(int access, String name, int ownerAccess) {
|
||||
// Dont make constructors final
|
||||
if (name.equals("<init>")) {
|
||||
return access;
|
||||
}
|
||||
|
||||
// Skip interface and static methods
|
||||
if ((ownerAccess & Opcodes.ACC_INTERFACE) != 0 || (access & Opcodes.ACC_STATIC) != 0) {
|
||||
return access;
|
||||
}
|
||||
|
||||
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||
return access | Opcodes.ACC_FINAL;
|
||||
}
|
||||
|
||||
return access;
|
||||
}
|
||||
|
||||
private static int removeFinal(int i) {
|
||||
return i & ~Opcodes.ACC_FINAL;
|
||||
}
|
||||
|
||||
public interface Access extends AccessOperator {
|
||||
Access makeAccessible();
|
||||
|
||||
Access makeExtendable();
|
||||
|
||||
Access makeMutable();
|
||||
}
|
||||
|
||||
public enum ClassAccess implements Access {
|
||||
DEFAULT((access, name, ownerAccess) -> access),
|
||||
ACCESSIBLE((access, name, ownerAccess) -> makePublic(access)),
|
||||
EXTENDABLE((access, name, ownerAccess) -> makePublic(removeFinal(access))),
|
||||
ACCESSIBLE_EXTENDABLE((access, name, ownerAccess) -> makePublic(removeFinal(access)));
|
||||
|
||||
private final AccessOperator operator;
|
||||
|
||||
ClassAccess(AccessOperator operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeAccessible() {
|
||||
if (this == EXTENDABLE || this == ACCESSIBLE_EXTENDABLE) {
|
||||
return ACCESSIBLE_EXTENDABLE;
|
||||
}
|
||||
|
||||
return ACCESSIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeExtendable() {
|
||||
if (this == ACCESSIBLE || this == ACCESSIBLE_EXTENDABLE) {
|
||||
return ACCESSIBLE_EXTENDABLE;
|
||||
}
|
||||
|
||||
return EXTENDABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeMutable() {
|
||||
throw new UnsupportedOperationException("Classes cannot be made mutable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int access, String targetName, int ownerAccess) {
|
||||
return operator.apply(access, targetName, ownerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
public enum MethodAccess implements Access {
|
||||
DEFAULT((access, name, ownerAccess) -> access),
|
||||
ACCESSIBLE((access, name, ownerAccess) -> makePublic(makeFinalIfPrivate(access, name, ownerAccess))),
|
||||
EXTENDABLE((access, name, ownerAccess) -> makeProtected(removeFinal(access))),
|
||||
ACCESSIBLE_EXTENDABLE((access, name, owner) -> makePublic(removeFinal(access)));
|
||||
|
||||
private final AccessOperator operator;
|
||||
|
||||
MethodAccess(AccessOperator operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeAccessible() {
|
||||
if (this == EXTENDABLE || this == ACCESSIBLE_EXTENDABLE) {
|
||||
return ACCESSIBLE_EXTENDABLE;
|
||||
}
|
||||
|
||||
return ACCESSIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeExtendable() {
|
||||
if (this == ACCESSIBLE || this == ACCESSIBLE_EXTENDABLE) {
|
||||
return ACCESSIBLE_EXTENDABLE;
|
||||
}
|
||||
|
||||
return EXTENDABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeMutable() {
|
||||
throw new UnsupportedOperationException("Methods cannot be made mutable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int access, String targetName, int ownerAccess) {
|
||||
return operator.apply(access, targetName, ownerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FieldAccess implements Access {
|
||||
DEFAULT((access, name, ownerAccess) -> access),
|
||||
ACCESSIBLE((access, name, ownerAccess) -> makePublic(access)),
|
||||
MUTABLE((access, name, ownerAccess) -> removeFinal(access)),
|
||||
ACCESSIBLE_MUTABLE((access, name, ownerAccess) -> makePublic(removeFinal(access)));
|
||||
|
||||
private final AccessOperator operator;
|
||||
|
||||
FieldAccess(AccessOperator operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeAccessible() {
|
||||
if (this == MUTABLE || this == ACCESSIBLE_MUTABLE) {
|
||||
return ACCESSIBLE_MUTABLE;
|
||||
}
|
||||
|
||||
return ACCESSIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeExtendable() {
|
||||
throw new UnsupportedOperationException("Fields cannot be made extendable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Access makeMutable() {
|
||||
if (this == ACCESSIBLE || this == ACCESSIBLE_MUTABLE) {
|
||||
return ACCESSIBLE_MUTABLE;
|
||||
}
|
||||
|
||||
return MUTABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int apply(int access, String targetName, int ownerAccess) {
|
||||
return operator.apply(access, targetName, ownerAccess);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface AccessOperator {
|
||||
int apply(int access, String targetName, int ownerAccess);
|
||||
}
|
||||
}
|
||||
@@ -41,29 +41,35 @@ import org.gradle.api.Project;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
import org.zeroturnaround.zip.transform.ByteArrayZipEntryTransformer;
|
||||
import org.zeroturnaround.zip.transform.ZipEntryTransformer;
|
||||
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
|
||||
|
||||
import net.fabricmc.mappings.EntryTriple;
|
||||
import net.fabricmc.accesswidener.AccessWidener;
|
||||
import net.fabricmc.accesswidener.AccessWidenerRemapper;
|
||||
import net.fabricmc.accesswidener.AccessWidenerReader;
|
||||
import net.fabricmc.accesswidener.AccessWidenerVisitor;
|
||||
import net.fabricmc.accesswidener.AccessWidenerWriter;
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.loom.processors.JarProcessor;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.Checksum;
|
||||
import net.fabricmc.tinyremapper.TinyRemapper;
|
||||
|
||||
public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
private AccessWidener accessWidener = new AccessWidener();
|
||||
private Project project;
|
||||
private AccessWidenerReader accessWidenerReader = new AccessWidenerReader(accessWidener);
|
||||
private final Project project;
|
||||
private byte[] inputHash;
|
||||
|
||||
@Override
|
||||
public void setup(Project project) {
|
||||
public AccessWidenerJarProcessor(Project project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
LoomGradleExtension loomGradleExtension = project.getExtensions().getByType(LoomGradleExtension.class);
|
||||
|
||||
if (!loomGradleExtension.accessWidener.exists()) {
|
||||
@@ -73,13 +79,13 @@ public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
inputHash = Checksum.sha256(loomGradleExtension.accessWidener);
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(loomGradleExtension.accessWidener))) {
|
||||
accessWidener.read(reader);
|
||||
accessWidenerReader.read(reader);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read project access widener file");
|
||||
}
|
||||
|
||||
//Remap accessWidener if its not named, allows for AE's to be written in intermediary
|
||||
if (!accessWidener.namespace.equals("named")) {
|
||||
if (!accessWidener.getNamespace().equals("named")) {
|
||||
try {
|
||||
TinyRemapper tinyRemapper = loomGradleExtension.getMinecraftMappedProvider().getTinyRemapper("srg", "named");
|
||||
tinyRemapper.readClassPath(loomGradleExtension.getMinecraftMappedProvider().getRemapClasspath());
|
||||
@@ -113,10 +119,11 @@ public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
protected byte[] transform(ZipEntry zipEntry, byte[] input) {
|
||||
ClassReader reader = new ClassReader(input);
|
||||
ClassWriter writer = new ClassWriter(0);
|
||||
ClassVisitor classVisitor = AccessWidenerVisitor.createClassVisitor(Constants.ASM_VERSION, writer, accessWidener);
|
||||
|
||||
project.getLogger().lifecycle("Applying access widener to " + className);
|
||||
|
||||
reader.accept(new AccessTransformer(writer), 0);
|
||||
reader.accept(classVisitor, 0);
|
||||
return writer.toByteArray();
|
||||
}
|
||||
};
|
||||
@@ -142,9 +149,10 @@ public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
public byte[] getRemappedAccessWidener(Remapper asmRemapper) throws IOException {
|
||||
AccessWidenerRemapper remapper = new AccessWidenerRemapper(accessWidener, asmRemapper, "intermediary");
|
||||
AccessWidener remapped = remapper.remap();
|
||||
AccessWidenerWriter accessWidenerWriter = new AccessWidenerWriter(remapped);
|
||||
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
remapped.write(writer);
|
||||
accessWidenerWriter.write(writer);
|
||||
return writer.toString().getBytes();
|
||||
}
|
||||
}
|
||||
@@ -175,78 +183,4 @@ public class AccessWidenerJarProcessor implements JarProcessor {
|
||||
|
||||
return !Arrays.equals(inputHash, hash); // TODO how do we know if the current jar as the correct access applied? save the hash of the input?
|
||||
}
|
||||
|
||||
private class AccessTransformer extends ClassVisitor {
|
||||
private String className;
|
||||
private int classAccess;
|
||||
|
||||
private AccessTransformer(ClassVisitor classVisitor) {
|
||||
super(Opcodes.ASM7, classVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
className = name;
|
||||
classAccess = access;
|
||||
super.visit(
|
||||
version,
|
||||
accessWidener.getClassAccess(name).apply(access, name, classAccess),
|
||||
name,
|
||||
signature,
|
||||
superName,
|
||||
interfaces
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
super.visitInnerClass(
|
||||
name,
|
||||
outerName,
|
||||
innerName,
|
||||
accessWidener.getClassAccess(name).apply(access, name, classAccess)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
return super.visitField(
|
||||
accessWidener.getFieldAccess(new EntryTriple(className, name, descriptor)).apply(access, name, classAccess),
|
||||
name,
|
||||
descriptor,
|
||||
signature,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
return new AccessWidenerMethodVisitor(super.visitMethod(
|
||||
accessWidener.getMethodAccess(new EntryTriple(className, name, descriptor)).apply(access, name, classAccess),
|
||||
name,
|
||||
descriptor,
|
||||
signature,
|
||||
exceptions
|
||||
));
|
||||
}
|
||||
|
||||
private class AccessWidenerMethodVisitor extends MethodVisitor {
|
||||
AccessWidenerMethodVisitor(MethodVisitor methodVisitor) {
|
||||
super(Opcodes.ASM7, methodVisitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
|
||||
if (opcode == Opcodes.INVOKESPECIAL && owner.equals(className) && !name.equals("<init>")) {
|
||||
AccessWidener.Access methodAccess = accessWidener.getMethodAccess(new EntryTriple(owner, name, descriptor));
|
||||
|
||||
if (methodAccess != AccessWidener.MethodAccess.DEFAULT) {
|
||||
opcode = Opcodes.INVOKEVIRTUAL;
|
||||
}
|
||||
}
|
||||
|
||||
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* 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.accesswidener;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import net.fabricmc.mappings.EntryTriple;
|
||||
|
||||
public class AccessWidenerRemapper {
|
||||
private final AccessWidener input;
|
||||
private final String to;
|
||||
private final Remapper remapper;
|
||||
|
||||
public AccessWidenerRemapper(AccessWidener input, Remapper remapper, String to) {
|
||||
this.input = input;
|
||||
this.to = to;
|
||||
this.remapper = remapper;
|
||||
}
|
||||
|
||||
public AccessWidener remap() {
|
||||
// Dont remap if we dont need to
|
||||
if (input.namespace.equals(to)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
AccessWidener remapped = new AccessWidener();
|
||||
remapped.namespace = to;
|
||||
|
||||
for (Map.Entry<String, AccessWidener.Access> entry : input.classAccess.entrySet()) {
|
||||
remapped.classAccess.put(remapper.map(entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, AccessWidener.Access> entry : input.methodAccess.entrySet()) {
|
||||
remapped.addOrMerge(remapped.methodAccess, remapMethod(entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
for (Map.Entry<EntryTriple, AccessWidener.Access> entry : input.fieldAccess.entrySet()) {
|
||||
remapped.addOrMerge(remapped.fieldAccess, remapField(entry.getKey()), entry.getValue());
|
||||
}
|
||||
|
||||
return remapped;
|
||||
}
|
||||
|
||||
private EntryTriple remapMethod(EntryTriple entryTriple) {
|
||||
return new EntryTriple(
|
||||
remapper.map(entryTriple.getOwner()),
|
||||
remapper.mapMethodName(entryTriple.getOwner(), entryTriple.getName(), entryTriple.getDesc()),
|
||||
remapper.mapDesc(entryTriple.getDesc())
|
||||
);
|
||||
}
|
||||
|
||||
private EntryTriple remapField(EntryTriple entryTriple) {
|
||||
return new EntryTriple(
|
||||
remapper.map(entryTriple.getOwner()),
|
||||
remapper.mapFieldName(entryTriple.getOwner(), entryTriple.getName(), entryTriple.getDesc()),
|
||||
remapper.mapDesc(entryTriple.getDesc())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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.mappings;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.cadixdev.lorenz.MappingSet;
|
||||
import org.cadixdev.lorenz.io.TextMappingsWriter;
|
||||
import org.cadixdev.lorenz.io.proguard.ProGuardReader;
|
||||
import org.cadixdev.lorenz.model.ClassMapping;
|
||||
import org.cadixdev.lorenz.model.FieldMapping;
|
||||
import org.cadixdev.lorenz.model.InnerClassMapping;
|
||||
import org.cadixdev.lorenz.model.MethodMapping;
|
||||
import org.cadixdev.lorenz.model.TopLevelClassMapping;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.SelfResolvingDependency;
|
||||
import org.gradle.api.tasks.TaskDependency;
|
||||
import org.zeroturnaround.zip.ByteSource;
|
||||
import org.zeroturnaround.zip.ZipEntrySource;
|
||||
import org.zeroturnaround.zip.ZipUtil;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.DownloadUtil;
|
||||
import net.fabricmc.loom.util.MinecraftVersionInfo;
|
||||
import net.fabricmc.lorenztiny.TinyMappingsReader;
|
||||
import net.fabricmc.mapping.tree.TinyMappingFactory;
|
||||
|
||||
public class MojangMappingsDependency implements SelfResolvingDependency {
|
||||
public static final String GROUP = "net.minecraft";
|
||||
public static final String MODULE = "mappings";
|
||||
// Keys in dependency manifest
|
||||
private static final String MANIFEST_CLIENT_MAPPINGS = "client_mappings";
|
||||
private static final String MANIFEST_SERVER_MAPPINGS = "server_mappings";
|
||||
|
||||
private final Project project;
|
||||
private final LoomGradleExtension extension;
|
||||
|
||||
public MojangMappingsDependency(Project project, LoomGradleExtension extension) {
|
||||
this.project = project;
|
||||
this.extension = extension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<File> resolve() {
|
||||
Path mappingsDir = extension.getMappingsProvider().getMappingsDir();
|
||||
Path mappingsFile = mappingsDir.resolve(String.format("%s.%s-%s.tiny", GROUP, MODULE, getVersion()));
|
||||
Path clientMappings = mappingsDir.resolve(String.format("%s.%s-%s-client.map", GROUP, MODULE, getVersion()));
|
||||
Path serverMappings = mappingsDir.resolve(String.format("%s.%s-%s-server.map", GROUP, MODULE, getVersion()));
|
||||
|
||||
if (!Files.exists(mappingsFile) || project.getGradle().getStartParameter().isRefreshDependencies()) {
|
||||
MappingSet mappingSet;
|
||||
|
||||
try {
|
||||
mappingSet = getMappingsSet(clientMappings, serverMappings);
|
||||
|
||||
try (Writer writer = new StringWriter()) {
|
||||
new TinyWriter(writer, "intermediary", "named").write(mappingSet);
|
||||
Files.deleteIfExists(mappingsFile);
|
||||
|
||||
ZipUtil.pack(new ZipEntrySource[] {
|
||||
new ByteSource("mappings/mappings.tiny", writer.toString().getBytes(StandardCharsets.UTF_8))
|
||||
}, mappingsFile.toFile());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to resolve Mojang mappings", e);
|
||||
}
|
||||
}
|
||||
|
||||
try (BufferedReader clientBufferedReader = Files.newBufferedReader(clientMappings, StandardCharsets.UTF_8)) {
|
||||
project.getLogger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
project.getLogger().warn("Using of the official minecraft mappings is at your own risk!");
|
||||
project.getLogger().warn("Please make sure to read and understand the following license:");
|
||||
project.getLogger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
String line;
|
||||
|
||||
while ((line = clientBufferedReader.readLine()).startsWith("#")) {
|
||||
project.getLogger().warn(line);
|
||||
}
|
||||
|
||||
project.getLogger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read client mappings", e);
|
||||
}
|
||||
|
||||
return Collections.singleton(mappingsFile.toFile());
|
||||
}
|
||||
|
||||
private MappingSet getMappingsSet(Path clientMappings, Path serverMappings) throws IOException {
|
||||
MinecraftVersionInfo versionInfo = extension.getMinecraftProvider().getVersionInfo();
|
||||
|
||||
if (versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS) == null) {
|
||||
throw new RuntimeException("Failed to find official mojang mappings for " + getVersion());
|
||||
}
|
||||
|
||||
String clientMappingsUrl = versionInfo.downloads.get(MANIFEST_CLIENT_MAPPINGS).url;
|
||||
String serverMappingsUrl = versionInfo.downloads.get(MANIFEST_SERVER_MAPPINGS).url;
|
||||
|
||||
DownloadUtil.downloadIfChanged(new URL(clientMappingsUrl), clientMappings.toFile(), project.getLogger());
|
||||
DownloadUtil.downloadIfChanged(new URL(serverMappingsUrl), serverMappings.toFile(), project.getLogger());
|
||||
|
||||
MappingSet mappings = MappingSet.create();
|
||||
|
||||
try (BufferedReader clientBufferedReader = Files.newBufferedReader(clientMappings, StandardCharsets.UTF_8);
|
||||
BufferedReader serverBufferedReader = Files.newBufferedReader(serverMappings, StandardCharsets.UTF_8)) {
|
||||
try (ProGuardReader proGuardReaderClient = new ProGuardReader(clientBufferedReader);
|
||||
ProGuardReader proGuardReaderServer = new ProGuardReader(serverBufferedReader)) {
|
||||
proGuardReaderClient.read(mappings);
|
||||
proGuardReaderServer.read(mappings);
|
||||
}
|
||||
}
|
||||
|
||||
MappingSet officialToNamed = mappings.reverse();
|
||||
MappingSet intermediaryToOfficial;
|
||||
|
||||
try (BufferedReader reader = Files.newBufferedReader(extension.getMappingsProvider().getIntermediaryTiny(), StandardCharsets.UTF_8)) {
|
||||
intermediaryToOfficial = new TinyMappingsReader(TinyMappingFactory.loadWithDetection(reader), "intermediary", "official").read();
|
||||
}
|
||||
|
||||
MappingSet intermediaryToMojang = MappingSet.create();
|
||||
|
||||
// Merging. Don't use MappingSet#merge
|
||||
iterateClasses(intermediaryToOfficial, inputMappings -> {
|
||||
officialToNamed.getClassMapping(inputMappings.getFullDeobfuscatedName())
|
||||
.ifPresent(namedClass -> {
|
||||
ClassMapping<?, ?> mojangClassMapping = intermediaryToMojang.getOrCreateClassMapping(inputMappings.getFullObfuscatedName())
|
||||
.setDeobfuscatedName(namedClass.getFullDeobfuscatedName());
|
||||
|
||||
for (FieldMapping fieldMapping : inputMappings .getFieldMappings()) {
|
||||
namedClass.getFieldMapping(fieldMapping.getDeobfuscatedName())
|
||||
.ifPresent(namedField -> {
|
||||
mojangClassMapping.getOrCreateFieldMapping(fieldMapping.getSignature())
|
||||
.setDeobfuscatedName(namedField.getDeobfuscatedName());
|
||||
});
|
||||
}
|
||||
|
||||
for (MethodMapping methodMapping : inputMappings .getMethodMappings()) {
|
||||
namedClass.getMethodMapping(methodMapping.getDeobfuscatedSignature())
|
||||
.ifPresent(namedMethod -> {
|
||||
mojangClassMapping.getOrCreateMethodMapping(methodMapping.getSignature())
|
||||
.setDeobfuscatedName(namedMethod.getDeobfuscatedName());
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return intermediaryToMojang;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<File> resolve(boolean transitive) {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskDependency getBuildDependencies() {
|
||||
return task -> Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroup() {
|
||||
return GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return MODULE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return extension.getMinecraftProvider().getMinecraftVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contentEquals(Dependency dependency) {
|
||||
if (dependency instanceof MojangMappingsDependency) {
|
||||
return ((MojangMappingsDependency) dependency).extension.getMinecraftProvider().getMinecraftVersion().equals(getVersion());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dependency copy() {
|
||||
return new MojangMappingsDependency(project, extension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReason() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void because(String s) {
|
||||
}
|
||||
|
||||
private static void iterateClasses(MappingSet mappings, Consumer<ClassMapping<?, ?>> consumer) {
|
||||
for (TopLevelClassMapping classMapping : mappings.getTopLevelClassMappings()) {
|
||||
iterateClass(classMapping, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void iterateClass(ClassMapping<?, ?> classMapping, Consumer<ClassMapping<?, ?>> consumer) {
|
||||
consumer.accept(classMapping);
|
||||
|
||||
for (InnerClassMapping innerClassMapping : classMapping.getInnerClassMappings()) {
|
||||
iterateClass(innerClassMapping, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
private static class TinyWriter extends TextMappingsWriter {
|
||||
private final String namespaceFrom;
|
||||
private final String namespaceTo;
|
||||
|
||||
protected TinyWriter(Writer writer, String namespaceFrom, String namespaceTo) {
|
||||
super(writer);
|
||||
this.namespaceFrom = namespaceFrom;
|
||||
this.namespaceTo = namespaceTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(MappingSet mappings) {
|
||||
writer.println("tiny\t2\t0\t" + namespaceFrom + "\t" + namespaceTo);
|
||||
|
||||
iterateClasses(mappings, classMapping -> {
|
||||
writer.println("c\t" + classMapping.getFullObfuscatedName() + "\t" + classMapping.getFullDeobfuscatedName());
|
||||
|
||||
for (FieldMapping fieldMapping : classMapping.getFieldMappings()) {
|
||||
fieldMapping.getType().ifPresent(fieldType -> {
|
||||
writer.println("\tf\t" + fieldType + "\t" + fieldMapping.getObfuscatedName() + "\t" + fieldMapping.getDeobfuscatedName());
|
||||
});
|
||||
}
|
||||
|
||||
for (MethodMapping methodMapping : classMapping.getMethodMappings()) {
|
||||
writer.println("\tm\t" + methodMapping.getSignature().getDescriptor() + "\t" + methodMapping.getObfuscatedName() + "\t" + methodMapping.getDeobfuscatedName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,10 +72,10 @@ public abstract class AnnotationProcessorInvoker<T extends 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");
|
||||
put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, extension.getMappingsProvider().tinyMappings.getCanonicalPath());
|
||||
put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, extension.getNextMixinMappings().getCanonicalPath());
|
||||
put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, extension));
|
||||
put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary");
|
||||
}};
|
||||
|
||||
project.getLogger().debug("Outputting refmap to dir: " + getDestinationDir(task) + " for compile task: " + task);
|
||||
@@ -94,14 +94,14 @@ public abstract class AnnotationProcessorInvoker<T extends Task> {
|
||||
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)
|
||||
configs.getByName(Constants.Configurations.MINECRAFT_NAMED),
|
||||
configs.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED),
|
||||
configs.getByName(Constants.Configurations.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);
|
||||
Constants.Dependencies.MIXIN_COMPILE_EXTENSIONS + Constants.Dependencies.Versions.MIXIN_COMPILE_EXTENSIONS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user