diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a0fdd15a..e5d8228d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,6 +6,7 @@ on: - 'dev/0.6-forge' - 'dev/0.7-forge' - 'dev/0.8' + - 'dev/0.9' jobs: build: diff --git a/.gitignore b/.gitignore index c316a770..6a36faa2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ !/settings.gradle !/Jenkinsfile !/checkstyle.xml -!/codenarc.groovy \ No newline at end of file +!/codenarc.groovy +!/bootstrap +!/forge-runtime diff --git a/bootstrap/.gitignore b/bootstrap/.gitignore new file mode 100644 index 00000000..f8dc37c9 --- /dev/null +++ b/bootstrap/.gitignore @@ -0,0 +1,7 @@ +# Ignore everything +/* + +!/src +!/build.gradle +!/.gitignore +!/test-project \ No newline at end of file diff --git a/bootstrap/build.gradle b/bootstrap/build.gradle new file mode 100644 index 00000000..7db4d755 --- /dev/null +++ b/bootstrap/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'java' + id 'groovy' +} + +sourceCompatibility = 8 +targetCompatibility = 8 + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" + it.options.release = 8 +} + +repositories { + mavenCentral() +} + +dependencies { + implementation gradleApi() + + testImplementation(gradleTestKit()) + testImplementation('org.spockframework:spock-core:2.0-M5-groovy-3.0') { + exclude module: 'groovy-all' + } +} + +test { + maxHeapSize = "4096m" + useJUnitPlatform() +} \ No newline at end of file diff --git a/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java new file mode 100644 index 00000000..c64246ef --- /dev/null +++ b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/BootstrappedPlugin.java @@ -0,0 +1,7 @@ +package net.fabricmc.loom.bootstrap; + +import org.gradle.api.plugins.PluginAware; + +public interface BootstrappedPlugin { + void apply(PluginAware project); +} diff --git a/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java new file mode 100644 index 00000000..54762934 --- /dev/null +++ b/bootstrap/src/main/java/net/fabricmc/loom/bootstrap/LoomGradlePluginBootstrap.java @@ -0,0 +1,72 @@ +package net.fabricmc.loom.bootstrap; + +import java.util.ArrayList; +import java.util.List; + +import org.gradle.api.JavaVersion; +import org.gradle.api.Plugin; +import org.gradle.api.plugins.PluginAware; +import org.gradle.util.GradleVersion; + +/** + * This bootstrap is compiled against a minimal gradle API and java 8, this allows us to show a nice error to users who run on unsupported configurations. + */ +@SuppressWarnings("unused") +public class LoomGradlePluginBootstrap implements Plugin { + private static final int MIN_SUPPORTED_MAJOR_GRADLE_VERSION = 7; + private static final int MIN_SUPPORTED_MAJOR_JAVA_VERSION = 16; + + private static final String PLUGIN_CLASS_NAME = "net.fabricmc.loom.LoomGradlePlugin"; + + @Override + public void apply(PluginAware project) { + List errors = new ArrayList<>(); + + if (!isValidGradleRuntime()) { + errors.add(String.format("You are using an outdated version of Gradle (%s). Gradle %d or higher is required.", GradleVersion.current().getVersion(), MIN_SUPPORTED_MAJOR_GRADLE_VERSION)); + } + + if (!isValidJavaRuntime()) { + errors.add(String.format("You are using an outdated version of Java (%s). Java %d or higher is required.", JavaVersion.current().getMajorVersion(), MIN_SUPPORTED_MAJOR_JAVA_VERSION)); + + if (Boolean.getBoolean("idea.active")) { + // Idea specific error + errors.add("You can change the Java version in the Gradle settings dialog."); + } else { + String javaHome = System.getenv("JAVA_HOME"); + + if (javaHome != null) { + errors.add(String.format("The JAVA_HOME environment variable is currently set to (%s).", javaHome)); + } + } + } + + if (!errors.isEmpty()) { + throw new UnsupportedOperationException(String.join("\n", errors)); + } + + getActivePlugin().apply(project); + } + + private static boolean isValidJavaRuntime() { + // Note use compareTo to ensure compatibility with gradle < 6.0 + return JavaVersion.current().compareTo(JavaVersion.toVersion(MIN_SUPPORTED_MAJOR_JAVA_VERSION)) >= 0; + } + + private static boolean isValidGradleRuntime() { + return getMajorGradleVersion() >= MIN_SUPPORTED_MAJOR_GRADLE_VERSION; + } + + private static int getMajorGradleVersion() { + String version = GradleVersion.current().getVersion(); + return Integer.parseInt(version.substring(0, version.indexOf("."))); + } + + BootstrappedPlugin getActivePlugin() { + try { + return (BootstrappedPlugin) Class.forName(PLUGIN_CLASS_NAME).getConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Failed to bootstrap loom", e); + } + } +} diff --git a/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF b/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF new file mode 100644 index 00000000..9a1b230b --- /dev/null +++ b/bootstrap/src/main/resources/META-INF/trick-gradle-into-thinking-loom-is-signed.SF @@ -0,0 +1,4 @@ +Trick gradle into thinking that loom is signed to skip over transforming all classes in the jar. +This is required to get the bootstrap to well bootstrap on older gradle versions that dont support java 16. + +See https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/internal/classpath/InstrumentingClasspathFileTransformer.java#L129 \ No newline at end of file diff --git a/bootstrap/test-project/build.gradle b/bootstrap/test-project/build.gradle new file mode 100644 index 00000000..f2a0541d --- /dev/null +++ b/bootstrap/test-project/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'fabric-loom' version '0.9.local' +} + +dependencies { + minecraft "com.mojang:minecraft:1.16.5" + mappings loom.officialMojangMappings() +} \ No newline at end of file diff --git a/bootstrap/test-project/settings.gradle b/bootstrap/test-project/settings.gradle new file mode 100644 index 00000000..37a1b9e0 --- /dev/null +++ b/bootstrap/test-project/settings.gradle @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + mavenLocal() + } +} diff --git a/build.gradle b/build.gradle index 9c1c7f36..d36e1e98 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.w3c.dom.Document plugins { id 'java' @@ -12,6 +13,7 @@ plugins { id 'codenarc' id "org.cadixdev.licenser" version "0.5.0" id 'com.github.johnrengelman.shadow' version '7.0.0' + id 'net.kyori.blossom' version '1.3.0' } sourceCompatibility = 16 @@ -22,10 +24,9 @@ tasks.withType(JavaCompile).configureEach { it.options.release = 16 } - group = "dev.architectury" archivesBaseName = project.name -def baseVersion = '0.8.0' +def baseVersion = '0.9.0' def runNumber = System.getenv("GITHUB_RUN_NUMBER") ?: "9999" def isSnapshot = System.getenv("PR_NUM") != null @@ -40,26 +41,39 @@ if (!isSnapshot) { logger.lifecycle(":building plugin v${version}") -configurations { - forgeInjectShadow - forgeInjectCompileClasspath.extendsFrom(forgeInjectShadow) - forgeInjectRuntimeClasspath.extendsFrom(forgeInjectShadow) -} - -sourceSets { - forgeInject -} - repositories { mavenCentral() maven { url "https://maven.fabricmc.net/" } maven { url "https://maven.architectury.dev/" } - maven { url "https://maven.minecraftforge.net/" } + maven { + url "https://maven.minecraftforge.net/" + content { + excludeGroupByRegex "org\\.eclipse\\.?.*" + } + } +} + +configurations { + bootstrap { + transitive false + } + compileClasspath.extendsFrom bootstrap + runtimeClasspath.extendsFrom bootstrap + testRuntimeClasspath.extendsFrom bootstrap +} + +configurations.all { + resolutionStrategy { + // I am sorry, for now + // failOnNonReproducibleResolution() + } } dependencies { implementation gradleApi() + bootstrap project(":bootstrap") + // libraries implementation ('commons-io:commons-io:2.8.0') implementation ('org.zeroturnaround:zt-zip:1.14') @@ -80,11 +94,12 @@ dependencies { } // tinyfile management - implementation ('dev.architectury:tiny-remapper:1.0.0') - implementation ('dev.architectury:mappings-layers-core:1.1.6') + implementation ('dev.architectury:tiny-remapper:1.1.0') + implementation ('dev.architectury:mappings-layers-core:1.3.8') implementation ('net.fabricmc:tiny-mappings-parser:0.3.0+build.17') implementation 'net.fabricmc:access-widener:1.0.0' + implementation 'net.fabricmc:mapping-io:0.1.3' implementation ('net.fabricmc:lorenz-tiny:3.0.0') { transitive = false @@ -93,12 +108,33 @@ dependencies { implementation "dev.architectury:refmap-remapper:1.0.5" // decompilers - implementation ('net.fabricmc:fabric-fernflower:1.4.0') + implementation ('net.fabricmc:fabric-fernflower:1.4.1') implementation ('org.benf:cfr:0.151') // source code remapping implementation ('org.cadixdev:mercury:0.2.9-architectury') + // Mercury pulls all of these deps in, however eclipse does not specify the exact version to use so they can get updated without us knowing. + // Depend specifically on these versions to prevent them from being updated under our feet. + // The POM is also patched later on to as this strict versioning does not make it through. + implementation ('org.eclipse.jdt:org.eclipse.jdt.core:[3.21.0]') + implementation ('org.eclipse.platform:org.eclipse.compare.core:[3.6.1000]') + implementation ('org.eclipse.platform:org.eclipse.core.commands:[3.9.800]') + implementation ('org.eclipse.platform:org.eclipse.core.contenttype:[3.7.900]') + implementation ('org.eclipse.platform:org.eclipse.core.expressions:[3.7.100]') + implementation ('org.eclipse.platform:org.eclipse.core.filesystem:[1.7.700]') + implementation ('org.eclipse.platform:org.eclipse.core.jobs:[3.10.1100]') + implementation ('org.eclipse.platform:org.eclipse.core.resources:[3.14.0]') + implementation ('org.eclipse.platform:org.eclipse.core.runtime:[3.20.100]') + implementation ('org.eclipse.platform:org.eclipse.equinox.app:[1.5.100]') + implementation ('org.eclipse.platform:org.eclipse.equinox.common:[3.14.100]') + implementation ('org.eclipse.platform:org.eclipse.equinox.preferences:[3.8.200]') + implementation ('org.eclipse.platform:org.eclipse.equinox.registry:[3.10.100]') + implementation ('org.eclipse.platform:org.eclipse.osgi:[3.16.200]') + implementation ('org.eclipse.platform:org.eclipse.team.core:[3.8.1100]') + implementation ('org.eclipse.platform:org.eclipse.text:[3.11.0]') + + // Kapt integration compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0') @@ -109,17 +145,9 @@ dependencies { implementation ('de.oceanlabs.mcp:mcinjector:3.8.0') implementation ('com.opencsv:opencsv:5.4') - // Forge injection - forgeInjectShadow ('net.fabricmc:tiny-mappings-parser:0.2.2.14') - forgeInjectImplementation ('cpw.mods:modlauncher:6.1.3') - forgeInjectImplementation ('org.spongepowered:mixin:0.8.2') - forgeInjectImplementation ('com.google.code.gson:gson:2.8.6') - forgeInjectImplementation ('com.google.guava:guava:21.0') - forgeInjectImplementation ('org.apache.logging.log4j:log4j-api:2.11.2') - // Testing testImplementation(gradleTestKit()) - testImplementation('org.spockframework:spock-core:2.0-M5-groovy-3.0') { + testImplementation('org.spockframework:spock-core:2.0-groovy-3.0') { exclude module: 'groovy-all' } testImplementation 'io.javalin:javalin:3.13.7' @@ -127,13 +155,8 @@ dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' } -task forgeInjectJar(type: ShadowJar, dependsOn: [compileForgeInjectJava, processForgeInjectResources]) { - relocate 'net.fabricmc.mapping', 'net.fabricmc.loom.mapping' - relocate 'net.fabricmc.mappings', 'net.fabricmc.loom.mappings' - configurations = [project.configurations.forgeInjectShadow] - classifier = 'forgeinject' - from compileForgeInjectJava.outputs - from processForgeInjectResources.outputs +blossom { + replaceToken '$LOOM_VERSION', version } jar { @@ -141,17 +164,13 @@ jar { } task mainJar(type: Jar, dependsOn: jar) { - dependsOn forgeInjectJar - from zipTree(jar.archiveFile) - from(forgeInjectJar.outputs) { - into "inject" - rename { "injection.jar" } - } manifest { attributes 'Implementation-Version': project.version + ' Build(' + buildNum + ')' } + + from configurations.bootstrap.collect { it.isDirectory() ? it : zipTree(it) } } task sourcesJar(type: Jar, dependsOn: classes) { @@ -173,7 +192,6 @@ license { exclude '**/loom/util/DownloadUtil.java' exclude '**/projects' exclude '**/loom/util/FileSystemUtil.java' - exclude '**/loom/inject/mixin/MixinIntermediaryDevRemapper.java' } checkstyle { @@ -190,7 +208,7 @@ gradlePlugin { plugins { fabricLoom { id = 'dev.architectury.loom' - implementationClass = 'net.fabricmc.loom.LoomGradlePlugin' + implementationClass = 'net.fabricmc.loom.bootstrap.LoomGradlePluginBootstrap' } } } @@ -220,6 +238,20 @@ import org.w3c.dom.Document import org.w3c.dom.Element import org.w3c.dom.Node +def patchPom(groovy.util.Node node) { + node.dependencies.first().each { + def groupId = it.get("groupId").first().value().first() + + // Patch all eclipse deps to use a strict version + if (groupId.startsWith("org.eclipse.")) { + def version = it.get("version").first().value().first() + if (!version.startsWith("[")) { + it.get("version").first().value = new groovy.util.NodeList(["[$version]"]) + } + } + } +} + publishing { publications { plugin(MavenPublication) { @@ -229,6 +261,10 @@ publishing { from components.java artifact mainJar artifact sourcesJar + + pom.withXml { + patchPom(asNode()) + } } maven(MavenPublication) { publication -> @@ -239,6 +275,10 @@ publishing { artifact mainJar artifact sourcesJar artifact javadocJar + + pom.withXml { + patchPom(asNode()) + } } if (isSnapshot) return @@ -252,6 +292,10 @@ publishing { artifact mainJar artifact sourcesJar artifact javadocJar + + pom.withXml { + patchPom(asNode()) + } } pluginSnapshot(MavenPublication) { @@ -288,6 +332,21 @@ publishing { } } +// Need to tweak this file to pretend we are compatible with j8 so the bootstrap will run. +tasks.withType(GenerateModuleMetadata) { + doLast { + def file = outputFile.get().asFile + + def metadata = new groovy.json.JsonSlurper().parseText(file.text) + + metadata.variants.each { + it.attributes["org.gradle.jvm.version"] = 8 + } + + file.text = groovy.json.JsonOutput.toJson(metadata) + } +} + // A task to output a json file with a list of all the test to run task writeActionsTestMatrix() { doLast { @@ -313,3 +372,11 @@ task writeActionsTestMatrix() { output.text = json } } + +tasks.named('wrapper') { + distributionType = Wrapper.DistributionType.ALL +} + +tasks.withType(GenerateModuleMetadata) { + enabled = false +} diff --git a/forge-runtime/.gitignore b/forge-runtime/.gitignore new file mode 100644 index 00000000..31ecb657 --- /dev/null +++ b/forge-runtime/.gitignore @@ -0,0 +1,6 @@ +# Ignore everything +/* + +!/src +!/build.gradle +!/.gitignore diff --git a/forge-runtime/build.gradle b/forge-runtime/build.gradle new file mode 100644 index 00000000..f7bf4e4a --- /dev/null +++ b/forge-runtime/build.gradle @@ -0,0 +1,92 @@ +plugins { + id 'java' + id 'maven-publish' + id 'checkstyle' + id 'com.github.johnrengelman.shadow' + id 'org.cadixdev.licenser' +} + +group = rootProject.group +archivesBaseName = 'architectury-loom-forge-runtime' +version = rootProject.version + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +configurations { + include + compileOnly.extendsFrom include +} + +tasks.withType(JavaCompile).configureEach { + it.options.encoding = "UTF-8" + it.options.release = 8 +} + +repositories { + mavenCentral() + maven { url "https://maven.fabricmc.net/" } + maven { + url "https://maven.minecraftforge.net/" + content { + excludeGroupByRegex "org\\.eclipse\\.?.*" + } + } +} + +dependencies { + // shadowed + include ('net.fabricmc:tiny-mappings-parser:0.3.0+build.17') + + // guaranteed to be there at runtime + compileOnly ('cpw.mods:modlauncher:6.1.3') + compileOnly ('org.spongepowered:mixin:0.8.2') + compileOnly ('com.google.code.gson:gson:2.8.6') + compileOnly ('com.google.guava:guava:21.0') + compileOnly ('org.apache.logging.log4j:log4j-api:2.11.2') +} + +jar { + archiveClassifier = "slim" +} + +shadowJar { + archiveClassifier = "" + configurations = [project.configurations.include] + relocate "net.fabricmc.mapping", "dev.architectury.loom.forgeruntime.shadow.mapping" + relocate "net.fabricmc.mappings", "dev.architectury.loom.forgeruntime.shadow.mappings" +} + +assemble.dependsOn shadowJar + +license { + header rootProject.file("HEADER") + include "**/*.java" + exclude '**/loom/forgeruntime/mixin/MixinIntermediaryDevRemapper.java' +} + +checkstyle { + configFile = rootProject.checkstyle.configFile + toolVersion = rootProject.checkstyle.toolVersion +} + +publishing { + publications { + maven(MavenPublication) { + artifactId = 'architectury-loom-forge-runtime' + shadow.component it + } + } + + repositories { + if (System.getenv("MAVEN_PASS") != null) { + maven { + url = "https://deploy.shedaniel.me/" + credentials { + username = "shedaniel" + password = System.getenv("MAVEN_PASS") + } + } + } + } +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/YarnNamingService.java similarity index 96% rename from src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java rename to forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/YarnNamingService.java index 2b62dc53..9d3c2aa9 100644 --- a/src/forgeInject/java/net/fabricmc/loom/inject/YarnNamingService.java +++ b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/YarnNamingService.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package net.fabricmc.loom.inject; +package dev.architectury.loom.forgeruntime; import java.io.BufferedReader; import java.io.IOException; @@ -30,6 +30,7 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.AbstractMap; import java.util.Map; import java.util.Optional; import java.util.function.BiFunction; @@ -56,7 +57,7 @@ public class YarnNamingService implements INameMappingService { @Override public Map.Entry understanding() { - return new Pair<>("srg", "mcp"); + return new AbstractMap.SimpleImmutableEntry<>("srg", "mcp"); } @Override diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/ForgeLoomMixinRemapperInjectorService.java similarity index 98% rename from src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java rename to forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/ForgeLoomMixinRemapperInjectorService.java index 1fe961c6..c32f6724 100644 --- a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/ForgeLoomMixinRemapperInjectorService.java +++ b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/ForgeLoomMixinRemapperInjectorService.java @@ -22,7 +22,7 @@ * SOFTWARE. */ -package net.fabricmc.loom.inject.mixin; +package dev.architectury.loom.forgeruntime.mixin; import java.io.BufferedReader; import java.nio.file.Files; diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/MixinIntermediaryDevRemapper.java similarity index 99% rename from src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java rename to forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/MixinIntermediaryDevRemapper.java index ef5724dc..f331a9ca 100644 --- a/src/forgeInject/java/net/fabricmc/loom/inject/mixin/MixinIntermediaryDevRemapper.java +++ b/forge-runtime/src/main/java/dev/architectury/loom/forgeruntime/mixin/MixinIntermediaryDevRemapper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.fabricmc.loom.inject.mixin; +package dev.architectury.loom.forgeruntime.mixin; import java.util.ArrayDeque; import java.util.Collection; diff --git a/src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java b/forge-runtime/src/main/java/mcp/MethodsReturnNonnullByDefault.java similarity index 90% rename from src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java rename to forge-runtime/src/main/java/mcp/MethodsReturnNonnullByDefault.java index 8fbac050..8ec54e47 100644 --- a/src/forgeInject/java/mcp/MethodsReturnNonnullByDefault.java +++ b/forge-runtime/src/main/java/mcp/MethodsReturnNonnullByDefault.java @@ -25,7 +25,8 @@ package mcp; /** - * A dummy class, required for some Forge classes to load. + * A dummy class, required for some Forge classes to load + * because {@code MethodsReturnNonnullByDefault} in MCP has runtime retention. * * @deprecated Don't use this in your mods. JetBrains annotations are there for you. */ diff --git a/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService b/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService new file mode 100644 index 00000000..ec41d220 --- /dev/null +++ b/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService @@ -0,0 +1 @@ +dev.architectury.loom.forgeruntime.YarnNamingService diff --git a/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService new file mode 100644 index 00000000..679794d7 --- /dev/null +++ b/forge-runtime/src/main/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService @@ -0,0 +1 @@ +dev.architectury.loom.forgeruntime.mixin.ForgeLoomMixinRemapperInjectorService \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e5338d37..d435ce29 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 960cc9c4..0b542936 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,3 @@ rootProject.name = "architectury-loom" +include "bootstrap" +include "forge-runtime" diff --git a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService deleted file mode 100644 index 45290566..00000000 --- a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.INameMappingService +++ /dev/null @@ -1 +0,0 @@ -net.fabricmc.loom.inject.YarnNamingService diff --git a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService b/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService deleted file mode 100644 index 0fb04144..00000000 --- a/src/forgeInject/resources/META-INF/services/cpw.mods.modlauncher.api.ITransformationService +++ /dev/null @@ -1 +0,0 @@ -net.fabricmc.loom.inject.mixin.ForgeLoomMixinRemapperInjectorService \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 59668ca7..021388d6 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -60,7 +60,7 @@ import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.launch.LaunchProviderSettings; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.configuration.processors.JarProcessorManager; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingsProvider; import net.fabricmc.loom.configuration.providers.forge.ForgeProvider; import net.fabricmc.loom.configuration.providers.forge.ForgeUniversalProvider; @@ -68,9 +68,12 @@ import net.fabricmc.loom.configuration.providers.forge.ForgeUserdevProvider; import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider; import net.fabricmc.loom.configuration.providers.forge.PatchProvider; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; -import net.fabricmc.loom.configuration.providers.mappings.MojangMappingsDependency; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; +import net.fabricmc.loom.configuration.providers.mappings.GradleMappingContext; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.function.LazyBool; @@ -220,7 +223,14 @@ public class LoomGradleExtension { } public Dependency officialMojangMappings() { - return new MojangMappingsDependency(project, this); + return layered(LayeredMappingSpecBuilder::officialMojangMappings); + } + + public Dependency layered(Action action) { + LayeredMappingSpecBuilder builder = new LayeredMappingSpecBuilder(this); + action.execute(builder); + LayeredMappingSpec builtSpec = builder.build(); + return new LayeredMappingsDependency(new GradleMappingContext(project, "layers_" + builtSpec.getVersion().replace("+", "_").replace(".", "_")), builtSpec, builtSpec.getVersion()); } public LoomGradleExtension(Project project) { @@ -355,7 +365,7 @@ public class LoomGradleExtension { return new File((String) project.property("fabric.loom.natives.dir")); } - File natives = new File(getUserCache(), "natives/" + getMinecraftProvider().getMinecraftVersion()); + File natives = new File(getUserCache(), "natives/" + getMinecraftProvider().minecraftVersion()); if (!natives.exists()) { natives.mkdirs(); @@ -384,16 +394,16 @@ public class LoomGradleExtension { return getDependencyManager().getProvider(PatchProvider.class); } - public MinecraftProvider getMinecraftProvider() { - return getDependencyManager().getProvider(MinecraftProvider.class); + public MinecraftProviderImpl getMinecraftProvider() { + return getDependencyManager().getProvider(MinecraftProviderImpl.class); } public MinecraftMappedProvider getMinecraftMappedProvider() { return getMappingsProvider().mappedProvider; } - public MappingsProvider getMappingsProvider() { - return getDependencyManager().getProvider(isForge() ? FieldMigratedMappingsProvider.class : MappingsProvider.class); + public MappingsProviderImpl getMappingsProvider() { + return getDependencyManager().getProvider(isForge() ? FieldMigratedMappingsProvider.class : MappingsProviderImpl.class); } public McpConfigProvider getMcpConfigProvider() { @@ -520,7 +530,7 @@ public class LoomGradleExtension { // 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"); + File mixinMapping = new File(getProjectBuildCache(), "mixin-map-" + getMinecraftProvider().minecraftVersion() + "-" + getMappingsProvider().mappingsVersion + "." + mixinMappings.size() + ".tiny"); mixinMappings.add(mixinMapping); return mixinMapping; } diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 03201907..96c49433 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -33,9 +33,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.plugins.PluginAware; +import net.fabricmc.loom.bootstrap.BootstrappedPlugin; import net.fabricmc.loom.configuration.CompileConfiguration; import net.fabricmc.loom.configuration.FabricApiExtension; import net.fabricmc.loom.configuration.MavenPublication; @@ -44,12 +45,20 @@ import net.fabricmc.loom.configuration.providers.mappings.MappingsCache; import net.fabricmc.loom.decompilers.DecompilerConfiguration; import net.fabricmc.loom.task.LoomTasks; -public class LoomGradlePlugin implements Plugin { +public class LoomGradlePlugin implements BootstrappedPlugin { public static boolean refreshDeps; public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @Override + public void apply(PluginAware target) { + target.getPlugins().apply(LoomRepositoryPlugin.class); + + if (target instanceof Project project) { + apply(project); + } + } + public void apply(Project project) { String loomVersion = LoomGradlePlugin.class.getPackage().getImplementationVersion(); Set loggedVersions = new HashSet<>(Arrays.asList(System.getProperty("loom.printed.logged", "").split(","))); @@ -58,6 +67,7 @@ public class LoomGradlePlugin implements Plugin { loggedVersions.add(loomVersion); System.setProperty("loom.printed.logged", String.join(",", loggedVersions)); project.getLogger().lifecycle("Architectury Loom: " + loomVersion); + project.getLogger().lifecycle("You are using an unstable version of Architectury Loom! Please report any issues found!"); } refreshDeps = project.getGradle().getStartParameter().isRefreshDependencies() || "true".equals(System.getProperty("loom.refresh")); diff --git a/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java new file mode 100644 index 00000000..de242a72 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/LoomRepositoryPlugin.java @@ -0,0 +1,212 @@ +/* + * 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; + +import java.io.File; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.artifacts.repositories.IvyArtifactRepository; +import org.gradle.api.initialization.Settings; +import org.gradle.api.invocation.Gradle; +import org.gradle.api.plugins.PluginAware; + +public class LoomRepositoryPlugin implements Plugin { + @Override + public void apply(PluginAware target) { + RepositoryHandler repositories = null; + + if (target instanceof Settings settings) { + repositories = settings.getDependencyResolutionManagement().getRepositories(); + + // leave a marker so projects don't try to override these + settings.getGradle().getPluginManager().apply(LoomRepositoryPlugin.class); + } else if (target instanceof Project project) { + if (project.getGradle().getPlugins().hasPlugin(LoomRepositoryPlugin.class)) { + return; + } + + repositories = project.getRepositories(); + } else if (target instanceof Gradle) { + return; + } else { + throw new IllegalArgumentException("Expected target to be a Project or Settings, but was a " + target.getClass()); + } + + Cache cache = new Cache(target); + + // MavenConfiguration.java + repositories.flatDir(repo -> { + repo.setName("UserLocalCacheFiles"); + repo.dir(cache.getRootBuildCache()); + }); + repositories.maven(repo -> { + repo.setName("UserLocalRemappedMods"); + repo.setUrl(cache.getRemappedModCache()); + }); + repositories.maven(repo -> { + repo.setName("Fabric"); + repo.setUrl("https://maven.fabricmc.net/"); + }); + repositories.maven(repo -> { + repo.setName("Mojang"); + repo.setUrl("https://libraries.minecraft.net/"); + }); + repositories.maven(repo -> { + repo.setName("Forge"); + repo.setUrl("https://maven.minecraftforge.net/"); + + repo.content(descriptor -> { + descriptor.excludeGroupByRegex("org\\.eclipse\\.?.*"); + }); + repo.metadataSources(sources -> { + sources.mavenPom(); + sources.ignoreGradleMetadataRedirection(); + }); + }); + repositories.mavenCentral(); + + // MinecraftMappedProvider.java + repositories.ivy(repo -> { + repo.setUrl(cache.getUserCache()); + repo.patternLayout(layout -> { + layout.artifact("[revision]/[artifact]-[revision](-[classifier])(.[ext])"); + }); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + }); + + // MinecraftProcessedProvider.java + repositories.ivy(repo -> { + repo.setUrl(cache.getRootPersistentCache()); + repo.patternLayout(layout -> { + layout.artifact("[revision]/[artifact]-[revision](-[classifier])(.[ext])"); + }); + repo.metadataSources(IvyArtifactRepository.MetadataSources::artifact); + }); + } +} + +final class Cache { + private PluginAware target; + + Cache(PluginAware target) { + if (target instanceof Project || target instanceof Settings) { + this.target = target; + } else { + throw new IllegalArgumentException("Expected target to be a Project or Settings, but was a " + target.getClass()); + } + } + + File getUserCache() { + File gradleUserHomeDir = null; + + if (target instanceof Settings settings) { + gradleUserHomeDir = settings.getGradle().getGradleUserHomeDir(); + } else if (target instanceof Project project) { + gradleUserHomeDir = project.getGradle().getGradleUserHomeDir(); + } else { + throw new IllegalArgumentException("Expected target to be a Project or Settings, but was a " + target.getClass()); + } + + File userCache = new File(gradleUserHomeDir, "caches" + File.separator + "fabric-loom"); + + if (!userCache.exists()) { + userCache.mkdirs(); + } + + return userCache; + } + + public File getRootPersistentCache() { + File rootDir = null; + + if (target instanceof Settings settings) { + rootDir = settings.getRootDir(); + } else if (target instanceof Project project) { + rootDir = project.getRootDir(); + } else { + throw new IllegalArgumentException("Expected target to be a Project or Settings, but was a " + target.getClass()); + } + + File persistentCache = new File(rootDir, ".gradle" + File.separator + "loom-cache"); + + if (!persistentCache.exists()) { + persistentCache.mkdirs(); + } + + return persistentCache; + } + + public File getRootBuildCache() { + File rootDir = null; + + if (target instanceof Settings settings) { + rootDir = settings.getRootDir(); + } else if (target instanceof Project project) { + rootDir = project.getRootDir(); + } else { + throw new IllegalArgumentException("Expected target to be a Project or Settings, but was a " + target.getClass()); + } + + File buildCache = new File(rootDir, "build" + File.separator + "loom-cache"); + + if (!buildCache.exists()) { + buildCache.mkdirs(); + } + + return buildCache; + } + + public File getRemappedModCache() { + File remappedModCache = new File(getRootPersistentCache(), "remapped_mods"); + + if (!remappedModCache.exists()) { + remappedModCache.mkdir(); + } + + return remappedModCache; + } + + public File getNestedModCache() { + File nestedModCache = new File(getRootPersistentCache(), "nested_mods"); + + if (!nestedModCache.exists()) { + nestedModCache.mkdir(); + } + + return nestedModCache; + } + + public File getNativesJarStore() { + File natives = new File(getUserCache(), "natives/jars"); + + if (!natives.exists()) { + natives.mkdirs(); + } + + return natives; + } +} diff --git a/src/main/java/net/fabricmc/loom/build/JarRemapper.java b/src/main/java/net/fabricmc/loom/build/JarRemapper.java index 116fb135..67088057 100644 --- a/src/main/java/net/fabricmc/loom/build/JarRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/JarRemapper.java @@ -70,6 +70,7 @@ public class JarRemapper { LoggerFilter.replaceSystemOut(); TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper(); remapperBuilder.logger(project.getLogger()::lifecycle); + remapperBuilder.logUnknownInvokeDynamic(false); mappingProviders.forEach(remapperBuilder::withMappings); if (remapOptions != null) { diff --git a/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java index 1b93b44f..61b05eb6 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java @@ -29,12 +29,19 @@ import java.util.Arrays; import java.util.Collection; import java.util.stream.Collectors; -public record MergedNestedJarProvider(NestedJarProvider... parents) implements NestedJarProvider { +import org.gradle.api.Project; + +public record MergedNestedJarProvider(NestedJarProvider... children) implements NestedJarProvider { @Override public Collection provide() { - return Arrays.stream(parents) + return Arrays.stream(children) .map(NestedJarProvider::provide) .flatMap(Collection::stream) .collect(Collectors.toList()); } + + @Override + public void prepare(Project project) { + Arrays.stream(children).forEach(nestedJarProvider -> nestedJarProvider.prepare(project)); + } } diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java index 8de3103e..60640cb6 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java @@ -32,7 +32,8 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.stream.Collectors; + +import javax.annotation.Nullable; import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; @@ -42,7 +43,6 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.Dependency; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.artifacts.ProjectDependency; -import org.gradle.api.artifacts.ResolvedArtifact; import org.gradle.api.artifacts.ResolvedConfiguration; import org.gradle.api.artifacts.ResolvedDependency; import org.gradle.api.tasks.bundling.AbstractArchiveTask; @@ -103,17 +103,17 @@ public final class NestedDependencyProvider implements NestedJarProvider { visited.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 remapJarTasks = dependencyProject.getTasksByName("remapJar", false); Collection jarTasks = dependencyProject.getTasksByName("jar", false); for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) { - if (task instanceof RemapJarTask remapJarTask) { - File file = remapJarTask.getArchiveFile().get().getAsFile(); - fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file)); - } else if (task instanceof AbstractArchiveTask abstractArchiveTask) { - File file = abstractArchiveTask.getArchiveFile().get().getAsFile(); - fileList.add(new DependencyInfo<>(projectDependency, new ProjectDependencyMetaExtractor(), file)); + if (task instanceof AbstractArchiveTask abstractArchiveTask) { + fileList.add(new DependencyInfo<>( + projectDependency, + new ProjectDependencyMetaExtractor(), + abstractArchiveTask.getArchiveFile().get().getAsFile(), + abstractArchiveTask.getArchiveClassifier().getOrNull() + )); } } } @@ -133,14 +133,13 @@ public final class NestedDependencyProvider implements NestedJarProvider { continue; } - List files = dependency - .getModuleArtifacts() - .stream() - .map(ResolvedArtifact::getFile) - .collect(Collectors.toList()); - - for (File file : files) { - fileList.add(new DependencyInfo<>(dependency, new ResolvedDependencyMetaExtractor(), file)); + for (var artifact : dependency.getModuleArtifacts()) { + fileList.add(new DependencyInfo<>( + dependency, + new ResolvedDependencyMetaExtractor(), + artifact.getFile(), + artifact.getClassifier() + )); } } @@ -195,7 +194,12 @@ public final class NestedDependencyProvider implements NestedJarProvider { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("schemaVersion", 1); - jsonObject.addProperty("id", (metaExtractor.group(dependency) + "_" + metaExtractor.name(dependency)).replaceAll("\\.", "_").toLowerCase(Locale.ENGLISH)); + + jsonObject.addProperty("id", + (metaExtractor.group(dependency) + "_" + metaExtractor.name(dependency) + info.getClassifierSuffix()) + .replaceAll("\\.", "_") + .toLowerCase(Locale.ENGLISH) + ); jsonObject.addProperty("version", metaExtractor.version(dependency)); jsonObject.addProperty("name", metaExtractor.name(dependency)); @@ -206,8 +210,8 @@ public final class NestedDependencyProvider implements NestedJarProvider { return LoomGradlePlugin.GSON.toJson(jsonObject); } - private record DependencyInfo(D dependency, DependencyMetaExtractor metaExtractor, File file) { - public void validateInputs() { + private record DependencyInfo(D dependency, DependencyMetaExtractor metaExtractor, File file, @Nullable String classifier) { + void validateInputs() { if (!file.exists()) { throw new RuntimeException("Failed to include nested jars, as it could not be found @ " + file.getAbsolutePath()); } @@ -216,6 +220,14 @@ public final class NestedDependencyProvider implements NestedJarProvider { throw new RuntimeException("Failed to include nested jars, as file was not a jar: " + file.getAbsolutePath()); } } + + String getClassifierSuffix() { + if (classifier == null) { + return ""; + } else { + return "_" + classifier; + } + } } private interface DependencyMetaExtractor { diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java index badb2656..184617ff 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java @@ -58,6 +58,8 @@ public final class NestedJarPathProvider implements NestedJarProvider { } private void validateFiles() { + Preconditions.checkNotNull(files, "null files to nest, was prepare called?"); + for (File file : files) { Preconditions.checkArgument(file.getName().endsWith(".jar"), String.format("Tried to nest %s but it is not a jar", file.getAbsolutePath())); Preconditions.checkArgument(file.exists(), String.format("Tried to nest jar %s but it does not exist", file.getAbsolutePath())); diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 2b6ee858..e4f6fea6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -30,6 +30,7 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; import net.fabricmc.loom.LoomGradleExtension; @@ -38,7 +39,7 @@ import net.fabricmc.loom.build.mixin.KaptApInvoker; import net.fabricmc.loom.build.mixin.ScalaApInvoker; import net.fabricmc.loom.configuration.ide.SetupIntelijRunConfigs; import net.fabricmc.loom.configuration.providers.LaunchProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingsProvider; import net.fabricmc.loom.configuration.providers.forge.ForgeProvider; import net.fabricmc.loom.configuration.providers.forge.ForgeUniversalProvider; @@ -46,7 +47,7 @@ import net.fabricmc.loom.configuration.providers.forge.ForgeUserdevProvider; import net.fabricmc.loom.configuration.providers.forge.McpConfigProvider; import net.fabricmc.loom.configuration.providers.forge.PatchProvider; import net.fabricmc.loom.configuration.providers.forge.SrgProvider; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.task.GenVsCodeProjectTask; import net.fabricmc.loom.util.Constants; @@ -139,15 +140,18 @@ public final class CompileConfiguration { Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); + p.getTasks().withType(JavaCompile.class).configureEach(compile -> { + // Fork the java compiler to ensure that it does not keep any files open. + compile.getOptions().setFork(true); + }); + p.afterEvaluate(project -> { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - MavenConfiguration.setup(project); - LoomDependencyManager dependencyManager = new LoomDependencyManager(); extension.setDependencyManager(dependencyManager); - dependencyManager.addProvider(new MinecraftProvider(project)); + dependencyManager.addProvider(new MinecraftProviderImpl(project)); if (extension.isForge()) { dependencyManager.addProvider(new ForgeProvider(project)); @@ -164,7 +168,7 @@ public final class CompileConfiguration { dependencyManager.addProvider(new ForgeUniversalProvider(project)); } - dependencyManager.addProvider(extension.isForge() ? new FieldMigratedMappingsProvider(project) : new MappingsProvider(project)); + dependencyManager.addProvider(extension.isForge() ? new FieldMigratedMappingsProvider(project) : new MappingsProviderImpl(project)); dependencyManager.addProvider(new LaunchProvider(project)); dependencyManager.handleDependencies(project); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index d325457b..1d5487d4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -42,9 +42,10 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.ModCompileRemapper; import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; import net.fabricmc.loom.configuration.mods.ModProcessor; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; +import net.fabricmc.loom.LoomRepositoryPlugin; public class LoomDependencyManager { private static class ProviderList { @@ -85,7 +86,7 @@ public class LoomDependencyManager { public void handleDependencies(Project project) { List afterTasks = new ArrayList<>(); - MappingsProvider mappingsProvider = null; + MappingsProviderImpl mappingsProvider = null; project.getLogger().info(":setting up loom dependencies"); LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); @@ -99,8 +100,8 @@ public class LoomDependencyManager { return list; }).providers.add(provider); - if (provider instanceof MappingsProvider) { - mappingsProvider = (MappingsProvider) provider; + if (provider instanceof MappingsProviderImpl) { + mappingsProvider = (MappingsProviderImpl) provider; } } @@ -194,7 +195,9 @@ public class LoomDependencyManager { project.getLogger().debug("Loom adding " + name + " from installer JSON"); - if (jsonElement.getAsJsonObject().has("url")) { + // If user choose to use dependencyResolutionManagement, then they should declare + // these repositories manually in the settings file. + if (jsonElement.getAsJsonObject().has("url") && !project.getGradle().getPlugins().hasPlugin(LoomRepositoryPlugin.class)) { String url = jsonElement.getAsJsonObject().get("url").getAsString(); long count = project.getRepositories().stream().filter(artifactRepository -> artifactRepository instanceof MavenArtifactRepository) .map(artifactRepository -> (MavenArtifactRepository) artifactRepository) diff --git a/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java deleted file mode 100644 index dc10e375..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/MavenConfiguration.java +++ /dev/null @@ -1,74 +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.configuration; - -import org.gradle.api.Project; -import org.gradle.api.artifacts.repositories.MavenArtifactRepository; - -import net.fabricmc.loom.LoomGradleExtension; - -public class MavenConfiguration { - public static void setup(Project project) { - LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - - project.getRepositories().flatDir(repo -> { - repo.setName("UserLocalCacheFiles"); - repo.dir(extension.getRootProjectBuildCache()); - }); - - project.getRepositories().maven(repo -> { - repo.setName("UserLocalRemappedMods"); - repo.setUrl(extension.getRemappedModCache()); - }); - - project.getRepositories().maven(repo -> { - repo.setName("Fabric"); - repo.setUrl("https://maven.fabricmc.net/"); - }); - - project.getRepositories().maven(repo -> { - repo.setName("Mojang"); - repo.setUrl("https://libraries.minecraft.net/"); - }); - - project.getRepositories().maven(repo -> { - repo.setName("Forge"); - repo.setUrl("https://maven.minecraftforge.net/"); - - repo.metadataSources(sources -> { - sources.mavenPom(); - - try { - MavenArtifactRepository.MetadataSources.class.getDeclaredMethod("ignoreGradleMetadataRedirection") - .invoke(sources); - } catch (Throwable ignored) { - // Method not available - } - }); - }); - - project.getRepositories().mavenCentral(); - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java index 7e1a1f3f..831e17cb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java @@ -28,6 +28,7 @@ import java.io.IOException; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import org.gradle.api.Action; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.UnknownTaskException; @@ -153,7 +154,14 @@ public class RemapConfiguration { remapSourcesJarTask.dependsOn(sourcesTask); if (isDefaultRemap) { - remapSourcesJarTask.doLast(task -> project.getArtifacts().add("archives", remapSourcesJarTask.getOutput())); + // Do not use lambda here, see: https://github.com/gradle/gradle/pull/17087 + //noinspection Convert2Lambda + remapSourcesJarTask.doLast(new Action<>() { + @Override + public void execute(Task task) { + project.getArtifacts().add("archives", remapSourcesJarTask.getOutput()); + } + }); } if (extension.isShareCaches()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index b0629d69..25607f5c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -64,7 +64,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.LoggerFilter; @@ -142,7 +142,7 @@ public class ModProcessor { String toM = "named"; MinecraftMappedProvider mappedProvider = extension.getMinecraftMappedProvider(); - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); Path mc = extension.isForge() ? mappedProvider.getSrgJar().toPath() : mappedProvider.getIntermediaryJar().toPath(); Path[] mcDeps = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES).getFiles() @@ -157,6 +157,7 @@ public class ModProcessor { LoggerFilter.replaceSystemOut(); TinyRemapper remapper = TinyRemapper.newRemapper() .logger(project.getLogger()::lifecycle) + .logUnknownInvokeDynamic(false) .withMappings(TinyRemapperMappingsHelper.create(mappings, fromM, toM, false)) .renameInvalidLocals(false) .build(); diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java b/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java index 9673a9a4..596dba93 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/MinecraftProcessedProvider.java @@ -31,13 +31,13 @@ import java.util.function.Consumer; import org.apache.commons.io.FileUtils; import org.gradle.api.Project; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.Constants; public class MinecraftProcessedProvider extends MinecraftMappedProvider { - public static final String PROJECT_MAPPED_CLASSIFIER = "projectmapped"; + public final String projectMappedClassifier; private File projectMappedJar; @@ -46,6 +46,8 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider { public MinecraftProcessedProvider(Project project, JarProcessorManager jarProcessorManager) { super(project); this.jarProcessorManager = jarProcessorManager; + this.projectMappedClassifier = "project-" + project.getPath().replace(':', '@') + + "-mapped"; } @Override @@ -65,14 +67,12 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider { jarProcessorManager.process(projectMappedJar); } - getProject().getRepositories().flatDir(repository -> repository.dir(getJarDirectory(getExtension().getProjectPersistentCache(), PROJECT_MAPPED_CLASSIFIER))); - getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED, - getProject().getDependencies().module("net.minecraft:minecraft:" + getJarVersionString(PROJECT_MAPPED_CLASSIFIER))); + getProject().getDependencies().module("net.minecraft:minecraft:" + getJarVersionString(projectMappedClassifier))); } private void invalidateJars() { - File dir = getJarDirectory(getExtension().getUserCache(), PROJECT_MAPPED_CLASSIFIER); + File dir = getJarDirectory(getExtension().getUserCache(), projectMappedClassifier); if (dir.exists()) { getProject().getLogger().warn("Invalidating project jars"); @@ -86,10 +86,10 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider { } @Override - public void initFiles(MinecraftProvider minecraftProvider, MappingsProvider mappingsProvider) { + public void initFiles(MinecraftProviderImpl minecraftProvider, MappingsProviderImpl mappingsProvider) { super.initFiles(minecraftProvider, mappingsProvider); - projectMappedJar = new File(getJarDirectory(getExtension().getProjectPersistentCache(), PROJECT_MAPPED_CLASSIFIER), "minecraft-" + getJarVersionString(PROJECT_MAPPED_CLASSIFIER) + ".jar"); + projectMappedJar = new File(getJarDirectory(getExtension().getRootProjectPersistentCache(), projectMappedClassifier), "minecraft-" + getJarVersionString(projectMappedClassifier) + ".jar"); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java index 44180be5..6325162c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -66,16 +66,17 @@ public class LaunchProvider extends DependencyProvider { .property("client", "org.lwjgl.librarypath", getExtension().getNativesDirectory().getAbsolutePath()) .argument("client", "--assetIndex") - .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().getMinecraftVersion())) + .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) .argument("client", "--assetsDir") .argument("client", new File(getExtension().getUserCache(), "assets").getAbsolutePath()); if (getExtension().isForge()) { launchConfig + // Should match YarnNamingService.PATH_TO_MAPPINGS in forge-runtime .property("fabric.yarnWithSrg.path", getExtension().getMappingsProvider().tinyMappingsWithSrg.toAbsolutePath().toString()) .argument("--fml.mcVersion") - .argument(getExtension().getMinecraftProvider().getMinecraftVersion()) + .argument(getExtension().getMinecraftProvider().minecraftVersion()) .argument("--fml.forgeVersion") .argument(getExtension().getForgeProvider().getVersion().getForgeVersion()) @@ -136,7 +137,8 @@ public class LaunchProvider extends DependencyProvider { annotationDependency = addDependency(Constants.Dependencies.JETBRAINS_ANNOTATIONS + Constants.Dependencies.Versions.JETBRAINS_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); if (getExtension().isForge()) { - addDependency(Constants.Dependencies.JAVAX_ANNOTATIONS + Constants.Dependencies.Versions.JAVAX_ANNOTATIONS, "compileOnly"); + addDependency(Constants.Dependencies.FORGE_RUNTIME + Constants.Dependencies.Versions.FORGE_RUNTIME, JavaPlugin.RUNTIME_ONLY_CONFIGURATION_NAME); + addDependency(Constants.Dependencies.JAVAX_ANNOTATIONS + Constants.Dependencies.Versions.JAVAX_ANNOTATIONS, JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME); } postPopulationScheduler.accept(this::writeRemapClassPath); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java index a35fa92b..fed7f5fb 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java @@ -24,247 +24,10 @@ package net.fabricmc.loom.configuration.providers; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Optional; -import java.util.function.Consumer; - -import com.google.common.base.Stopwatch; -import com.google.common.io.Files; -import org.gradle.api.GradleException; -import org.gradle.api.Project; -import org.gradle.api.logging.Logger; - -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; -import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.DownloadUtil; -import net.fabricmc.loom.util.HashedDownloadUtil; -import net.fabricmc.stitch.merge.JarMerger; -public class MinecraftProvider extends DependencyProvider { - private String minecraftVersion; +public interface MinecraftProvider { + String minecraftVersion(); - private MinecraftVersionMeta versionInfo; - private MinecraftLibraryProvider libraryProvider; - - private File minecraftJson; - public File minecraftClientJar; - public File minecraftServerJar; - private File minecraftMergedJar; - private File versionManifestJson; - private String jarSuffix = ""; - - public MinecraftProvider(Project project) { - super(project); - } - - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { - minecraftVersion = dependency.getDependency().getVersion(); - - if (getExtension().shouldGenerateSrgTiny() && !getExtension().isForge()) { - addDependency("de.oceanlabs.mcp:mcp_config:" + minecraftVersion, Constants.Configurations.SRG); - } - - boolean offline = getProject().getGradle().getStartParameter().isOffline(); - - initFiles(); - - downloadMcJson(offline); - - try (FileReader reader = new FileReader(minecraftJson)) { - versionInfo = LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, MinecraftVersionMeta.class); - } - - // Add Loom as an annotation processor - addDependency(getProject().files(this.getClass().getProtectionDomain().getCodeSource().getLocation()), "compileOnly"); - - if (offline) { - if (minecraftClientJar.exists() && minecraftServerJar.exists()) { - getProject().getLogger().debug("Found client and server jars, presuming up-to-date"); - } else if (minecraftMergedJar.exists()) { - //Strictly we don't need the split jars if the merged one exists, let's try go on - getProject().getLogger().warn("Missing game jar but merged jar present, things might end badly"); - } else { - throw new GradleException("Missing jar(s); Client: " + minecraftClientJar.exists() + ", Server: " + minecraftServerJar.exists()); - } - } else { - downloadJars(getProject().getLogger()); - } - - libraryProvider = new MinecraftLibraryProvider(); - libraryProvider.provide(this, getProject()); - - if (!minecraftMergedJar.exists() || isRefreshDeps()) { - try { - mergeJars(getProject().getLogger()); - } catch (Throwable e) { - HashedDownloadUtil.delete(minecraftClientJar); - HashedDownloadUtil.delete(minecraftServerJar); - minecraftMergedJar.delete(); - - getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); - throw e; - } - } - } - - private void initFiles() { - minecraftJson = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-info.json"); - minecraftClientJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-client.jar"); - minecraftServerJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-server.jar"); - minecraftMergedJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-merged.jar"); - versionManifestJson = new File(getExtension().getUserCache(), "version_manifest.json"); - } - - public void deleteFiles() { - DownloadUtil.delete(minecraftClientJar); - DownloadUtil.delete(minecraftServerJar); - DownloadUtil.delete(minecraftMergedJar); - } - - private void downloadMcJson(boolean offline) throws IOException { - if (getExtension().isShareCaches() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) { - return; - } - - if (!offline && !isRefreshDeps() && hasRecentValidManifest()) { - // We have a recent valid manifest file, so do nothing - } else if (offline) { - if (versionManifestJson.exists()) { - // If there is the manifests already we'll presume that's good enough - getProject().getLogger().debug("Found version manifests, presuming up-to-date"); - } else { - // If we don't have the manifests then there's nothing more we can do - throw new GradleException("Version manifests not found at " + versionManifestJson.getAbsolutePath()); - } - } else { - getProject().getLogger().debug("Downloading version manifests"); - DownloadUtil.downloadIfChanged(new URL(Constants.VERSION_MANIFESTS), versionManifestJson, getProject().getLogger()); - } - - String versionManifest = Files.asCharSource(versionManifestJson, StandardCharsets.UTF_8).read(); - ManifestVersion mcManifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, ManifestVersion.class); - - Optional optionalVersion = Optional.empty(); - - if (getExtension().customManifest != null) { - ManifestVersion.Versions customVersion = new ManifestVersion.Versions(); - customVersion.id = minecraftVersion; - customVersion.url = getExtension().customManifest; - optionalVersion = Optional.of(customVersion); - getProject().getLogger().lifecycle("Using custom minecraft manifest"); - } - - if (!optionalVersion.isPresent()) { - optionalVersion = mcManifest.versions().stream().filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion)).findFirst(); - } - - if (optionalVersion.isPresent()) { - if (offline) { - if (minecraftJson.exists()) { - //If there is the manifest already we'll presume that's good enough - getProject().getLogger().debug("Found Minecraft {} manifest, presuming up-to-date", minecraftVersion); - } else { - //If we don't have the manifests then there's nothing more we can do - throw new GradleException("Minecraft " + minecraftVersion + " manifest not found at " + minecraftJson.getAbsolutePath()); - } - } else { - getProject().getLogger().debug("Downloading Minecraft {} manifest", minecraftVersion); - - ManifestVersion.Versions version = optionalVersion.get(); - String url = version.url; - - if (version.sha1 != null) { - HashedDownloadUtil.downloadIfInvalid(new URL(url), minecraftJson, version.sha1, getProject().getLogger(), true); - } else { - // Use the etag if no hash found from url - DownloadUtil.downloadIfChanged(new URL(url), minecraftJson, getProject().getLogger()); - } - } - } else { - throw new RuntimeException("Failed to find minecraft version: " + minecraftVersion); - } - } - - private boolean hasRecentValidManifest() throws IOException { - if (getExtension().customManifest != null) { - return false; - } - - if (!versionManifestJson.exists() || !minecraftJson.exists()) { - return false; - } - - if (versionManifestJson.lastModified() > System.currentTimeMillis() - 24 * 3_600_000) { - // Version manifest hasn't been modified in 24 hours, time to get a new one. - return false; - } - - ManifestVersion manifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(Files.asCharSource(versionManifestJson, StandardCharsets.UTF_8).read(), ManifestVersion.class); - Optional version = manifest.versions().stream().filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion)).findFirst(); - - // fail if the expected mc version was not found, will download the file again. - return version.isPresent(); - } - - private void downloadJars(Logger logger) throws IOException { - if (getExtension().isShareCaches() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) { - return; - } - - MinecraftVersionMeta.Download client = versionInfo.download("client"); - MinecraftVersionMeta.Download server = versionInfo.download("server"); - - HashedDownloadUtil.downloadIfInvalid(new URL(client.url()), minecraftClientJar, client.sha1(), logger, false); - HashedDownloadUtil.downloadIfInvalid(new URL(server.url()), minecraftServerJar, server.sha1(), logger, false); - } - - private void mergeJars(Logger logger) throws IOException { - logger.info(":merging jars"); - Stopwatch stopwatch = Stopwatch.createStarted(); - - try (JarMerger jarMerger = new JarMerger(minecraftClientJar, minecraftServerJar, minecraftMergedJar)) { - jarMerger.enableSyntheticParamsOffset(); - jarMerger.merge(); - } - - logger.info(":merged jars in " + stopwatch); - } - - public File getMergedJar() { - return minecraftMergedJar; - } - - public String getMinecraftVersion() { - return minecraftVersion; - } - - public MinecraftVersionMeta getVersionInfo() { - return versionInfo; - } - - public MinecraftLibraryProvider getLibraryProvider() { - return libraryProvider; - } - - public String getJarSuffix() { - return jarSuffix; - } - - public void setJarSuffix(String jarSuffix) { - this.jarSuffix = jarSuffix; - } - - @Override - public String getTargetConfig() { - return Constants.Configurations.MINECRAFT; - } + MinecraftVersionMeta getVersionInfo(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java new file mode 100644 index 00000000..b6399e91 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java @@ -0,0 +1,272 @@ +/* + * 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.configuration.providers; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.function.Consumer; + +import com.google.common.base.Stopwatch; +import com.google.common.io.Files; +import org.gradle.api.GradleException; +import org.gradle.api.Project; +import org.gradle.api.logging.Logger; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.DependencyProvider; +import net.fabricmc.loom.configuration.providers.minecraft.ManifestVersion; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftLibraryProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.HashedDownloadUtil; +import net.fabricmc.stitch.merge.JarMerger; + +public class MinecraftProviderImpl extends DependencyProvider implements MinecraftProvider { + private String minecraftVersion; + + private MinecraftVersionMeta versionInfo; + private MinecraftLibraryProvider libraryProvider; + + private File minecraftJson; + public File minecraftClientJar; + public File minecraftServerJar; + private File minecraftMergedJar; + private File versionManifestJson; + private String jarSuffix = ""; + + public MinecraftProviderImpl(Project project) { + super(project); + } + + @Override + public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { + minecraftVersion = dependency.getDependency().getVersion(); + + if (getExtension().shouldGenerateSrgTiny() && !getExtension().isForge()) { + addDependency("de.oceanlabs.mcp:mcp_config:" + minecraftVersion, Constants.Configurations.SRG); + } + + boolean offline = getProject().getGradle().getStartParameter().isOffline(); + + initFiles(); + + downloadMcJson(offline); + + try (FileReader reader = new FileReader(minecraftJson)) { + versionInfo = LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, MinecraftVersionMeta.class); + } + + // Add Loom as an annotation processor + addDependency(getProject().files(this.getClass().getProtectionDomain().getCodeSource().getLocation()), "compileOnly"); + + if (offline) { + if (minecraftClientJar.exists() && minecraftServerJar.exists()) { + getProject().getLogger().debug("Found client and server jars, presuming up-to-date"); + } else if (minecraftMergedJar.exists()) { + //Strictly we don't need the split jars if the merged one exists, let's try go on + getProject().getLogger().warn("Missing game jar but merged jar present, things might end badly"); + } else { + throw new GradleException("Missing jar(s); Client: " + minecraftClientJar.exists() + ", Server: " + minecraftServerJar.exists()); + } + } else { + downloadJars(getProject().getLogger()); + } + + libraryProvider = new MinecraftLibraryProvider(); + libraryProvider.provide(this, getProject()); + + if (!minecraftMergedJar.exists() || isRefreshDeps()) { + try { + mergeJars(getProject().getLogger()); + } catch (Throwable e) { + HashedDownloadUtil.delete(minecraftClientJar); + HashedDownloadUtil.delete(minecraftServerJar); + minecraftMergedJar.delete(); + + getProject().getLogger().error("Could not merge JARs! Deleting source JARs - please re-run the command and move on.", e); + throw e; + } + } + } + + private void initFiles() { + minecraftJson = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-info.json"); + minecraftClientJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-client.jar"); + minecraftServerJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-server.jar"); + minecraftMergedJar = new File(getExtension().getUserCache(), "minecraft-" + minecraftVersion + "-merged.jar"); + versionManifestJson = new File(getExtension().getUserCache(), "version_manifest.json"); + } + + public void deleteFiles() { + DownloadUtil.delete(minecraftClientJar); + DownloadUtil.delete(minecraftServerJar); + DownloadUtil.delete(minecraftMergedJar); + } + + private void downloadMcJson(boolean offline) throws IOException { + if (getExtension().isShareCaches() && !getExtension().isRootProject() && versionManifestJson.exists() && !isRefreshDeps()) { + return; + } + + if (!offline && !isRefreshDeps() && hasRecentValidManifest()) { + // We have a recent valid manifest file, so do nothing + } else if (offline) { + if (versionManifestJson.exists()) { + // If there is the manifests already we'll presume that's good enough + getProject().getLogger().debug("Found version manifests, presuming up-to-date"); + } else { + // If we don't have the manifests then there's nothing more we can do + throw new GradleException("Version manifests not found at " + versionManifestJson.getAbsolutePath()); + } + } else { + getProject().getLogger().debug("Downloading version manifests"); + DownloadUtil.downloadIfChanged(new URL(Constants.VERSION_MANIFESTS), versionManifestJson, getProject().getLogger()); + } + + String versionManifest = Files.asCharSource(versionManifestJson, StandardCharsets.UTF_8).read(); + ManifestVersion mcManifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(versionManifest, ManifestVersion.class); + + Optional optionalVersion = Optional.empty(); + + if (getExtension().customManifest != null) { + ManifestVersion.Versions customVersion = new ManifestVersion.Versions(); + customVersion.id = minecraftVersion; + customVersion.url = getExtension().customManifest; + optionalVersion = Optional.of(customVersion); + getProject().getLogger().lifecycle("Using custom minecraft manifest"); + } + + if (!optionalVersion.isPresent()) { + optionalVersion = mcManifest.versions().stream().filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion)).findFirst(); + } + + if (optionalVersion.isPresent()) { + if (offline) { + if (minecraftJson.exists()) { + //If there is the manifest already we'll presume that's good enough + getProject().getLogger().debug("Found Minecraft {} manifest, presuming up-to-date", minecraftVersion); + } else { + //If we don't have the manifests then there's nothing more we can do + throw new GradleException("Minecraft " + minecraftVersion + " manifest not found at " + minecraftJson.getAbsolutePath()); + } + } else { + getProject().getLogger().debug("Downloading Minecraft {} manifest", minecraftVersion); + + ManifestVersion.Versions version = optionalVersion.get(); + String url = version.url; + + if (version.sha1 != null) { + HashedDownloadUtil.downloadIfInvalid(new URL(url), minecraftJson, version.sha1, getProject().getLogger(), true); + } else { + // Use the etag if no hash found from url + DownloadUtil.downloadIfChanged(new URL(url), minecraftJson, getProject().getLogger()); + } + } + } else { + throw new RuntimeException("Failed to find minecraft version: " + minecraftVersion); + } + } + + private boolean hasRecentValidManifest() throws IOException { + if (getExtension().customManifest != null) { + return false; + } + + if (!versionManifestJson.exists() || !minecraftJson.exists()) { + return false; + } + + if (versionManifestJson.lastModified() > System.currentTimeMillis() - 24 * 3_600_000) { + // Version manifest hasn't been modified in 24 hours, time to get a new one. + return false; + } + + ManifestVersion manifest = LoomGradlePlugin.OBJECT_MAPPER.readValue(Files.asCharSource(versionManifestJson, StandardCharsets.UTF_8).read(), ManifestVersion.class); + Optional version = manifest.versions().stream().filter(versions -> versions.id.equalsIgnoreCase(minecraftVersion)).findFirst(); + + // fail if the expected mc version was not found, will download the file again. + return version.isPresent(); + } + + private void downloadJars(Logger logger) throws IOException { + if (getExtension().isShareCaches() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) { + return; + } + + MinecraftVersionMeta.Download client = versionInfo.download("client"); + MinecraftVersionMeta.Download server = versionInfo.download("server"); + + HashedDownloadUtil.downloadIfInvalid(new URL(client.url()), minecraftClientJar, client.sha1(), logger, false); + HashedDownloadUtil.downloadIfInvalid(new URL(server.url()), minecraftServerJar, server.sha1(), logger, false); + } + + private void mergeJars(Logger logger) throws IOException { + logger.info(":merging jars"); + Stopwatch stopwatch = Stopwatch.createStarted(); + + try (JarMerger jarMerger = new JarMerger(minecraftClientJar, minecraftServerJar, minecraftMergedJar)) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); + } + + logger.info(":merged jars in " + stopwatch); + } + + public File getMergedJar() { + return minecraftMergedJar; + } + + @Override + public String minecraftVersion() { + return minecraftVersion; + } + + @Override + public MinecraftVersionMeta getVersionInfo() { + return versionInfo; + } + + public MinecraftLibraryProvider getLibraryProvider() { + return libraryProvider; + } + + public String getJarSuffix() { + return jarSuffix; + } + + public void setJarSuffix(String jarSuffix) { + this.jarSuffix = jarSuffix; + } + + @Override + public String getTargetConfig() { + return Constants.Configurations.MINECRAFT; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java index 4895e879..f5833007 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/FieldMigratedMappingsProvider.java @@ -28,7 +28,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; @@ -59,7 +58,7 @@ import org.objectweb.asm.Opcodes; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.ThreadingUtils; import net.fabricmc.loom.util.srg.SrgMerger; @@ -68,7 +67,7 @@ import net.fabricmc.mapping.tree.FieldDef; import net.fabricmc.mapping.tree.TinyMappingFactory; import net.fabricmc.mapping.tree.TinyTree; -public class FieldMigratedMappingsProvider extends MappingsProvider { +public class FieldMigratedMappingsProvider extends MappingsProviderImpl { private List> migratedFields = new ArrayList<>(); public Path migratedFieldsCache; public Path rawTinyMappings; @@ -105,7 +104,7 @@ public class FieldMigratedMappingsProvider extends MappingsProvider { @Override public void manipulateMappings(Path mappingsJar) throws IOException { LoomGradleExtension extension = getExtension(); - Path mappingsFolder = mappingsDir.resolve(extension.getMinecraftProvider().getMinecraftVersion() + "/forge-" + extension.getPatchProvider().forgeVersion); + Path mappingsFolder = getMappedVersionedDir(removeSuffix).resolve("forge/" + extension.getPatchProvider().forgeVersion); this.rawTinyMappings = tinyMappings.toPath(); this.rawTinyMappingsWithSrg = tinyMappingsWithSrg; String mappingsJarName = mappingsJar.getFileName().toString(); @@ -143,7 +142,7 @@ public class FieldMigratedMappingsProvider extends MappingsProvider { migratedFields.forEach(entry -> { map.put(entry.getKey().owner + "#" + entry.getKey().field, entry.getValue()); }); - Files.write(migratedFieldsCache, new Gson().toJson(map).getBytes(StandardCharsets.UTF_8)); + Files.writeString(migratedFieldsCache, new Gson().toJson(map)); Files.deleteIfExists(tinyMappings.toPath()); } @@ -174,7 +173,7 @@ public class FieldMigratedMappingsProvider extends MappingsProvider { } } - Files.write(tinyMappings.toPath(), MappingsUtils.serializeToString(mappings).getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE); + Files.writeString(tinyMappings.toPath(), MappingsUtils.serializeToString(mappings), StandardOpenOption.CREATE); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index e4af017d..66921c8c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -30,6 +30,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintStream; import java.io.UncheckedIOException; import java.net.URI; @@ -45,11 +46,16 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import java.util.jar.Attributes; +import java.util.jar.Manifest; import java.util.stream.Stream; +import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableMap; @@ -73,7 +79,7 @@ import org.objectweb.asm.tree.ClassNode; import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; @@ -89,6 +95,10 @@ import net.fabricmc.loom.util.srg.SpecialSourceExecutor; import net.fabricmc.mapping.tree.TinyTree; public class MinecraftPatchedProvider extends DependencyProvider { + private static final String LOOM_PATCH_VERSION_KEY = "Loom-Patch-Version"; + private static final String CURRENT_LOOM_PATCH_VERSION = "2"; + private static final String NAME_MAPPING_SERVICE_PATH = "/inject/META-INF/services/cpw.mods.modlauncher.api.INameMappingService"; + // Step 1: Remap Minecraft to SRG (global) private File minecraftClientSrgJar; private File minecraftServerSrgJar; @@ -106,12 +116,14 @@ public class MinecraftPatchedProvider extends DependencyProvider { @Nullable private File projectAt = null; private boolean atDirty = false; + private boolean filesDirty = false; public MinecraftPatchedProvider(Project project) { super(project); } public void initFiles() throws IOException { + filesDirty = false; projectAtHash = new File(getExtension().getProjectPersistentCache(), "at.sha256"); SourceSet main = getProject().getConvention().findPlugin(JavaPluginConvention.class).getSourceSets().getByName("main"); @@ -140,9 +152,9 @@ public class MinecraftPatchedProvider extends DependencyProvider { atDirty = mismatched; } - MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider(); + MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); PatchProvider patchProvider = getExtension().getPatchProvider(); - String minecraftVersion = minecraftProvider.getMinecraftVersion(); + String minecraftVersion = minecraftProvider.minecraftVersion(); String patchId = "forge-" + patchProvider.forgeVersion; if (getExtension().useFabricMixin) { @@ -166,7 +178,8 @@ public class MinecraftPatchedProvider extends DependencyProvider { minecraftMergedPatchedSrgAtJar = new File(projectDir, "merged-srg-at-patched.jar"); minecraftMergedPatchedJar = new File(projectDir, "merged-patched.jar"); - if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(Predicates.not(File::exists))) { + if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(Predicates.not(File::exists)) + || !isPatchedSrgJarUpdateToDate(minecraftClientPatchedSrgJar) || !isPatchedSrgJarUpdateToDate(minecraftServerPatchedSrgJar)) { cleanAllCache(); } else if (atDirty || Stream.of(getProjectCache()).anyMatch(Predicates.not(File::exists))) { cleanProjectCache(); @@ -242,6 +255,11 @@ public class MinecraftPatchedProvider extends DependencyProvider { remapPatchedJars(getProject().getLogger()); } + if (dirty || !minecraftMergedPatchedJar.exists()) { + mergeJars(getProject().getLogger()); + } + + this.filesDirty = dirty; this.dirty = false; } @@ -258,12 +276,12 @@ public class MinecraftPatchedProvider extends DependencyProvider { private void createSrgJars(Logger logger) throws Exception { McpConfigProvider mcpProvider = getExtension().getMcpConfigProvider(); - MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider(); + MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); String[] mappingsPath = {null}; if (!ZipUtil.handle(mcpProvider.getMcp(), "config.json", (in, zipEntry) -> { - mappingsPath[0] = new JsonParser().parse(new InputStreamReader(in)).getAsJsonObject().get("data").getAsJsonObject().get("mappings").getAsString(); + mappingsPath[0] = JsonParser.parseReader(new InputStreamReader(in)).getAsJsonObject().get("data").getAsJsonObject().get("mappings").getAsString(); })) { throw new IllegalStateException("Failed to find 'config.json' in " + mcpProvider.getMcp().getAbsolutePath() + "!"); } @@ -330,25 +348,28 @@ public class MinecraftPatchedProvider extends DependencyProvider { copyAll(getExtension().getForgeUniversalProvider().getForge(), environment.patchedSrgJar.apply(this)); copyUserdevFiles(getExtension().getForgeUserdevProvider().getUserdevJar(), environment.patchedSrgJar.apply(this)); }); + } - logger.lifecycle(":injecting loom classes into minecraft"); - File injection = File.createTempFile("loom-injection", ".jar"); + private boolean isPatchedSrgJarUpdateToDate(File jar) { + if (!jar.exists()) return false; - try (InputStream in = MinecraftProvider.class.getResourceAsStream("/inject/injection.jar")) { - Files.copy(in, injection.toPath()); + boolean[] upToDate = {false}; + + if (!ZipUtil.handle(jar, "META-INF/MANIFEST.MF", (in, zipEntry) -> { + Manifest manifest = new Manifest(in); + Attributes attributes = manifest.getMainAttributes(); + String value = attributes.getValue(LOOM_PATCH_VERSION_KEY); + + if (Objects.equals(value, CURRENT_LOOM_PATCH_VERSION)) { + upToDate[0] = true; + } else { + getProject().getLogger().lifecycle(":forge patched jars not up to date. current version: " + value); + } + })) { + return false; } - for (Environment environment : Environment.values()) { - String side = environment.side(); - File target = environment.patchedSrgJar.apply(this); - walkFileSystems(injection, target, it -> { - if (it.getFileName().toString().equals("MANIFEST.MF")) { - return false; - } - - return getExtension().useFabricMixin || !it.getFileName().toString().endsWith("cpw.mods.modlauncher.api.ITransformationService"); - }, this::copyReplacing); - } + return upToDate[0]; } private void accessTransformForge(Logger logger) throws Exception { @@ -423,7 +444,8 @@ public class MinecraftPatchedProvider extends DependencyProvider { TinyRemapper remapper = TinyRemapper.newRemapper() .logger(getProject().getLogger()::lifecycle) - .withMappings(TinyRemapperMappingsHelper.create(mappingsWithSrg, "srg", "official", true)) + .logUnknownInvokeDynamic(false) + .withMappings(TinyRemapperMappingsHelper.create(mappingsWithSrg, "srg", "official", true)) .withMappings(InnerClassRemapper.of(input, mappingsWithSrg, "srg", "official")) .renameInvalidLocals(true) .rebuildSourceFilenames(true) @@ -487,7 +509,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { logger.lifecycle(":copying resources"); // Copy resources - MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider(); + MinecraftProviderImpl minecraftProvider = getExtension().getMinecraftProvider(); copyNonClassFiles(minecraftProvider.minecraftClientJar, minecraftMergedPatchedSrgJar); copyNonClassFiles(minecraftProvider.minecraftServerJar, minecraftMergedPatchedSrgJar); } @@ -552,7 +574,13 @@ public class MinecraftPatchedProvider extends DependencyProvider { } private void copyUserdevFiles(File source, File target) throws IOException { - walkFileSystems(source, target, file -> true, fs -> Collections.singleton(fs.getPath("inject")), (sourceFs, targetFs, sourcePath, targetPath) -> { + // Removes the Forge name mapping service definition so that our own is used. + // If there are multiple name mapping services with the same "understanding" pair + // (source -> target namespace pair), modlauncher throws a fit and will crash. + // To use our YarnNamingService instead of MCPNamingService, we have to remove this file. + Predicate filter = file -> !file.toString().equals(NAME_MAPPING_SERVICE_PATH); + + walkFileSystems(source, target, filter, fs -> Collections.singleton(fs.getPath("inject")), (sourceFs, targetFs, sourcePath, targetPath) -> { Path parent = targetPath.getParent(); if (parent != null) { @@ -561,6 +589,22 @@ public class MinecraftPatchedProvider extends DependencyProvider { Files.copy(sourcePath, targetPath); }); + + try (FileSystemUtil.FileSystemDelegate delegate = FileSystemUtil.getJarFileSystem(target, false)) { + Path manifestPath = delegate.get().getPath("META-INF/MANIFEST.MF"); + + Preconditions.checkArgument(Files.exists(manifestPath), "META-INF/MANIFEST.MF does not exist in patched srg jar!"); + Manifest manifest = new Manifest(); + + try (InputStream stream = Files.newInputStream(manifestPath)) { + manifest.read(stream); + manifest.getMainAttributes().putValue(LOOM_PATCH_VERSION_KEY, CURRENT_LOOM_PATCH_VERSION); + } + + try (OutputStream stream = Files.newOutputStream(manifestPath)) { + manifest.write(stream); + } + } } public File getMergedJar() { @@ -572,7 +616,7 @@ public class MinecraftPatchedProvider extends DependencyProvider { } public boolean isAtDirty() { - return atDirty; + return atDirty || filesDirty; } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java new file mode 100644 index 00000000..d9357014 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/GradleMappingContext.java @@ -0,0 +1,74 @@ +/* + * 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.configuration.providers.mappings; + +import java.io.File; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.logging.Logger; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.MinecraftProvider; + +public class GradleMappingContext implements MappingContext { + private final Project project; + private final LoomGradleExtension extension; + private final String workingDirName; + + public GradleMappingContext(Project project, String workingDirName) { + this.project = project; + this.extension = project.getExtensions().getByType(LoomGradleExtension.class); + this.workingDirName = workingDirName; + } + + @Override + public File mavenFile(String mavenNotation) { + Configuration configuration = project.getConfigurations().detachedConfiguration(project.getDependencies().create(mavenNotation)); + return configuration.getSingleFile(); + } + + @Override + public MappingsProvider mappingsProvider() { + return extension.getMappingsProvider(); + } + + @Override + public MinecraftProvider minecraftProvider() { + return extension.getMinecraftProvider(); + } + + @Override + public File workingDirectory(String name) { + File tempDir = new File(mappingsProvider().getMappingsDir().toFile(), workingDirName); + tempDir.mkdirs(); + return new File(tempDir, name); + } + + @Override + public Logger getLogger() { + return project.getLogger(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java new file mode 100644 index 00000000..9f86fb5d --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpec.java @@ -0,0 +1,34 @@ +/* + * 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.configuration.providers.mappings; + +import java.util.List; + +public record LayeredMappingSpec(List> layers) { + public String getVersion() { + // TODO something better? + return "layered+hash.%d".formatted(Math.abs(hashCode())); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilder.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilder.java new file mode 100644 index 00000000..ac4fac49 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingSpecBuilder.java @@ -0,0 +1,79 @@ +/* + * 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.configuration.providers.mappings; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.gradle.api.Action; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.mappings.crane.CraneMappingsSpec; +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec; +import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec; +import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpecBuilder; + +public class LayeredMappingSpecBuilder { + private final List> layers = new LinkedList<>(); + @Nullable + private final LoomGradleExtension extension; + + public LayeredMappingSpecBuilder(LoomGradleExtension extension) { + this.extension = extension; + } + + public LayeredMappingSpecBuilder officialMojangMappings() { + layers.add(new MojangMappingsSpec(() -> extension != null && extension.isSilentMojangMappingsLicenseEnabled())); + return this; + } + + public LayeredMappingSpecBuilder parchment(String mavenNotation) { + parchment(mavenNotation, parchmentMappingsSpecBuilder -> parchmentMappingsSpecBuilder.setRemovePrefix(true)); + return this; + } + + public LayeredMappingSpecBuilder parchment(String mavenNotation, Action action) { + var builder = ParchmentMappingsSpecBuilder.builder(mavenNotation); + action.execute(builder); + layers.add(builder.build()); + return this; + } + + public LayeredMappingSpecBuilder crane(String mavenNotation) { + layers.add(new CraneMappingsSpec(mavenNotation)); + return this; + } + + public LayeredMappingSpec build() { + List> builtLayers = new LinkedList<>(); + // Intermediary is always the base layer + builtLayers.add(new IntermediaryMappingsSpec()); + builtLayers.addAll(layers); + + return new LayeredMappingSpec(Collections.unmodifiableList(builtLayers)); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java new file mode 100644 index 00000000..ddc1664d --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsDependency.java @@ -0,0 +1,143 @@ +/* + * 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.configuration.providers.mappings; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +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.LoomGradlePlugin; +import net.fabricmc.mappingio.adapter.MappingDstNsReorder; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + +public class LayeredMappingsDependency implements SelfResolvingDependency { + private static final String GROUP = "loom"; + private static final String MODULE = "mappings"; + + private final MappingContext mappingContext; + private final LayeredMappingSpec layeredMappingSpec; + private final String version; + + public LayeredMappingsDependency(MappingContext mappingContext, LayeredMappingSpec layeredMappingSpec, String version) { + this.mappingContext = mappingContext; + this.layeredMappingSpec = layeredMappingSpec; + this.version = version; + } + + @Override + public Set resolve() { + Path mappingsDir = mappingContext.mappingsProvider().getMappingsDir(); + Path mappingsFile = mappingsDir.resolve(String.format("%s.%s-%s.tiny", GROUP, MODULE, getVersion())); + + if (!Files.exists(mappingsFile) || LoomGradlePlugin.refreshDeps) { + try { + var processor = new LayeredMappingsProcessor(layeredMappingSpec); + MemoryMappingTree mappings = processor.getMappings(mappingContext); + + try (Writer writer = new StringWriter()) { + Tiny2Writer tiny2Writer = new Tiny2Writer(writer, false); + + MappingDstNsReorder nsReorder = new MappingDstNsReorder(tiny2Writer, Collections.singletonList(MappingNamespace.NAMED.stringValue())); + MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(nsReorder, MappingNamespace.INTERMEDIARY.stringValue()); + mappings.accept(nsSwitch); + + 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); + } + } + + return Collections.singleton(mappingsFile.toFile()); + } + + @Override + public Set 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 version; + } + + @Override + public boolean contentEquals(Dependency dependency) { + if (dependency instanceof LayeredMappingsDependency layeredMappingsDependency) { + return Objects.equals(layeredMappingsDependency.getVersion(), this.getVersion()); + } + + return false; + } + + @Override + public Dependency copy() { + return new LayeredMappingsDependency(mappingContext, layeredMappingSpec, version); + } + + @Override + public String getReason() { + return null; + } + + @Override + public void because(String s) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java new file mode 100644 index 00000000..cc45ea6f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java @@ -0,0 +1,89 @@ +/* + * 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.configuration.providers.mappings; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.tree.MemoryMappingTree; + +public class LayeredMappingsProcessor { + private final LayeredMappingSpec layeredMappingSpec; + + public LayeredMappingsProcessor(LayeredMappingSpec spec) { + this.layeredMappingSpec = spec; + } + + public MemoryMappingTree getMappings(MappingContext context) throws IOException { + MemoryMappingTree mappingTree = new MemoryMappingTree(); + + List> visitedLayers = new ArrayList<>(); + + for (MappingsSpec spec : layeredMappingSpec.layers()) { + MappingLayer layer = spec.createLayer(context); + + for (Class dependentLayer : layer.dependsOn()) { + if (!visitedLayers.contains(dependentLayer)) { + throw new RuntimeException("Layer %s depends on %s".formatted(layer.getClass().getName(), dependentLayer.getName())); + } + } + + visitedLayers.add(layer.getClass()); + + // We have to rebuild a new tree to work on when a layer doesnt merge into layered + boolean rebuild = layer.getSourceNamespace() != MappingNamespace.NAMED; + MemoryMappingTree workingTree; + + if (rebuild) { + var tempTree = new MemoryMappingTree(); + + // This can be null on the first layer + if (mappingTree.getSrcNamespace() != null) { + var sourceNsSwitch = new MappingSourceNsSwitch(tempTree, layer.getSourceNamespace().stringValue()); + mappingTree.accept(sourceNsSwitch); + } + + workingTree = tempTree; + } else { + workingTree = mappingTree; + } + + try { + layer.visit(workingTree); + } catch (IOException e) { + throw new IOException("Failed to visit: " + layer.getClass(), e); + } + + if (rebuild) { + mappingTree = new MemoryMappingTree(); + workingTree.accept(new MappingSourceNsSwitch(mappingTree, MappingNamespace.NAMED.stringValue())); + } + } + + return mappingTree; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java new file mode 100644 index 00000000..5ae5ea6f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingContext.java @@ -0,0 +1,50 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.mappings; + +import java.io.File; + +import org.gradle.api.logging.Logger; + +import net.fabricmc.loom.configuration.providers.MinecraftProvider; + +public interface MappingContext { + File mavenFile(String mavenNotation); + + MappingsProvider mappingsProvider(); + + MinecraftProvider minecraftProvider(); + + default String minecraftVersion() { + return minecraftProvider().minecraftVersion(); + } + + /** + * Creates a temporary working dir to be used to store working files. + */ + File workingDirectory(String name); + + Logger getLogger(); +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingLayer.java new file mode 100644 index 00000000..2fdd451f --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingLayer.java @@ -0,0 +1,43 @@ +/* + * 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.configuration.providers.mappings; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import net.fabricmc.mappingio.MappingVisitor; + +public interface MappingLayer { + void visit(MappingVisitor mappingVisitor) throws IOException; + + default MappingNamespace getSourceNamespace() { + return MappingNamespace.NAMED; + } + + default List> dependsOn() { + return Collections.emptyList(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingNamespace.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingNamespace.java new file mode 100644 index 00000000..8ac400ad --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingNamespace.java @@ -0,0 +1,37 @@ +/* + * 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.configuration.providers.mappings; + +import java.util.Locale; + +public enum MappingNamespace { + OFFICIAL, + INTERMEDIARY, + NAMED; + + public String stringValue() { + return name().toLowerCase(Locale.ROOT); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java index 0d929d79..c2c28280 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProvider.java @@ -24,515 +24,11 @@ package net.fabricmc.loom.configuration.providers.mappings; -import java.io.BufferedReader; import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; -import com.google.common.net.UrlEscapers; -import com.google.gson.JsonObject; -import dev.architectury.mappingslayers.api.utils.MappingsModificationUtils; -import dev.architectury.mappingslayers.api.utils.MappingsUtils; -import org.apache.commons.io.FileUtils; -import org.apache.tools.ant.util.StringUtils; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.zeroturnaround.zip.FileSource; -import org.zeroturnaround.zip.ZipEntrySource; -import org.zeroturnaround.zip.ZipUtil; +public interface MappingsProvider { + Path getMappingsDir(); -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; -import net.fabricmc.loom.configuration.processors.JarProcessorManager; -import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; -import net.fabricmc.loom.configuration.providers.forge.SrgProvider; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; -import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.DeletingFileVisitor; -import net.fabricmc.loom.util.DownloadUtil; -import net.fabricmc.loom.util.srg.MCPReader; -import net.fabricmc.loom.util.srg.SrgMerger; -import net.fabricmc.loom.util.srg.SrgNamedWriter; -import net.fabricmc.mapping.reader.v2.TinyMetadata; -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.TinyFile; -import net.fabricmc.stitch.commands.tinyv2.TinyV2Writer; - -public class MappingsProvider extends DependencyProvider { - public MinecraftMappedProvider mappedProvider; - public MinecraftPatchedProvider patchedProvider; - - public String mappingsName; - public String minecraftVersion; - public String mappingsVersion; - - protected 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 - public File tinyMappings; - // tinyMappings wrapped in a jar - public File tinyMappingsJar; - public Path tinyMappingsWithSrg; - public File mixinTinyMappingsWithSrg; // FORGE: The mixin mappings have srg names in intermediary. - public File srgToNamedSrg; // FORGE: srg to named in srg file format - - private File unpickDefinitionsFile; - private boolean hasUnpickDefinitions; - private UnpickMetadata unpickMetadata; - - public MappingsProvider(Project project) { - super(project); - mappingsDir = getExtension().getUserCache().toPath().resolve("mappings"); - mappingsStepsDir = mappingsDir.resolve("steps"); - } - - public void clean() throws IOException { - FileUtils.deleteDirectory(mappingsDir.toFile()); - } - - public TinyTree getMappings() throws IOException { - return MappingsCache.INSTANCE.get(tinyMappings.toPath()); - } - - public TinyTree getMappingsWithSrg() throws IOException { - if (getExtension().shouldGenerateSrgTiny()) { - return MappingsCache.INSTANCE.get(tinyMappingsWithSrg); - } - - throw new UnsupportedOperationException("Not running with Forge support / Tiny srg support."); - } - - @Override - public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { - MinecraftProvider minecraftProvider = getDependencyManager().getProvider(MinecraftProvider.class); - - getProject().getLogger().info(":setting up mappings (" + dependency.getDependency().getName() + " " + dependency.getResolvedVersion() + ")"); - - String version = dependency.getResolvedVersion(); - File mappingsJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not find yarn mappings: " + dependency)); - - this.mappingsName = StringUtils.removeSuffix(dependency.getDependency().getGroup() + "." + dependency.getDependency().getName(), "-unmerged"); - this.minecraftVersion = minecraftProvider.getMinecraftVersion(); - - boolean isV2; - - if (isMCP(mappingsJar.toPath())) { - File old = mappingsJar; - mappingsJar = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".zip") + "-" + minecraftVersion + ".jar").toFile(); - FileUtils.copyFile(old, mappingsJar); - mappingsName += "-" + minecraftVersion; - } - - // Only do this for official yarn, there isn't really a way we can get the mc version for all mappings - if (dependency.getDependency().getGroup() != null && dependency.getDependency().getGroup().equals("net.fabricmc") && dependency.getDependency().getName().equals("yarn") && dependency.getDependency().getVersion() != null) { - String yarnVersion = dependency.getDependency().getVersion(); - char separator = yarnVersion.contains("+build.") ? '+' : yarnVersion.contains("-") ? '-' : '.'; - String yarnMinecraftVersion = yarnVersion.substring(0, yarnVersion.lastIndexOf(separator)); - - if (!yarnMinecraftVersion.equalsIgnoreCase(minecraftVersion)) { - throw new RuntimeException(String.format("Minecraft Version (%s) does not match yarn's minecraft version (%s)", minecraftVersion, yarnMinecraftVersion)); - } - - // We can save reading the zip file + header by checking the file name - isV2 = mappingsJar.getName().endsWith("-v2.jar"); - } else { - isV2 = doesJarContainV2Mappings(mappingsJar.toPath()); - } - - this.mappingsVersion = version + (isV2 ? "-v2" : ""); - - initFiles(); - - if (isRefreshDeps()) { - cleanFiles(); - } - - Files.createDirectories(mappingsDir); - Files.createDirectories(mappingsStepsDir); - - String[] depStringSplit = dependency.getDepString().split(":"); - String jarClassifier = "final"; - - if (depStringSplit.length >= 4) { - jarClassifier = jarClassifier + depStringSplit[3]; - } - - String removeSuffix = StringUtils.removeSuffix(mappingsJar.getName(), ".jar"); - tinyMappings = mappingsDir.resolve(removeSuffix + ".tiny").toFile(); - unpickDefinitionsFile = mappingsDir.resolve(removeSuffix + ".unpick").toFile(); - tinyMappingsJar = new File(getExtension().getUserCache(), removeSuffix + "-" + jarClassifier + ".jar"); - tinyMappingsWithSrg = mappingsDir.resolve(removeSuffix + "-srg.tiny"); - mixinTinyMappingsWithSrg = mappingsDir.resolve(removeSuffix + "-mixin-srg.tiny").toFile(); - srgToNamedSrg = mappingsDir.resolve(removeSuffix + "-srg-named.srg").toFile(); - - if (!tinyMappings.exists() || isRefreshDeps()) { - storeMappings(getProject(), minecraftProvider, mappingsJar.toPath(), postPopulationScheduler); - } else { - try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { - extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); - } - } - - if (getExtension().isForge()) { - patchedProvider = new MinecraftPatchedProvider(getProject()); - patchedProvider.provide(dependency, postPopulationScheduler); - } - - manipulateMappings(mappingsJar.toPath()); - - if (getExtension().shouldGenerateSrgTiny()) { - if (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps()) { - // Merge tiny mappings with srg - SrgMerger.mergeSrg(getExtension().getSrgProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg, true); - } - } - - if (!tinyMappingsJar.exists() || isRefreshDeps()) { - ZipUtil.pack(new ZipEntrySource[] {new FileSource("mappings/mappings.tiny", tinyMappings)}, tinyMappingsJar); - } - - if (hasUnpickDefinitions()) { - String notation = String.format("%s:%s:%s:constants", - dependency.getDependency().getGroup(), - dependency.getDependency().getName(), - dependency.getDependency().getVersion() - ); - - getProject().getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); - populateUnpickClasspath(); - } - - if (getExtension().isForge()) { - if (!getExtension().shouldGenerateSrgTiny()) { - throw new IllegalStateException("We have to generate srg tiny in a forge environment!"); - } - - if (!mixinTinyMappingsWithSrg.exists() || isRefreshDeps()) { - List lines = new ArrayList<>(Files.readAllLines(tinyMappingsWithSrg)); - lines.set(0, lines.get(0).replace("intermediary", "yraidemretni").replace("srg", "intermediary")); - Files.deleteIfExists(mixinTinyMappingsWithSrg.toPath()); - Files.write(mixinTinyMappingsWithSrg.toPath(), lines); - } - - if (!srgToNamedSrg.exists() || isRefreshDeps()) { - SrgNamedWriter.writeTo(getProject().getLogger(), srgToNamedSrg.toPath(), getMappingsWithSrg(), "srg", "named"); - } - } - - addDependency(tinyMappingsJar, Constants.Configurations.MAPPINGS_FINAL); - - LoomGradleExtension extension = getExtension(); - - if (extension.accessWidener != null) { - extension.addJarProcessor(new AccessWidenerJarProcessor(getProject())); - } - - JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors()); - extension.setJarProcessorManager(processorManager); - processorManager.setupProcessors(); - - if (extension.isForge()) { - patchedProvider.finishProvide(); - } - - if (processorManager.active() || (extension.isForge() && patchedProvider.usesProjectCache())) { - mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager); - getProject().getLogger().lifecycle("Using project based jar storage"); - } else { - mappedProvider = new MinecraftMappedProvider(getProject()); - } - - mappedProvider.initFiles(minecraftProvider, this); - mappedProvider.provide(dependency, postPopulationScheduler); - } - - public void manipulateMappings(Path mappingsJar) throws IOException { } - - private void storeMappings(Project project, MinecraftProvider minecraftProvider, Path yarnJar, Consumer postPopulationScheduler) - throws Exception { - project.getLogger().info(":extracting " + yarnJar.getFileName()); - - if (isMCP(yarnJar)) { - readAndMergeMCP(yarnJar, postPopulationScheduler); - return; - } - - try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { - extractMappings(fileSystem, baseTinyMappings); - extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); - } - - if (baseMappingsAreMergedV2()) { - // Architectury Loom Patch - // If a merged tiny v2 mappings file is provided - // Skip merging, should save a lot of time - Files.copy(baseTinyMappings, tinyMappings.toPath(), StandardCopyOption.REPLACE_EXISTING); - } else if (baseMappingsAreV2()) { - // These are unmerged v2 mappings - mergeAndSaveMappings(project, yarnJar); - } else { - // These are merged v1 mappings - if (tinyMappings.exists()) { - tinyMappings.delete(); - } - - project.getLogger().lifecycle(":populating field names"); - suggestFieldNames(minecraftProvider, baseTinyMappings, tinyMappings.toPath()); - } - } - - private void readAndMergeMCP(Path mcpJar, Consumer postPopulationScheduler) throws Exception { - Path intermediaryTinyPath = getIntermediaryTiny(); - SrgProvider provider = getExtension().getSrgProvider(); - - if (provider == null) { - if (!getExtension().shouldGenerateSrgTiny()) { - Configuration srg = getProject().getConfigurations().maybeCreate(Constants.Configurations.SRG); - srg.setTransitive(false); - } - - provider = new SrgProvider(getProject()); - getProject().getDependencies().add(provider.getTargetConfig(), "de.oceanlabs.mcp:mcp_config:" + minecraftVersion); - Configuration configuration = getProject().getConfigurations().getByName(provider.getTargetConfig()); - provider.provide(DependencyInfo.create(getProject(), configuration.getDependencies().iterator().next(), configuration), postPopulationScheduler); - } - - Path srgPath = provider.getSrg().toPath(); - - TinyFile file = new MCPReader(intermediaryTinyPath, srgPath).read(mcpJar); - TinyV2Writer.write(file, tinyMappings.toPath()); - } - - private boolean isMCP(Path path) throws IOException { - try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { - return Files.exists(fs.getPath("fields.csv")) && Files.exists(fs.getPath("methods.csv")); - } - } - - private boolean baseMappingsAreV2() throws IOException { - try (BufferedReader reader = Files.newBufferedReader(baseTinyMappings)) { - TinyV2Factory.readMetadata(reader); - return true; - } catch (IllegalArgumentException e) { - // TODO: just check the mappings version when Parser supports V1 in readMetadata() - return false; - } - } - - private boolean doesJarContainV2Mappings(Path path) throws IOException { - try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { - try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { - TinyV2Factory.readMetadata(reader); - return true; - } catch (IllegalArgumentException | NoSuchFileException e) { - return false; - } - } - } - - private boolean baseMappingsAreMergedV2() throws IOException { - try (BufferedReader reader = Files.newBufferedReader(baseTinyMappings)) { - TinyMetadata metadata = TinyV2Factory.readMetadata(reader); - return metadata.getNamespaces().containsAll(Arrays.asList("named", "intermediary", "official")); - } catch (IllegalArgumentException e) { - return false; - } - } - - public static void extractMappings(FileSystem jar, Path extractTo) throws IOException { - Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING); - } - - private void extractUnpickDefinitions(FileSystem jar, Path extractTo) throws IOException { - Path unpickPath = jar.getPath("extras/definitions.unpick"); - Path unpickMetadataPath = jar.getPath("extras/unpick.json"); - - if (!Files.exists(unpickPath) || !Files.exists(unpickMetadataPath)) { - return; - } - - Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING); - - unpickMetadata = parseUnpickMetadata(unpickMetadataPath); - hasUnpickDefinitions = true; - } - - private UnpickMetadata parseUnpickMetadata(Path input) throws IOException { - JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(input), JsonObject.class); - - if (!jsonObject.has("version") || jsonObject.get("version").getAsInt() != 1) { - throw new UnsupportedOperationException("Unsupported unpick version"); - } - - return new UnpickMetadata( - jsonObject.get("unpickGroup").getAsString(), - jsonObject.get("unpickVersion").getAsString() - ); - } - - private void populateUnpickClasspath() { - String unpickCliName = "unpick-cli"; - getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, - String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion) - ); - } - - private void extractIntermediary(Path intermediaryJar, Path intermediaryTiny) throws IOException { - getProject().getLogger().info(":extracting " + intermediaryJar.getFileName()); - - 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()); - - try (FileSystem unmergedYarnJarFs = FileSystems.newFileSystem(unmergedYarnJar, (ClassLoader) null)) { - extractMappings(unmergedYarnJarFs, unmergedYarn); - } - - Stopwatch stopwatch = Stopwatch.createStarted(); - project.getLogger().info(":merging mappings"); - Path invertedIntermediary = Paths.get(mappingsStepsDir.toString(), "inverted-intermediary.tiny"); - reorderMappings(getIntermediaryTiny(), invertedIntermediary, "intermediary", "official"); - Path unorderedMergedMappings = Paths.get(mappingsStepsDir.toString(), "unordered-merged.tiny"); - mergeMappings(invertedIntermediary, unmergedYarn, unorderedMergedMappings); - reorderMappings(unorderedMergedMappings, tinyMappings.toPath(), "official", "intermediary", "named"); - Files.deleteIfExists(invertedIntermediary); - Files.deleteIfExists(unorderedMergedMappings); - project.getLogger().info(":merged mappings in " + stopwatch.stop()); - } - - private void reorderMappings(Path oldMappings, Path newMappings, String... newOrder) throws IOException { - MappingsModificationUtils.modify(oldMappings, newMappings, tree -> - MappingsUtils.reorderNamespaces(tree, Arrays.asList(newOrder))); - } - - private void mergeMappings(Path intermediaryMappings, Path yarnMappings, Path newMergedMappings) { - try { - Command command = new CommandMergeTinyV2(); - runCommand(command, intermediaryMappings.toAbsolutePath().toString(), - yarnMappings.toAbsolutePath().toString(), - newMergedMappings.toAbsolutePath().toString(), - "intermediary", "official"); - } catch (Exception e) { - throw new RuntimeException("Could not merge mappings from " + intermediaryMappings.toString() - + " with mappings from " + yarnMappings, e); - } - } - - private void suggestFieldNames(MinecraftProvider minecraftProvider, Path oldMappings, Path newMappings) { - Command command = new CommandProposeFieldNames(); - runCommand(command, minecraftProvider.getMergedJar().getAbsolutePath(), - oldMappings.toAbsolutePath().toString(), - newMappings.toAbsolutePath().toString()); - } - - private void runCommand(Command command, String... args) { - try { - command.run(args); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private void initFiles() { - baseTinyMappings = mappingsDir.resolve(mappingsName + "-tiny-" + minecraftVersion + "-" + mappingsVersion + "-base"); - } - - public void cleanFiles() { - try { - if (Files.exists(mappingsStepsDir)) { - Files.walkFileTree(mappingsStepsDir, new DeletingFileVisitor()); - } - - if (Files.exists(baseTinyMappings)) { - Files.deleteIfExists(baseTinyMappings); - } - - if (tinyMappings != null) { - tinyMappings.delete(); - } - - if (tinyMappingsJar != null) { - tinyMappingsJar.delete(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Override - public String getTargetConfig() { - 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; - } - - public String getMappingsKey() { - return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + mappingsVersion; - } - - public File getUnpickDefinitionsFile() { - return unpickDefinitionsFile; - } - - public boolean hasUnpickDefinitions() { - return hasUnpickDefinitions; - } - - public record UnpickMetadata(String unpickGroup, String unpickVersion) { - } + File intermediaryTinyFile(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java new file mode 100644 index 00000000..373f6ab5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -0,0 +1,582 @@ +/* + * 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.configuration.providers.mappings; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.net.UrlEscapers; +import com.google.gson.JsonObject; +import org.apache.commons.io.FileUtils; +import org.apache.tools.ant.util.StringUtils; +import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; +import org.zeroturnaround.zip.FileSource; +import org.zeroturnaround.zip.ZipEntrySource; +import org.zeroturnaround.zip.ZipUtil; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.DependencyProvider; +import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; +import net.fabricmc.loom.configuration.processors.JarProcessorManager; +import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.providers.forge.MinecraftPatchedProvider; +import net.fabricmc.loom.configuration.providers.forge.SrgProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.DeletingFileVisitor; +import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.srg.MCPReader; +import net.fabricmc.loom.util.srg.SrgMerger; +import net.fabricmc.loom.util.srg.SrgNamedWriter; +import net.fabricmc.mapping.reader.v2.TinyMetadata; +import net.fabricmc.mapping.reader.v2.TinyV2Factory; +import net.fabricmc.mapping.tree.TinyTree; +import net.fabricmc.mappingio.adapter.MappingNsCompleter; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.Tiny2Reader; +import net.fabricmc.mappingio.format.Tiny2Writer; +import net.fabricmc.mappingio.tree.MemoryMappingTree; +import net.fabricmc.stitch.Command; +import net.fabricmc.stitch.commands.CommandProposeFieldNames; +import net.fabricmc.stitch.commands.tinyv2.TinyFile; +import net.fabricmc.stitch.commands.tinyv2.TinyV2Writer; + +public class MappingsProviderImpl extends DependencyProvider implements MappingsProvider { + public MinecraftMappedProvider mappedProvider; + public MinecraftPatchedProvider patchedProvider; + + public String mappingsName; + public String minecraftVersion; + public String mappingsVersion; + public String removeSuffix; + + protected final Path mappingsDir; + protected Path mappingsVersionedDir; + 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 + public File tinyMappings; + // tinyMappings wrapped in a jar + public File tinyMappingsJar; + public Path tinyMappingsWithSrg; + public File mixinTinyMappingsWithSrg; // FORGE: The mixin mappings have srg names in intermediary. + public File srgToNamedSrg; // FORGE: srg to named in srg file format + + private File unpickDefinitionsFile; + private boolean hasUnpickDefinitions; + private UnpickMetadata unpickMetadata; + + public MappingsProviderImpl(Project project) { + super(project); + mappingsDir = getExtension().getUserCache().toPath().resolve("mappings"); + mappingsStepsDir = mappingsDir.resolve("steps"); + } + + public Path getMappingsVersionedDir() throws IOException { + if (mappingsVersionedDir == null) { + mappingsVersionedDir = mappingsDir.resolve(getExtension().getMinecraftProvider().minecraftVersion()); + + if (!Files.exists(mappingsVersionedDir)) { + Files.createDirectories(mappingsVersionedDir); + } + } + + return mappingsVersionedDir; + } + + public Path getMappedVersionedDir(String name) throws IOException { + Path dir = getMappingsVersionedDir().resolve(name); + + if (!Files.exists(dir)) { + Files.createDirectories(dir); + } + + return dir; + } + + public void clean() throws IOException { + FileUtils.deleteDirectory(mappingsDir.toFile()); + } + + public TinyTree getMappings() throws IOException { + return MappingsCache.INSTANCE.get(tinyMappings.toPath()); + } + + public TinyTree getMappingsWithSrg() throws IOException { + if (getExtension().shouldGenerateSrgTiny()) { + return MappingsCache.INSTANCE.get(tinyMappingsWithSrg); + } + + throw new UnsupportedOperationException("Not running with Forge support / Tiny srg support."); + } + + @Override + public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws Exception { + MinecraftProviderImpl minecraftProvider = getDependencyManager().getProvider(MinecraftProviderImpl.class); + + getProject().getLogger().info(":setting up mappings (" + dependency.getDependency().getName() + " " + dependency.getResolvedVersion() + ")"); + + String version = dependency.getResolvedVersion(); + File mappingsJar = dependency.resolveFile().orElseThrow(() -> new RuntimeException("Could not find yarn mappings: " + dependency)); + + this.mappingsName = StringUtils.removeSuffix(dependency.getDependency().getGroup() + "." + dependency.getDependency().getName(), "-unmerged"); + this.minecraftVersion = minecraftProvider.minecraftVersion(); + + boolean isV2; + + if (isMCP(mappingsJar.toPath())) { + File old = mappingsJar; + mappingsJar = mappingsDir.resolve(StringUtils.removeSuffix(mappingsJar.getName(), ".zip") + "-" + minecraftVersion + ".jar").toFile(); + FileUtils.copyFile(old, mappingsJar); + mappingsName += "-" + minecraftVersion; + } + + // Only do this for official yarn, there isn't really a way we can get the mc version for all mappings + if (dependency.getDependency().getGroup() != null && dependency.getDependency().getGroup().equals("net.fabricmc") && dependency.getDependency().getName().equals("yarn") && dependency.getDependency().getVersion() != null) { + String yarnVersion = dependency.getDependency().getVersion(); + char separator = yarnVersion.contains("+build.") ? '+' : yarnVersion.contains("-") ? '-' : '.'; + String yarnMinecraftVersion = yarnVersion.substring(0, yarnVersion.lastIndexOf(separator)); + + if (!yarnMinecraftVersion.equalsIgnoreCase(minecraftVersion)) { + throw new RuntimeException(String.format("Minecraft Version (%s) does not match yarn's minecraft version (%s)", minecraftVersion, yarnMinecraftVersion)); + } + + // We can save reading the zip file + header by checking the file name + isV2 = mappingsJar.getName().endsWith("-v2.jar") || mappingsJar.getName().endsWith("-mergedv2.jar"); + } else { + isV2 = doesJarContainV2Mappings(mappingsJar.toPath()); + } + + this.removeSuffix = StringUtils.removeSuffix(mappingsJar.getName(), ".jar"); + this.mappingsVersion = version + (isV2 ? "-v2" : ""); + + initFiles(); + + if (isRefreshDeps()) { + cleanFiles(); + } + + Files.createDirectories(mappingsDir); + Files.createDirectories(mappingsStepsDir); + + String[] depStringSplit = dependency.getDepString().split(":"); + String jarClassifier = "final"; + + if (depStringSplit.length >= 4) { + jarClassifier = jarClassifier + depStringSplit[3]; + } + + Path mappedVersionedDir = getMappedVersionedDir(removeSuffix); + tinyMappings = mappedVersionedDir.resolve("mappings.tiny").toFile(); + unpickDefinitionsFile = mappedVersionedDir.resolve("definitions.unpick").toFile(); + tinyMappingsJar = new File(getExtension().getUserCache(), removeSuffix + "-" + jarClassifier + ".jar"); + tinyMappingsWithSrg = mappedVersionedDir.resolve("mappings-srg.tiny"); + mixinTinyMappingsWithSrg = mappedVersionedDir.resolve("mappings-mixin-srg.tiny").toFile(); + srgToNamedSrg = mappedVersionedDir.resolve("mappings-srg-named.srg").toFile(); + + if (!tinyMappings.exists() || isRefreshDeps()) { + storeMappings(getProject(), minecraftProvider, mappingsJar.toPath(), postPopulationScheduler); + } else { + try (FileSystem fileSystem = FileSystems.newFileSystem(mappingsJar.toPath(), (ClassLoader) null)) { + extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); + } + } + + if (getExtension().isForge()) { + patchedProvider = new MinecraftPatchedProvider(getProject()); + patchedProvider.provide(dependency, postPopulationScheduler); + } + + manipulateMappings(mappingsJar.toPath()); + + if (getExtension().shouldGenerateSrgTiny()) { + if (Files.notExists(tinyMappingsWithSrg) || isRefreshDeps()) { + // Merge tiny mappings with srg + SrgMerger.mergeSrg(getExtension().getSrgProvider().getSrg().toPath(), tinyMappings.toPath(), tinyMappingsWithSrg, true); + } + } + + if (!tinyMappingsJar.exists() || isRefreshDeps()) { + ZipUtil.pack(new ZipEntrySource[] {new FileSource("mappings/mappings.tiny", tinyMappings)}, tinyMappingsJar); + } + + if (hasUnpickDefinitions()) { + String notation = String.format("%s:%s:%s:constants", + dependency.getDependency().getGroup(), + dependency.getDependency().getName(), + dependency.getDependency().getVersion() + ); + + getProject().getDependencies().add(Constants.Configurations.MAPPING_CONSTANTS, notation); + populateUnpickClasspath(); + } + + if (getExtension().isForge()) { + if (!getExtension().shouldGenerateSrgTiny()) { + throw new IllegalStateException("We have to generate srg tiny in a forge environment!"); + } + + if (!mixinTinyMappingsWithSrg.exists() || isRefreshDeps()) { + List lines = new ArrayList<>(Files.readAllLines(tinyMappingsWithSrg)); + lines.set(0, lines.get(0).replace("intermediary", "yraidemretni").replace("srg", "intermediary")); + Files.deleteIfExists(mixinTinyMappingsWithSrg.toPath()); + Files.write(mixinTinyMappingsWithSrg.toPath(), lines); + } + + if (!srgToNamedSrg.exists() || isRefreshDeps()) { + SrgNamedWriter.writeTo(getProject().getLogger(), srgToNamedSrg.toPath(), getMappingsWithSrg(), "srg", "named"); + } + } + + addDependency(tinyMappingsJar, Constants.Configurations.MAPPINGS_FINAL); + + LoomGradleExtension extension = getExtension(); + + if (extension.accessWidener != null) { + extension.addJarProcessor(new AccessWidenerJarProcessor(getProject())); + } + + JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors()); + extension.setJarProcessorManager(processorManager); + processorManager.setupProcessors(); + + if (extension.isForge()) { + patchedProvider.finishProvide(); + } + + if (processorManager.active() || (extension.isForge() && patchedProvider.usesProjectCache())) { + mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager); + getProject().getLogger().lifecycle("Using project based jar storage"); + } else { + mappedProvider = new MinecraftMappedProvider(getProject()); + } + + mappedProvider.initFiles(minecraftProvider, this); + mappedProvider.provide(dependency, postPopulationScheduler); + } + + public void manipulateMappings(Path mappingsJar) throws IOException { } + + private void storeMappings(Project project, MinecraftProviderImpl minecraftProvider, Path yarnJar, Consumer postPopulationScheduler) + throws Exception { + project.getLogger().info(":extracting " + yarnJar.getFileName()); + + if (isMCP(yarnJar)) { + readAndMergeMCP(yarnJar, postPopulationScheduler); + return; + } + + try (FileSystem fileSystem = FileSystems.newFileSystem(yarnJar, (ClassLoader) null)) { + extractMappings(fileSystem, baseTinyMappings); + extractUnpickDefinitions(fileSystem, unpickDefinitionsFile.toPath()); + } + + if (baseMappingsAreMergedV2()) { + // Architectury Loom Patch + // If a merged tiny v2 mappings file is provided + // Skip merging, should save a lot of time + Files.copy(baseTinyMappings, tinyMappings.toPath(), StandardCopyOption.REPLACE_EXISTING); + } else if (baseMappingsAreV2()) { + // These are unmerged v2 mappings + mergeAndSaveMappings(project, yarnJar); + } else { + // These are merged v1 mappings + if (tinyMappings.exists()) { + tinyMappings.delete(); + } + + project.getLogger().lifecycle(":populating field names"); + suggestFieldNames(minecraftProvider, baseTinyMappings, tinyMappings.toPath()); + } + } + + private void readAndMergeMCP(Path mcpJar, Consumer postPopulationScheduler) throws Exception { + Path intermediaryTinyPath = getIntermediaryTiny(); + SrgProvider provider = getExtension().getSrgProvider(); + + if (provider == null) { + if (!getExtension().shouldGenerateSrgTiny()) { + Configuration srg = getProject().getConfigurations().maybeCreate(Constants.Configurations.SRG); + srg.setTransitive(false); + } + + provider = new SrgProvider(getProject()); + getProject().getDependencies().add(provider.getTargetConfig(), "de.oceanlabs.mcp:mcp_config:" + minecraftVersion); + Configuration configuration = getProject().getConfigurations().getByName(provider.getTargetConfig()); + provider.provide(DependencyInfo.create(getProject(), configuration.getDependencies().iterator().next(), configuration), postPopulationScheduler); + } + + Path srgPath = provider.getSrg().toPath(); + + TinyFile file = new MCPReader(intermediaryTinyPath, srgPath).read(mcpJar); + TinyV2Writer.write(file, tinyMappings.toPath()); + } + + private boolean isMCP(Path path) throws IOException { + try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { + return Files.exists(fs.getPath("fields.csv")) && Files.exists(fs.getPath("methods.csv")); + } + } + + private boolean baseMappingsAreV2() throws IOException { + try (BufferedReader reader = Files.newBufferedReader(baseTinyMappings)) { + TinyV2Factory.readMetadata(reader); + return true; + } catch (IllegalArgumentException | NoSuchFileException e) { + // TODO: just check the mappings version when Parser supports V1 in readMetadata() + return false; + } + } + + private boolean baseMappingsAreMergedV2() throws IOException { + try (BufferedReader reader = Files.newBufferedReader(baseTinyMappings)) { + TinyMetadata metadata = TinyV2Factory.readMetadata(reader); + return metadata.getNamespaces().containsAll(Arrays.asList("named", "intermediary", "official")); + } catch (IllegalArgumentException | NoSuchFileException e) { + return false; + } + } + + private boolean doesJarContainV2Mappings(Path path) throws IOException { + try (FileSystem fs = FileSystems.newFileSystem(path, (ClassLoader) null)) { + try (BufferedReader reader = Files.newBufferedReader(fs.getPath("mappings", "mappings.tiny"))) { + TinyV2Factory.readMetadata(reader); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } + } + + public static void extractMappings(FileSystem jar, Path extractTo) throws IOException { + Files.copy(jar.getPath("mappings/mappings.tiny"), extractTo, StandardCopyOption.REPLACE_EXISTING); + } + + private void extractUnpickDefinitions(FileSystem jar, Path extractTo) throws IOException { + Path unpickPath = jar.getPath("extras/definitions.unpick"); + Path unpickMetadataPath = jar.getPath("extras/unpick.json"); + + if (!Files.exists(unpickPath) || !Files.exists(unpickMetadataPath)) { + return; + } + + Files.copy(unpickPath, extractTo, StandardCopyOption.REPLACE_EXISTING); + + unpickMetadata = parseUnpickMetadata(unpickMetadataPath); + hasUnpickDefinitions = true; + } + + private UnpickMetadata parseUnpickMetadata(Path input) throws IOException { + JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(Files.readString(input), JsonObject.class); + + if (!jsonObject.has("version") || jsonObject.get("version").getAsInt() != 1) { + throw new UnsupportedOperationException("Unsupported unpick version"); + } + + return new UnpickMetadata( + jsonObject.get("unpickGroup").getAsString(), + jsonObject.get("unpickVersion").getAsString() + ); + } + + private void populateUnpickClasspath() { + String unpickCliName = "unpick-cli"; + getProject().getDependencies().add(Constants.Configurations.UNPICK_CLASSPATH, + String.format("%s:%s:%s", unpickMetadata.unpickGroup, unpickCliName, unpickMetadata.unpickVersion) + ); + } + + private void extractIntermediary(Path intermediaryJar, Path intermediaryTiny) throws IOException { + getProject().getLogger().info(":extracting " + intermediaryJar.getFileName()); + + 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()); + + try (FileSystem unmergedYarnJarFs = FileSystems.newFileSystem(unmergedYarnJar, (ClassLoader) null)) { + extractMappings(unmergedYarnJarFs, unmergedYarn); + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + project.getLogger().info(":merging mappings"); + MemoryMappingTree tree = new MemoryMappingTree(); + MappingNsCompleter nsCompleter = new MappingNsCompleter(tree, Collections.singletonMap(MappingNamespace.NAMED.stringValue(), MappingNamespace.INTERMEDIARY.stringValue()), true); + + try (BufferedReader reader = Files.newBufferedReader(getIntermediaryTiny(), StandardCharsets.UTF_8)) { + Tiny2Reader.read(reader, nsCompleter); + } + + MemoryMappingTree tempTree = new MemoryMappingTree(); + + MappingSourceNsSwitch sourceNsSwitch = new MappingSourceNsSwitch(tempTree, MappingNamespace.OFFICIAL.stringValue()); + tree.accept(sourceNsSwitch); + tree = tempTree; + + try (BufferedReader reader = Files.newBufferedReader(unmergedYarn, StandardCharsets.UTF_8)) { + Tiny2Reader.read(reader, tree); + } + + try (Tiny2Writer writer = new Tiny2Writer(Files.newBufferedWriter(tinyMappings.toPath(), StandardCharsets.UTF_8), false)) { + tree.accept(writer); + } + + project.getLogger().info(":merged mappings in " + stopwatch.stop()); + } + + private void suggestFieldNames(MinecraftProviderImpl minecraftProvider, Path oldMappings, Path newMappings) { + Command command = new CommandProposeFieldNames(); + runCommand(command, minecraftProvider.getMergedJar().getAbsolutePath(), + oldMappings.toAbsolutePath().toString(), + newMappings.toAbsolutePath().toString()); + } + + private void runCommand(Command command, String... args) { + try { + command.run(args); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void initFiles() throws IOException { + baseTinyMappings = getMappedVersionedDir(removeSuffix).resolve("mappings-base.tiny"); + + if (Files.exists(mappingsStepsDir)) { + Files.walkFileTree(mappingsStepsDir, new DeletingFileVisitor()); + } + } + + public void cleanFiles() { + try { + if (Files.exists(mappingsStepsDir)) { + Files.walkFileTree(mappingsStepsDir, new DeletingFileVisitor()); + } + + if (Files.exists(baseTinyMappings)) { + Files.deleteIfExists(baseTinyMappings); + } + + if (tinyMappings != null) { + tinyMappings.delete(); + } + + if (tinyMappingsJar != null) { + tinyMappingsJar.delete(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public String getTargetConfig() { + return Constants.Configurations.MAPPINGS; + } + + @Override + public Path getMappingsDir() { + return mappingsDir; + } + + public Path getIntermediaryTiny() throws IOException { + if (intermediaryTiny == null) { + minecraftVersion = getExtension().getMinecraftProvider().minecraftVersion(); + Preconditions.checkNotNull(minecraftVersion, "Minecraft version cannot be null"); + + intermediaryTiny = mappingsDir.resolve(String.format("intermediary-%s-v2.tiny", minecraftVersion)); + + if (isRefreshDeps() && !hasRefreshed) { + Files.deleteIfExists(intermediaryTiny); + } + + if (!Files.exists(intermediaryTiny)) { + hasRefreshed = true; + intermediaryTiny = getMappingsVersionedDir().resolve("intermediary-v2.tiny"); + + // Download and extract intermediary + String encodedMinecraftVersion = UrlEscapers.urlFragmentEscaper().escape(minecraftVersion); + String intermediaryArtifactUrl = getExtension().getIntermediaryUrl().apply(encodedMinecraftVersion); + Path intermediaryJar = getMappingsVersionedDir().resolve("intermediary-v2.jar"); + DownloadUtil.downloadIfChanged(new URL(intermediaryArtifactUrl), intermediaryJar.toFile(), getProject().getLogger()); + + extractIntermediary(intermediaryJar, intermediaryTiny); + } + } + + return intermediaryTiny; + } + + public String getMappingsKey() { + return mappingsName + "." + minecraftVersion.replace(' ', '_').replace('.', '_').replace('-', '_') + "." + mappingsVersion; + } + + public File getUnpickDefinitionsFile() { + return unpickDefinitionsFile; + } + + public boolean hasUnpickDefinitions() { + return hasUnpickDefinitions; + } + + @Override + public File intermediaryTinyFile() { + try { + return getIntermediaryTiny().toFile(); + } catch (IOException e) { + throw new RuntimeException("Failed to get intermediary", e); + } + } + + public record UnpickMetadata(String unpickGroup, String unpickVersion) { + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsSpec.java new file mode 100644 index 00000000..1666d95c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsSpec.java @@ -0,0 +1,29 @@ +/* + * 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.configuration.providers.mappings; + +public interface MappingsSpec { + L createLayer(MappingContext context); +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java deleted file mode 100644 index 6e45d2b9..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MojangMappingsDependency.java +++ /dev/null @@ -1,337 +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.configuration.providers.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.Action; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.ExternalModuleDependency; -import org.gradle.api.artifacts.ModuleIdentifier; -import org.gradle.api.artifacts.ModuleVersionIdentifier; -import org.gradle.api.artifacts.MutableVersionConstraint; -import org.gradle.api.artifacts.SelfResolvingDependency; -import org.gradle.api.artifacts.VersionConstraint; -import org.gradle.api.internal.artifacts.DefaultModuleIdentifier; -import org.gradle.api.internal.artifacts.ModuleVersionSelectorStrictSpec; -import org.gradle.api.internal.artifacts.dependencies.AbstractModuleDependency; -import org.gradle.api.internal.artifacts.dependencies.DefaultMutableVersionConstraint; -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.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; -import net.fabricmc.loom.util.HashedDownloadUtil; -import net.fabricmc.lorenztiny.TinyMappingsReader; -import net.fabricmc.mapping.tree.TinyMappingFactory; - -public class MojangMappingsDependency extends AbstractModuleDependency implements SelfResolvingDependency, ExternalModuleDependency { - 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; - - private boolean changing; - private boolean force; - - public MojangMappingsDependency(Project project, LoomGradleExtension extension) { - super(null); - this.project = project; - this.extension = extension; - } - - @Override - public ExternalModuleDependency copy() { - MojangMappingsDependency copiedProjectDependency = new MojangMappingsDependency(project, extension); - this.copyTo(copiedProjectDependency); - return copiedProjectDependency; - } - - @Override - public void version(Action action) { - } - - @Override - public boolean isForce() { - return this.force; - } - - @Override - public ExternalModuleDependency setForce(boolean force) { - this.validateMutation(this.force, force); - this.force = force; - return this; - } - - @Override - public boolean isChanging() { - return this.changing; - } - - @Override - public ExternalModuleDependency setChanging(boolean changing) { - this.validateMutation(this.changing, changing); - this.changing = changing; - return this; - } - - @Override - public Set 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) || LoomGradlePlugin.refreshDeps) { - 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); - } - } - - if (!extension.isSilentMojangMappingsLicenseEnabled()) { - 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 { - MinecraftVersionMeta versionInfo = extension.getMinecraftProvider().getVersionInfo(); - - if (versionInfo.download(MANIFEST_CLIENT_MAPPINGS) == null) { - throw new RuntimeException("Failed to find official mojang mappings for " + getVersion()); - } - - MinecraftVersionMeta.Download clientMappingsDownload = versionInfo.download(MANIFEST_CLIENT_MAPPINGS); - MinecraftVersionMeta.Download serverMappingsDownload = versionInfo.download(MANIFEST_CLIENT_MAPPINGS); - - HashedDownloadUtil.downloadIfInvalid(new URL(clientMappingsDownload.url()), clientMappings.toFile(), clientMappingsDownload.sha1(), project.getLogger(), false); - HashedDownloadUtil.downloadIfInvalid(new URL(serverMappingsDownload.url()), serverMappings.toFile(), clientMappingsDownload.sha1(), project.getLogger(), false); - - 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 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() { - if (extension.getDependencyManager() == null) return "1.0.0"; - return extension.getMinecraftProvider().getMinecraftVersion(); - } - - @Override - public VersionConstraint getVersionConstraint() { - return new DefaultMutableVersionConstraint(getVersion()); - } - - @Override - public boolean matchesStrictly(ModuleVersionIdentifier identifier) { - return (new ModuleVersionSelectorStrictSpec(this)).isSatisfiedBy(identifier); - } - - @Override - public ModuleIdentifier getModule() { - return DefaultModuleIdentifier.newId(GROUP, MODULE); - } - - @Override - public boolean contentEquals(Dependency dependency) { - if (dependency instanceof MojangMappingsDependency mojangMappingsDependency) { - return mojangMappingsDependency.extension.getMinecraftProvider().getMinecraftVersion().equals(getVersion()); - } - - return false; - } - - @Override - public String getReason() { - return null; - } - - @Override - public void because(String s) { - } - - private static void iterateClasses(MappingSet mappings, Consumer> consumer) { - for (TopLevelClassMapping classMapping : mappings.getTopLevelClassMappings()) { - iterateClass(classMapping, consumer); - } - } - - private static void iterateClass(ClassMapping classMapping, Consumer> 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()); - } - }); - } - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingLayer.java new file mode 100644 index 00000000..65fa8bd4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingLayer.java @@ -0,0 +1,92 @@ +package net.fabricmc.loom.configuration.providers.mappings.crane; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Collections; + +import dev.architectury.mappingslayers.api.mutable.MutableClassDef; +import dev.architectury.mappingslayers.api.mutable.MutableFieldDef; +import dev.architectury.mappingslayers.api.mutable.MutableMethodDef; +import dev.architectury.mappingslayers.api.mutable.MutableParameterDef; +import dev.architectury.mappingslayers.api.mutable.MutableTinyTree; +import dev.architectury.mappingslayers.api.utils.MappingsUtils; +import org.apache.commons.io.IOUtils; + +import net.fabricmc.loom.configuration.providers.mappings.MappingLayer; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingVisitor; + +public record CraneMappingLayer(File craneJar) implements MappingLayer { + private static final String TINY_FILE_NAME = "crane.tiny"; + + @Override + public void visit(MappingVisitor visitor) throws IOException { + try (FileSystemUtil.FileSystemDelegate fs = FileSystemUtil.getJarFileSystem(craneJar().toPath(), false)) { + try (BufferedReader reader = Files.newBufferedReader(fs.get().getPath(TINY_FILE_NAME), StandardCharsets.UTF_8)) { + // can't use this, it requires 2 namespaces + // Tiny2Reader.read(reader, mappingVisitor); + MutableTinyTree tree = MappingsUtils.deserializeFromString(IOUtils.toString(reader)); + + do { + if (visitor.visitHeader()) { + visitor.visitNamespaces(tree.getMetadata().getNamespaces().get(0), Collections.emptyList()); + } + + if (visitor.visitContent()) { + for (MutableClassDef classDef : tree.getClassesMutable()) { + if (visitor.visitClass(classDef.getName(0))) { + if (!visitor.visitElementContent(MappedElementKind.CLASS)) { + return; + } + + for (MutableFieldDef fieldDef : classDef.getFieldsMutable()) { + if (visitor.visitField(fieldDef.getName(0), fieldDef.getDescriptor(0))) { + if (!visitor.visitElementContent(MappedElementKind.FIELD)) { + return; + } + + if (fieldDef.getComment() != null) { + visitor.visitComment(MappedElementKind.FIELD, fieldDef.getComment()); + } + } + } + + for (MutableMethodDef methodDef : classDef.getMethodsMutable()) { + if (visitor.visitMethod(methodDef.getName(0), methodDef.getDescriptor(0))) { + if (!visitor.visitElementContent(MappedElementKind.METHOD)) { + return; + } + + for (MutableParameterDef parameterDef : methodDef.getParametersMutable()) { + if (visitor.visitMethodArg(parameterDef.getLocalVariableIndex(), parameterDef.getLocalVariableIndex(), parameterDef.getName(0))) { + if (!visitor.visitElementContent(MappedElementKind.METHOD_ARG)) { + return; + } + + if (parameterDef.getComment() != null) { + visitor.visitComment(MappedElementKind.METHOD_ARG, parameterDef.getComment()); + } + } + } + + if (methodDef.getComment() != null) { + visitor.visitComment(MappedElementKind.METHOD, methodDef.getComment()); + } + } + } + + if (classDef.getComment() != null) { + visitor.visitComment(MappedElementKind.FIELD, classDef.getComment()); + } + } + } + } + } while (!visitor.visitEnd()); + } + } + } +} diff --git a/src/forgeInject/java/net/fabricmc/loom/inject/Pair.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingsSpec.java similarity index 73% rename from src/forgeInject/java/net/fabricmc/loom/inject/Pair.java rename to src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingsSpec.java index 0206cb5d..179bcc92 100644 --- a/src/forgeInject/java/net/fabricmc/loom/inject/Pair.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/crane/CraneMappingsSpec.java @@ -22,31 +22,14 @@ * SOFTWARE. */ -package net.fabricmc.loom.inject; +package net.fabricmc.loom.configuration.providers.mappings.crane; -import java.util.Map; - -final class Pair implements Map.Entry { - private final A first; - private final B second; - - Pair(A first, B second) { - this.first = first; - this.second = second; - } +import net.fabricmc.loom.configuration.providers.mappings.MappingContext; +import net.fabricmc.loom.configuration.providers.mappings.MappingsSpec; +public record CraneMappingsSpec(String mavenNotation) implements MappingsSpec { @Override - public A getKey() { - return first; - } - - @Override - public B getValue() { - return second; - } - - @Override - public B setValue(B value) { - throw new UnsupportedOperationException("Pairs are immutable!"); + public CraneMappingLayer createLayer(MappingContext context) { + return new CraneMappingLayer(context.mavenFile(mavenNotation())); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingLayer.java new file mode 100644 index 00000000..e7fe3938 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingLayer.java @@ -0,0 +1,55 @@ +/* + * 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.configuration.providers.mappings.intermediary; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Collections; + +import net.fabricmc.loom.configuration.providers.mappings.MappingLayer; +import net.fabricmc.loom.configuration.providers.mappings.MappingNamespace; +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.adapter.MappingNsCompleter; +import net.fabricmc.mappingio.format.Tiny2Reader; + +public record IntermediaryMappingLayer(File tinyFile) implements MappingLayer { + @Override + public MappingNamespace getSourceNamespace() { + return MappingNamespace.OFFICIAL; + } + + @Override + public void visit(MappingVisitor mappingVisitor) throws IOException { + // Populate named with intermediary and add Add a "named" namespace + MappingNsCompleter nsCompleter = new MappingNsCompleter(mappingVisitor, Collections.singletonMap(MappingNamespace.NAMED.stringValue(), MappingNamespace.INTERMEDIARY.stringValue()), true); + + try (BufferedReader reader = Files.newBufferedReader(tinyFile().toPath(), StandardCharsets.UTF_8)) { + Tiny2Reader.read(reader, nsCompleter); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingsSpec.java new file mode 100644 index 00000000..e4c71fda --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/intermediary/IntermediaryMappingsSpec.java @@ -0,0 +1,35 @@ +/* + * 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.configuration.providers.mappings.intermediary; + +import net.fabricmc.loom.configuration.providers.mappings.MappingContext; +import net.fabricmc.loom.configuration.providers.mappings.MappingsSpec; + +public record IntermediaryMappingsSpec() implements MappingsSpec { + @Override + public IntermediaryMappingLayer createLayer(MappingContext context) { + return new IntermediaryMappingLayer(context.mappingsProvider().intermediaryTinyFile()); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java new file mode 100644 index 00000000..835dc910 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingLayer.java @@ -0,0 +1,105 @@ +/* + * 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.configuration.providers.mappings.mojmap; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.gradle.api.logging.Logger; + +import net.fabricmc.loom.configuration.providers.mappings.MappingLayer; +import net.fabricmc.loom.configuration.providers.mappings.MappingNamespace; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingLayer; +import net.fabricmc.loom.util.HashedDownloadUtil; +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch; +import net.fabricmc.mappingio.format.ProGuardReader; + +public record MojangMappingLayer(MinecraftVersionMeta.Download clientDownload, + MinecraftVersionMeta.Download serverDownload, + File workingDir, + Logger logger, + MojangMappingsSpec.SilenceLicenseOption silenceLicense) implements MappingLayer { + @Override + public void visit(MappingVisitor mappingVisitor) throws IOException { + var clientMappings = new File(workingDir(), "client.txt"); + var serverMappings = new File(workingDir(), "server.txt"); + + download(clientMappings, serverMappings); + + if (!silenceLicense.isSilent()) { + printMappingsLicense(clientMappings.toPath()); + } + + // Make official the source namespace + MappingSourceNsSwitch nsSwitch = new MappingSourceNsSwitch(mappingVisitor, MappingNamespace.OFFICIAL.stringValue()); + + try (BufferedReader clientBufferedReader = Files.newBufferedReader(clientMappings.toPath(), StandardCharsets.UTF_8); + BufferedReader serverBufferedReader = Files.newBufferedReader(serverMappings.toPath(), StandardCharsets.UTF_8)) { + ProGuardReader.read(clientBufferedReader, MappingNamespace.NAMED.stringValue(), MappingNamespace.OFFICIAL.stringValue(), nsSwitch); + ProGuardReader.read(serverBufferedReader, MappingNamespace.NAMED.stringValue(), MappingNamespace.OFFICIAL.stringValue(), nsSwitch); + } + } + + private void download(File clientMappings, File serverMappings) throws IOException { + HashedDownloadUtil.downloadIfInvalid(new URL(clientDownload().url()), clientMappings, clientDownload().sha1(), logger(), false); + HashedDownloadUtil.downloadIfInvalid(new URL(serverDownload().url()), serverMappings, serverDownload().sha1(), logger(), false); + } + + private void printMappingsLicense(Path clientMappings) { + try (BufferedReader clientBufferedReader = Files.newBufferedReader(clientMappings, StandardCharsets.UTF_8)) { + logger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + logger().warn("Using of the official minecraft mappings is at your own risk!"); + logger().warn("Please make sure to read and understand the following license:"); + logger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + String line; + + while ((line = clientBufferedReader.readLine()).startsWith("#")) { + logger().warn(line); + } + + logger().warn("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + } catch (IOException e) { + throw new RuntimeException("Failed to read client mappings", e); + } + } + + @Override + public MappingNamespace getSourceNamespace() { + return MappingNamespace.OFFICIAL; + } + + @Override + public List> dependsOn() { + return List.of(IntermediaryMappingLayer.class); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java new file mode 100644 index 00000000..4fa87d7c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/mojmap/MojangMappingsSpec.java @@ -0,0 +1,88 @@ +/* + * 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.configuration.providers.mappings.mojmap; + +import net.fabricmc.loom.configuration.providers.mappings.MappingContext; +import net.fabricmc.loom.configuration.providers.mappings.MappingsSpec; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; + +public record MojangMappingsSpec(SilenceLicenseOption silenceLicense) implements MappingsSpec { + // Keys in dependency manifest + private static final String MANIFEST_CLIENT_MAPPINGS = "client_mappings"; + private static final String MANIFEST_SERVER_MAPPINGS = "server_mappings"; + + public MojangMappingsSpec(SilenceLicenseSupplier supplier) { + this(new SilenceLicenseOption(supplier)); + } + + public MojangMappingsSpec() { + this(() -> false); + } + + @FunctionalInterface + public interface SilenceLicenseSupplier { + boolean isSilent(); + } + + public record SilenceLicenseOption(SilenceLicenseSupplier supplier) { + public boolean isSilent() { + return supplier.isSilent(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SilenceLicenseOption that)) return false; + return isSilent() == that.isSilent(); + } + + @Override + public int hashCode() { + return Boolean.hashCode(isSilent()); + } + + @Override + public String toString() { + return isSilent() + ""; + } + } + + @Override + public MojangMappingLayer createLayer(MappingContext context) { + MinecraftVersionMeta versionInfo = context.minecraftProvider().getVersionInfo(); + + if (versionInfo.download(MANIFEST_CLIENT_MAPPINGS) == null) { + throw new RuntimeException("Failed to find official mojang mappings for " + context.minecraftVersion()); + } + + return new MojangMappingLayer( + versionInfo.download(MANIFEST_CLIENT_MAPPINGS), + versionInfo.download(MANIFEST_SERVER_MAPPINGS), + context.workingDirectory("mojang"), + context.getLogger(), + silenceLicense() + ); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java new file mode 100644 index 00000000..70764234 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingLayer.java @@ -0,0 +1,63 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.mappings.parchment; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Objects; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.providers.mappings.MappingLayer; +import net.fabricmc.loom.configuration.providers.mappings.MappingNamespace; +import net.fabricmc.mappingio.MappingVisitor; + +public record ParchmentMappingLayer(File parchmentFile, boolean removePrefix) implements MappingLayer { + private static final String PARCHMENT_DATA_FILE_NAME = "parchment.json"; + + @Override + public void visit(MappingVisitor mappingVisitor) throws IOException { + ParchmentTreeV1 parchmentData = getParchmentData(); + + if (removePrefix()) { + mappingVisitor = new ParchmentPrefixStripingMappingVisitor(mappingVisitor); + } + + parchmentData.visit(mappingVisitor, MappingNamespace.NAMED.stringValue()); + } + + private ParchmentTreeV1 getParchmentData() throws IOException { + try (var zipFile = new ZipFile(parchmentFile())) { + ZipEntry zipFileEntry = zipFile.getEntry(PARCHMENT_DATA_FILE_NAME); + Objects.requireNonNull(zipFileEntry, "Could not find %s in parchment data file".formatted(PARCHMENT_DATA_FILE_NAME)); + + try (var reader = new InputStreamReader(zipFile.getInputStream(zipFileEntry))) { + return LoomGradlePlugin.OBJECT_MAPPER.readValue(reader, ParchmentTreeV1.class); + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpec.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpec.java new file mode 100644 index 00000000..efec61c2 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpec.java @@ -0,0 +1,35 @@ +/* + * 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.configuration.providers.mappings.parchment; + +import net.fabricmc.loom.configuration.providers.mappings.MappingContext; +import net.fabricmc.loom.configuration.providers.mappings.MappingsSpec; + +public record ParchmentMappingsSpec(String mavenNotation, boolean removePrefix) implements MappingsSpec { + @Override + public ParchmentMappingLayer createLayer(MappingContext context) { + return new ParchmentMappingLayer(context.mavenFile(mavenNotation()), removePrefix()); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpecBuilder.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpecBuilder.java new file mode 100644 index 00000000..808e077c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentMappingsSpecBuilder.java @@ -0,0 +1,48 @@ +/* + * 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.configuration.providers.mappings.parchment; + +public class ParchmentMappingsSpecBuilder { + private final String mavenNotation; + + private boolean removePrefix; + + private ParchmentMappingsSpecBuilder(String mavenNotation) { + this.mavenNotation = mavenNotation; + } + + public static ParchmentMappingsSpecBuilder builder(String depNotation) { + return new ParchmentMappingsSpecBuilder(depNotation); + } + + public ParchmentMappingsSpecBuilder setRemovePrefix(boolean removePrefix) { + this.removePrefix = removePrefix; + return this; + } + + public ParchmentMappingsSpec build() { + return new ParchmentMappingsSpec(mavenNotation, removePrefix); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentPrefixStripingMappingVisitor.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentPrefixStripingMappingVisitor.java new file mode 100644 index 00000000..6280a5f0 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentPrefixStripingMappingVisitor.java @@ -0,0 +1,50 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.mappings.parchment; + +import java.util.Locale; + +import net.fabricmc.mappingio.MappingVisitor; +import net.fabricmc.mappingio.adapter.ForwardingMappingVisitor; + +public final class ParchmentPrefixStripingMappingVisitor extends ForwardingMappingVisitor { + protected ParchmentPrefixStripingMappingVisitor(MappingVisitor next) { + super(next); + } + + @Override + public boolean visitMethodArg(int argPosition, int lvIndex, String srcName) { + return super.visitMethodArg(argPosition, lvIndex, stripMethodArg(srcName)); + } + + public static String stripMethodArg(String arg) { + if (arg.length() > 1 && arg.startsWith("p") && Character.isUpperCase(arg.charAt(1))) { + String a2 = arg.substring(1); // Remove p + return a2.substring(0, 1).toLowerCase(Locale.ROOT) + a2.substring(1); // Make first char lowercase + } + + return arg; + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentTreeV1.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentTreeV1.java new file mode 100644 index 00000000..6d9c1a6c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/parchment/ParchmentTreeV1.java @@ -0,0 +1,165 @@ +/* + * 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.configuration.providers.mappings.parchment; + +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.mappingio.MappedElementKind; +import net.fabricmc.mappingio.MappingVisitor; + +public record ParchmentTreeV1( + String version, + @Nullable + List classes, + @Nullable + List packages +) { + public void visit(MappingVisitor visitor, String srcNamespace) { + while (true) { + if (visitor.visitHeader()) { + visitor.visitNamespaces(srcNamespace, Collections.emptyList()); + } + + if (visitor.visitContent()) { + if (classes() != null) { + for (Class c : classes()) { + c.visit(visitor); + } + } + } + + if (visitor.visitEnd()) { + break; + } + } + } + + public record Class( + String name, + @Nullable + List fields, + @Nullable + List methods, + @Nullable + List javadoc + ) { + public void visit(MappingVisitor visitor) { + if (visitor.visitClass(name())) { + if (!visitor.visitElementContent(MappedElementKind.CLASS)) { + return; + } + + if (fields() != null) { + for (Field field : fields()) { + field.visit(visitor); + } + } + + if (methods() != null) { + for (Method method : methods()) { + method.visit(visitor); + } + } + + if (javadoc() != null) { + visitor.visitComment(MappedElementKind.CLASS, String.join("\n", javadoc())); + } + } + } + } + + public record Field( + String name, + String descriptor, + @Nullable + List javadoc + ) { + public void visit(MappingVisitor visitor) { + if (visitor.visitField(name, descriptor)) { + if (!visitor.visitElementContent(MappedElementKind.FIELD)) { + return; + } + + if (javadoc() != null) { + visitor.visitComment(MappedElementKind.FIELD, String.join("\n", javadoc())); + } + } + } + } + + public record Method( + String name, + String descriptor, + @Nullable + List parameters, + @Nullable + List javadoc + ) { + public void visit(MappingVisitor visitor) { + if (visitor.visitMethod(name, descriptor)) { + if (!visitor.visitElementContent(MappedElementKind.METHOD)) { + return; + } + + if (parameters() != null) { + for (Parameter parameter : parameters()) { + parameter.visit(visitor); + } + } + + if (javadoc() != null) { + visitor.visitComment(MappedElementKind.METHOD, String.join("\n", javadoc())); + } + } + } + } + + public record Parameter( + int index, + String name, + @Nullable + String javadoc + ) { + public void visit(MappingVisitor visitor) { + if (visitor.visitMethodArg(index, index, name)) { + if (!visitor.visitElementContent(MappedElementKind.METHOD_ARG)) { + return; + } + + if (javadoc() != null) { + visitor.visitComment(MappedElementKind.METHOD_ARG, javadoc); + } + } + } + } + + public record Package( + String name, + List javadoc + ) { } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java index 130df4d8..bfb42d74 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftLibraryProvider.java @@ -29,13 +29,13 @@ import java.io.File; import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.util.Constants; public class MinecraftLibraryProvider { public File MINECRAFT_LIBS; - public void provide(MinecraftProvider minecraftProvider, Project project) { + public void provide(MinecraftProviderImpl minecraftProvider, Project project) { MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); initFiles(project, minecraftProvider); @@ -47,7 +47,7 @@ public class MinecraftLibraryProvider { } } - private void initFiles(Project project, MinecraftProvider minecraftProvider) { + private void initFiles(Project project, MinecraftProviderImpl minecraftProvider) { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); MINECRAFT_LIBS = new File(extension.getUserCache(), "libraries"); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java index 14333000..7b935ccf 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftMappedProvider.java @@ -54,8 +54,8 @@ import org.gradle.api.Project; import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.configuration.DependencyProvider; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.tr.OutputRemappingHandler; import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.util.Constants; @@ -81,7 +81,7 @@ public class MinecraftMappedProvider extends DependencyProvider { private File minecraftIntermediaryJar; private File minecraftSrgJar; - private MinecraftProvider minecraftProvider; + private MinecraftProviderImpl minecraftProvider; public MinecraftMappedProvider(Project project) { super(project); @@ -151,7 +151,7 @@ public class MinecraftMappedProvider extends DependencyProvider { private void mapMinecraftJar() throws Exception { String fromM = "official"; - MappingsProvider mappingsProvider = getExtension().getMappingsProvider(); + MappingsProviderImpl mappingsProvider = getExtension().getMappingsProvider(); Path input = inputJar.toPath(); Path outputMapped = minecraftMappedJar.toPath(); @@ -253,6 +253,7 @@ public class MinecraftMappedProvider extends DependencyProvider { public TinyRemapper getTinyRemapper() throws IOException { TinyRemapper.Builder builder = TinyRemapper.newRemapper() .renameInvalidLocals(true) + .logUnknownInvokeDynamic(false) .ignoreConflicts(getExtension().isForge()) .cacheMappings(true) .threads(Runtime.getRuntime().availableProcessors()) @@ -290,13 +291,11 @@ public class MinecraftMappedProvider extends DependencyProvider { } protected void addDependencies(DependencyInfo dependency, Consumer postPopulationScheduler) { - getProject().getRepositories().flatDir(repository -> repository.dir(getJarDirectory(getExtension().getUserCache(), "mapped"))); - getProject().getDependencies().add(Constants.Configurations.MINECRAFT_NAMED, getProject().getDependencies().module("net.minecraft:minecraft:" + getJarVersionString("mapped"))); } - public void initFiles(MinecraftProvider minecraftProvider, MappingsProvider mappingsProvider) { + public void initFiles(MinecraftProviderImpl minecraftProvider, MappingsProviderImpl mappingsProvider) { this.minecraftProvider = minecraftProvider; minecraftIntermediaryJar = new File(getExtension().getUserCache(), "minecraft-" + getJarVersionString("intermediary") + ".jar"); minecraftSrgJar = !getExtension().isForge() ? null : new File(getExtension().getUserCache(), "minecraft-" + getJarVersionString("srg") + ".jar"); @@ -309,7 +308,7 @@ public class MinecraftMappedProvider extends DependencyProvider { } protected String getJarVersionString(String type) { - return String.format("%s-%s-%s-%s%s", minecraftProvider.getMinecraftVersion(), type, getExtension().getMappingsProvider().mappingsName, getExtension().getMappingsProvider().mappingsVersion, minecraftProvider.getJarSuffix()); + return String.format("%s-%s-%s-%s%s", minecraftProvider.minecraftVersion(), type, getExtension().getMappingsProvider().mappingsName, getExtension().getMappingsProvider().mappingsVersion, minecraftProvider.getJarSuffix()); } public File getIntermediaryJar() { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java index af5aee2b..00fb88c8 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/assets/MinecraftAssetsProvider.java @@ -43,13 +43,13 @@ import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.configuration.providers.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.HashedDownloadUtil; public class MinecraftAssetsProvider { - public static void provide(MinecraftProvider minecraftProvider, Project project) throws IOException { + public static void provide(MinecraftProviderImpl minecraftProvider, Project project) throws IOException { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); boolean offline = project.getGradle().getStartParameter().isOffline(); @@ -63,7 +63,7 @@ public class MinecraftAssetsProvider { assets.mkdirs(); } - File assetsInfo = new File(assets, "indexes" + File.separator + assetIndex.fabricId(minecraftProvider.getMinecraftVersion()) + ".json"); + File assetsInfo = new File(assets, "indexes" + File.separator + assetIndex.fabricId(minecraftProvider.minecraftVersion()) + ".json"); project.getLogger().info(":downloading asset index"); diff --git a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java b/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java index 13df1b66..4d8d7257 100644 --- a/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java +++ b/src/main/java/net/fabricmc/loom/decompilers/fernflower/AbstractFernFlowerDecompiler.java @@ -72,6 +72,7 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler { put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1"); put(IFernflowerPreferences.LOG_LEVEL, "trace"); put(IFernflowerPreferences.THREADS, metaData.numberOfThreads()); + put(IFernflowerPreferences.INDENT_STRING, "\t"); }}; List args = new ArrayList<>(); @@ -106,7 +107,14 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler { spec.getMainClass().set(fernFlowerExecutor().getName()); spec.jvmArgs("-Xms200m", "-Xmx3G"); spec.setArgs(args); - spec.setErrorOutput(System.err); + spec.setErrorOutput(new ConsumingOutputStream(line -> { + if (line.startsWith("Inconsistent inner class entries")) { + // Suppress this + return; + } + + System.err.println(line); + })); spec.setStandardOutput(new ConsumingOutputStream(line -> { if (line.startsWith("Listening for transport") || !line.contains("::")) { System.out.println(line); diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 87706073..4b180030 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -42,7 +42,7 @@ import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.decompilers.DecompilationMetadata; import net.fabricmc.loom.api.decompilers.LoomDecompiler; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.sources.ForgeSourcesRemapper; import net.fabricmc.loom.decompilers.LineNumberRemapper; import net.fabricmc.loom.util.Constants; @@ -111,7 +111,7 @@ public class GenerateSourcesTask extends AbstractLoomTask { public static File getMappedJarFileWithSuffix(Project project, String suffix) { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); String path = mappedJar.getAbsolutePath(); diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index da7bf898..f91b3348 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -33,7 +33,7 @@ import org.gradle.api.tasks.TaskContainer; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.decompilers.LoomDecompiler; import net.fabricmc.loom.configuration.ide.RunConfigSettings; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler; import net.fabricmc.loom.util.Constants; @@ -125,7 +125,7 @@ public final class LoomTasks { LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); project.afterEvaluate(p -> { - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); File inputJar = mappingsProvider.mappedProvider.getMappedJar(); if (mappingsProvider.hasUnpickDefinitions()) { diff --git a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java index 97217fcb..7f7074de 100644 --- a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java @@ -49,9 +49,10 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; -import net.fabricmc.loom.configuration.providers.mappings.MojangMappingsDependency; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder; +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.lorenztiny.TinyMappingsJoiner; import net.fabricmc.mapping.tree.TinyMappingFactory; @@ -96,7 +97,7 @@ public class MigrateMappingsTask extends AbstractLoomTask { Files.createDirectories(outputDir); File mappings = loadMappings(); - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); try { TinyTree currentMappings = mappingsProvider.getMappings(); @@ -118,12 +119,13 @@ public class MigrateMappingsTask extends AbstractLoomTask { Set files; try { - if (mappings.startsWith(MojangMappingsDependency.GROUP + ':' + MojangMappingsDependency.MODULE + ':') || mappings.startsWith("net.mojang.minecraft:mappings:")) { - if (!mappings.endsWith(":" + project.getExtensions().getByType(LoomGradleExtension.class).getMinecraftProvider().getMinecraftVersion())) { + if (mappings.startsWith("net.minecraft:mappings:") || mappings.startsWith("net.mojang.minecraft:mappings:")) { + if (!mappings.endsWith(":" + project.getExtensions().getByType(LoomGradleExtension.class).getMinecraftProvider().minecraftVersion())) { throw new UnsupportedOperationException("Migrating Mojang mappings is currently only supported for the specified minecraft version"); } - files = new MojangMappingsDependency(project, getExtension()).resolve(); + LayeredMappingsDependency dep = (LayeredMappingsDependency) getExtension().layered(LayeredMappingSpecBuilder::officialMojangMappings); + files = dep.resolve(); } else { Dependency dependency = project.getDependencies().create(mappings); files = project.getConfigurations().detachedConfiguration(dependency).resolve(); diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 129fc991..49e57c8d 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -81,7 +81,7 @@ import net.fabricmc.loom.build.nesting.NestedDependencyProvider; import net.fabricmc.loom.build.nesting.NestedJarPathProvider; import net.fabricmc.loom.build.nesting.NestedJarProvider; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.loom.util.TinyRemapperMappingsHelper; @@ -211,7 +211,7 @@ public class RemapJarTask extends Jar { throw new FileNotFoundException(input.toString()); } - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); String fromM = this.fromM.get(); String toM = this.toM.get(); diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 249d8682..5947f116 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -91,6 +91,7 @@ public class Constants { public static final String TERMINAL_CONSOLE_APPENDER = "net.minecrell:terminalconsoleappender:"; public static final String JETBRAINS_ANNOTATIONS = "org.jetbrains:annotations:"; public static final String JAVAX_ANNOTATIONS = "com.google.code.findbugs:jsr305:"; // I hate that I have to add these. + public static final String FORGE_RUNTIME = "dev.architectury:architectury-loom-forge-runtime:"; public static final String ACCESS_TRANSFORMERS = "net.minecraftforge:accesstransformers:"; private Dependencies() { @@ -105,6 +106,7 @@ public class Constants { public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0"; public static final String JETBRAINS_ANNOTATIONS = "19.0.0"; public static final String JAVAX_ANNOTATIONS = "3.0.2"; + public static final String FORGE_RUNTIME = "$LOOM_VERSION"; // replaced with current version at build time public static final String ACCESS_TRANSFORMERS = "2.2.0"; private Versions() { diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index 338009f3..20a7006e 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -45,7 +45,7 @@ import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.RemappedConfigurationEntry; import net.fabricmc.loom.configuration.providers.LaunchProvider; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.tr.MercuryUtils; import net.fabricmc.lorenztiny.TinyMappingsReader; import net.fabricmc.mapping.tree.TinyTree; @@ -175,7 +175,7 @@ public class SourceRemapper { } LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); - MappingsProvider mappingsProvider = extension.getMappingsProvider(); + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); String intermediary = extension.isForge() ? "srg" : "intermediary"; int id = -1; diff --git a/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java b/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java index bdb22d73..e0b4ed5b 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java +++ b/src/main/java/net/fabricmc/loom/util/srg/MCPReader.java @@ -24,7 +24,9 @@ package net.fabricmc.loom.util.srg; +import java.io.BufferedReader; import java.io.IOException; +import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -35,12 +37,17 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.opencsv.CSVReader; import com.opencsv.exceptions.CsvValidationException; +import dev.architectury.mappingslayers.api.mutable.MutableClassDef; +import dev.architectury.mappingslayers.api.mutable.MutableFieldDef; +import dev.architectury.mappingslayers.api.mutable.MutableMethodDef; +import dev.architectury.mappingslayers.api.mutable.MutableTinyTree; +import dev.architectury.mappingslayers.api.utils.MappingsUtils; +import org.apache.commons.io.IOUtils; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.io.srg.tsrg.TSrgReader; import org.cadixdev.lorenz.model.ClassMapping; @@ -180,17 +187,43 @@ public class MCPReader { private Map readSrg() throws IOException { Map tokens = new HashMap<>(); - try (TSrgReader reader = new TSrgReader(Files.newBufferedReader(srgTsrgPath, StandardCharsets.UTF_8))) { - MappingSet mappingSet = reader.read(); + try (BufferedReader reader = Files.newBufferedReader(srgTsrgPath, StandardCharsets.UTF_8)) { + String content = IOUtils.toString(reader); - for (TopLevelClassMapping classMapping : mappingSet.getTopLevelClassMappings()) { - appendClass(tokens, classMapping); + if (content.startsWith("tsrg2")) { + readTsrg2(tokens, content); + } else { + MappingSet mappingSet = new TSrgReader(new StringReader(content)).read(); + + for (TopLevelClassMapping classMapping : mappingSet.getTopLevelClassMappings()) { + appendClass(tokens, classMapping); + } } } return tokens; } + private void readTsrg2(Map tokens, String content) { + MutableTinyTree tree = MappingsUtils.deserializeFromTsrg2(content); + int obfIndex = tree.getMetadata().index("obf"); + int srgIndex = tree.getMetadata().index("srg"); + + for (MutableClassDef classDef : tree.getClassesMutable()) { + MemberToken ofClass = MemberToken.ofClass(classDef.getName(obfIndex)); + tokens.put(ofClass, classDef.getName(srgIndex)); + + for (MutableFieldDef fieldDef : classDef.getFieldsMutable()) { + tokens.put(MemberToken.ofField(ofClass, fieldDef.getName(obfIndex)), fieldDef.getName(srgIndex)); + } + + for (MutableMethodDef methodDef : classDef.getMethodsMutable()) { + tokens.put(MemberToken.ofMethod(ofClass, methodDef.getName(obfIndex), methodDef.getDescriptor(obfIndex)), + methodDef.getName(srgIndex)); + } + } + } + private void injectMcp(Path mcpJar, Map intermediaryToSrgMap, Map intermediaryToDocsMap, Map> intermediaryToParamsMap) throws IOException, CsvValidationException { Map> srgToIntermediary = inverseMap(intermediaryToSrgMap); @@ -304,21 +337,12 @@ public class MCPReader { } } - private static class MemberToken { - private final TokenType type; - @Nullable - private final MemberToken owner; - private final String name; - @Nullable - private final String descriptor; - - MemberToken(TokenType type, @Nullable MemberToken owner, String name, @Nullable String descriptor) { - this.type = type; - this.owner = owner; - this.name = name; - this.descriptor = descriptor; - } - + private record MemberToken( + TokenType type, + @Nullable MCPReader.MemberToken owner, + String name, + @Nullable String descriptor + ) { static MemberToken ofClass(String name) { return new MemberToken(TokenType.CLASS, null, name, null); } @@ -330,19 +354,6 @@ public class MCPReader { static MemberToken ofMethod(MemberToken owner, String name, String descriptor) { return new MemberToken(TokenType.METHOD, owner, name, descriptor); } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MemberToken that = (MemberToken) o; - return type == that.type && name.equals(that.name) && Objects.equals(descriptor, that.descriptor) && Objects.equals(owner, that.owner); - } - - @Override - public int hashCode() { - return Objects.hash(type, name, descriptor, owner); - } } private enum TokenType { diff --git a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java b/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java index 9f9fbe7d..cfaa34f7 100644 --- a/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java +++ b/src/main/java/net/fabricmc/loom/util/srg/SrgMerger.java @@ -26,6 +26,7 @@ package net.fabricmc.loom.util.srg; import java.io.BufferedReader; import java.io.IOException; +import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -34,6 +35,12 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; +import dev.architectury.mappingslayers.api.mutable.MutableClassDef; +import dev.architectury.mappingslayers.api.mutable.MutableFieldDef; +import dev.architectury.mappingslayers.api.mutable.MutableMethodDef; +import dev.architectury.mappingslayers.api.mutable.MutableTinyTree; +import dev.architectury.mappingslayers.api.utils.MappingsUtils; +import org.apache.commons.io.IOUtils; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.lorenz.io.srg.tsrg.TSrgReader; import org.cadixdev.lorenz.model.ClassMapping; @@ -74,13 +81,9 @@ public final class SrgMerger { * or if an element mentioned in the SRG file does not have tiny mappings */ public static void mergeSrg(Path srg, Path tiny, Path out, boolean lenient) throws IOException, MappingException { - MappingSet arr; + MappingSet arr = readSrg(srg); TinyTree foss; - try (TSrgReader reader = new TSrgReader(Files.newBufferedReader(srg))) { - arr = reader.read(); - } - try (BufferedReader reader = Files.newBufferedReader(tiny)) { foss = TinyMappingFactory.loadWithDetection(reader); } @@ -104,6 +107,44 @@ public final class SrgMerger { TinyV2Writer.write(file, out); } + private static MappingSet readSrg(Path srg) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(srg)) { + String content = IOUtils.toString(reader); + + if (content.startsWith("tsrg2")) { + return readTsrg2(content); + } else { + try (TSrgReader srgReader = new TSrgReader(new StringReader(content))) { + return srgReader.read(); + } + } + } + } + + private static MappingSet readTsrg2(String content) { + MappingSet set = MappingSet.create(); + MutableTinyTree tree = MappingsUtils.deserializeFromTsrg2(content); + int obfIndex = tree.getMetadata().index("obf"); + int srgIndex = tree.getMetadata().index("srg"); + + for (MutableClassDef classDef : tree.getClassesMutable()) { + ClassMapping classMapping = set.getOrCreateClassMapping(classDef.getName(obfIndex)); + classMapping.setDeobfuscatedName(classDef.getName(srgIndex)); + + for (MutableFieldDef fieldDef : classDef.getFieldsMutable()) { + FieldMapping fieldMapping = classMapping.getOrCreateFieldMapping(fieldDef.getName(obfIndex)); + fieldMapping.setDeobfuscatedName(fieldDef.getName(srgIndex)); + } + + for (MutableMethodDef methodDef : classDef.getMethodsMutable()) { + MethodMapping methodMapping = classMapping.getOrCreateMethodMapping(methodDef.getName(obfIndex), methodDef.getDescriptor(obfIndex)); + methodMapping.setDeobfuscatedName(methodDef.getName(srgIndex)); + } + } + + return set; + } + private static void classToTiny(TinyTree foss, List namespaces, ClassMapping klass, Consumer classConsumer, boolean lenient) { String obf = klass.getFullObfuscatedName(); String srg = klass.getFullDeobfuscatedName(); diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DependencyResolutionManagementTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DependencyResolutionManagementTest.groovy new file mode 100644 index 00000000..a8b27336 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DependencyResolutionManagementTest.groovy @@ -0,0 +1,52 @@ +/* + * 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.test.integration + +import net.fabricmc.loom.test.util.ProjectTestTrait +import spock.lang.Specification +import spock.lang.Unroll + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class DependencyResolutionManagementTest extends Specification implements ProjectTestTrait { + @Override + String name() { + "dependencyResolutionManagement" + } + + @Unroll + def "build (gradle #gradle)"() { + when: + def result = create("build", gradle) + then: + result.task(":basic:build").outcome == SUCCESS + result.task(":projmap:build").outcome == SUCCESS + + where: + gradle | _ + DEFAULT_GRADLE | _ + PRE_RELEASE_GRADLE | _ + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/ParchmentTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/ParchmentTest.groovy new file mode 100644 index 00000000..836ba58a --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/ParchmentTest.groovy @@ -0,0 +1,52 @@ +/* + * 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.test.integration + +import net.fabricmc.loom.test.util.ProjectTestTrait +import spock.lang.Specification +import spock.lang.Unroll + +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class ParchmentTest extends Specification implements ProjectTestTrait { + @Override + String name() { + "parchment" + } + + @Unroll + def "parchment #gradle"() { + when: + def result = create("build", gradle) + + then: + result.task(":build").outcome == SUCCESS + + where: + gradle | _ + DEFAULT_GRADLE | _ + PRE_RELEASE_GRADLE | _ + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SignedProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SignedProjectTest.groovy new file mode 100644 index 00000000..3570b220 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SignedProjectTest.groovy @@ -0,0 +1,96 @@ +/* + * 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.test.integration + +import net.fabricmc.loom.test.util.ArchiveAssertionsTrait + +import net.fabricmc.loom.test.util.MockMavenServerTrait +import spock.lang.Specification +import spock.lang.Stepwise +import spock.lang.Unroll +import spock.util.environment.RestoreSystemProperties + +import static java.lang.System.setProperty +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +/** + * This tests publishing signed artifacts to a maven server + */ +@Stepwise +class SignedProjectTest extends Specification implements MockMavenServerTrait, ArchiveAssertionsTrait { + @Unroll + @RestoreSystemProperties + def "sign and publish lib #gradle"() { + given: + setProperty('loom.test.secretKey', PRIVATE_KEY) + when: + def result = create("publish", gradle) + then: + result.task(":publish").outcome == SUCCESS + where: + gradle | _ + DEFAULT_GRADLE | _ + PRE_RELEASE_GRADLE | _ + } + + @Override + String name() { + "signed" + } + + static final String PRIVATE_KEY = """ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQPGBGCm5LMBCADUeHXbe4TmP3qELtz6he7CLaVMFXqL/fU+M7GIrj0qtLU6pJ9v +KSbF3tQATKlU91zkQCIPg41VBlqkx85bOm0u7Nvv4JhWMqE+ZsNoNVXS2xQEyEIW +rX9Cd0/YibU2FpWOlo1l/UZPPD/lYUrkZEhgoKHMHP3SYb5Ohgpy4klTeQdXTRMF +q0IeyFynw3eqrdWmktOEApd7qeu/Hs1NEnZssSZQdAYB4R+tL/ePgIYXsViWvqbT +XDYfmd+AiRnACGtrt5P8tSmKhLPzth36cvqLXI+hSBGHu0PRfvQnfjn3CWq8AaIj +dLsmRYw8NedYZ5DgY3NIRMBkG561Uc1Kj5c7ABEBAAH+BwMCfh5aZV7x7zP/HCCP +WwbuNO9fLKss4J7sNVWdkX/ZsOy5OMLBql70F8PKEovObkYiAWsPUjrQ50VMhCUc +V2443FErPTC9A+5NsJ3Sx+BazbsUd9cprnJIW0tVGP4ij9j14A0VOogJUbzrxonQ +aCQ4OMJi5cxk/o2z/N5WDG92Qb/CxNlp6oxuUgdxXWdhWSpW6XBlBKfMsjK6acpo +gAQg+e8m0FCRrpd+vMoHFPYa0UdY8s2YH88te7YiQYYPf9FI3Uk8FeKRCqgRIwTr +fWd7Ubh2vK0h3ua3gyJm1aqQbIYVk/a2L1KF6tsuh1AYGbyXitx6cujSOukwz3xi +ej4CutY60PoIFihSiBBsRwpvcGr9RoYkJ8tKBqq67xTttYhdlBiedM4/05gdCglw +UXm7O1LVOro6vaI6RzP1hL5Q/OLkx4mxXtaNbsjP1/Urml7bB1aqzeoMXUSlSqB+ +LHavKxonYcEj2cRKRg1v/t2UV0lXyimammJ5c4Hq49bLygYITrT9pL3n9OOmAYBL +/+uv7h640cYWeR8YBQ7jCbdaqP+bJNmIbKlLMZfcS49Bt/WM1kFa6CqvAyNFewuL +CnRbMcdteYGWYvSyvmzKDa7tQ6TILt9ZrJOGPTGrEM2zLIR8H6eDpzXSVwJb/0Nk +apaCzB9GqMDtYpEu+nMg1/EI2oSzj0Ng0pV+rAJr6oLc6Y0iesVKbwg2VgYgzF7U +CG9B15hPofUDKXb43Fig4nWieceDzGveY8vlFeSMBxzxhCRsXKP9oWogtNRJiJ9c +t+VkzBADEb82mnG/QuTBgCxceRBVu4Bg9tPGRSHjBZurtdkKvJqEq5ay/lGZ718b +3Za/hMzR6rakVfKdGs7A2HN68iCkX3cZYn+uaKT8aEUSXoSFZXfJqU3pVi2ql2MN +43RseA0og79mtCFsb29tLXRlc3QgPGxvb20tdGVzdEBleGFtcGxlLmNvbT6JAU4E +EwEKADgWIQSP20iY86Edwz6Qcq0M9Z/0ipBcJgUCYKbkswIbLwULCQgHAgYVCgkI +CwIEFgIDAQIeAQIXgAAKCRAM9Z/0ipBcJruCCADNydlXQRAr799Fr58zf9YGBcH5 +7F3TQpzK2zd6iktFy9cjIu04pYtvdrEP+29hLmy1ibUBI3yx8HH1BxHm8Eu2ZTAn +b5EYkmF73CecdtSu3yL0tmk/4GLO6t2r/SN7imFnq9xKyTqJmtftQngBhgoA6KPk +4ZEkOA1MbVpaSjGy5H1U/XusH1UDA3SZWlOwrY3xO8TfycsR9BijtCqxTnuwNXzT +wWoDPJEzJM/KCs0aXRbwwWALcxqk6sevLwx4D4/k3xxEB8cf5cBJC8bJjnBz5FSi +WBVyzTF8wLkcSacL93kE6swpP+iNkIwkO4eoyTA2RmTJUcz/M0zWS7NEM8S0 +=xl+8 +-----END PGP PRIVATE KEY BLOCK-----""" +} \ No newline at end of file diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy new file mode 100644 index 00000000..a31d9ccd --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy @@ -0,0 +1,55 @@ +/* + * 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.test.unit + +import net.fabricmc.loom.build.nesting.MergedNestedJarProvider +import net.fabricmc.loom.build.nesting.NestedJarProvider +import org.gradle.api.Project +import spock.lang.Specification + +class MergedNestedJarProviderTest extends Specification { + def "empty test"() { + given: + def nestedJarProvider = new MergedNestedJarProvider(new TestNestedJarProvider()) + when: + nestedJarProvider.prepare(null) + then: + nestedJarProvider.provide() != null + } + + private class TestNestedJarProvider implements NestedJarProvider { + private Collection files = null + + @Override + Collection provide() { + return files + } + + @Override + void prepare(Project project) { + files = [] + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/IntermediaryMappingLayerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/IntermediaryMappingLayerTest.groovy new file mode 100644 index 00000000..3184ceb9 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/IntermediaryMappingLayerTest.groovy @@ -0,0 +1,43 @@ +/* + * 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.test.unit.layeredmappings + +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec + +class IntermediaryMappingLayerTest extends LayeredMappingsSpecification { + def "Read intermediary mappings" () { + setup: + mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_17_URL, "intermediary.jar"), "mappings/mappings.tiny") + when: + def mappings = getSingleMapping(new IntermediaryMappingsSpec()) + def tiny = getTiny(mappings) + then: + mappings.srcNamespace == "official" + mappings.dstNamespaces == ["intermediary", "named"] + mappings.classes.size() == 6107 + mappings.getClass("abc").getDstName(0) == "net/minecraft/class_3191" + mappings.getClass("abc").getDstName(1) == "net/minecraft/class_3191" + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy new file mode 100644 index 00000000..7fe03993 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy @@ -0,0 +1,118 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.unit.layeredmappings + +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilder +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec +import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec +import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpec +import org.gradle.api.Action +import org.gradle.util.ConfigureUtil +import spock.lang.Specification + +class LayeredMappingSpecBuilderTest extends Specification { + def "simple mojmap" () { + when: + def spec = layered() { + officialMojangMappings() + } + def layers = spec.layers() + then: + spec.version == "layered+hash.961" + layers.size() == 2 + layers[0].class == IntermediaryMappingsSpec + layers[1].class == MojangMappingsSpec + } + + def "simple mojmap with parchment" () { + when: + def spec = layered() { + officialMojangMappings() + parchment("I like cake") + } + def layers = spec.layers() + def parchment = layers[2] as ParchmentMappingsSpec + then: + spec.version == "layered+hash.863714404" + layers.size() == 3 + layers[0].class == IntermediaryMappingsSpec + layers[1].class == MojangMappingsSpec + layers[2].class == ParchmentMappingsSpec + parchment.mavenNotation() == "I like cake" + parchment.removePrefix() == true + } + + def "simple mojmap with parchment keep prefix" () { + when: + def spec = layered() { + officialMojangMappings() + parchment("I like cake") { + it.removePrefix = false + } + } + def layers = spec.layers() + def parchment = layers[2] as ParchmentMappingsSpec + then: + spec.version == "layered+hash.863714410" + layers.size() == 3 + layers[0].class == IntermediaryMappingsSpec + layers[1].class == MojangMappingsSpec + layers[2].class == ParchmentMappingsSpec + parchment.mavenNotation() == "I like cake" + parchment.removePrefix() == false + } + + def "simple mojmap with parchment keep prefix alternate hash" () { + when: + def spec = layered() { + officialMojangMappings() + parchment("I really like cake") { + it.removePrefix = false + } + } + def layers = spec.layers() + def parchment = layers[2] as ParchmentMappingsSpec + then: + spec.version == "layered+hash.1144465487" + layers.size() == 3 + layers[0].class == IntermediaryMappingsSpec + layers[1].class == MojangMappingsSpec + layers[2].class == ParchmentMappingsSpec + parchment.mavenNotation() == "I really like cake" + parchment.removePrefix() == false + } + + // Gradle does this big of magic behind the scenes + LayeredMappingSpec layered(@DelegatesTo(LayeredMappingSpecBuilder) Closure cl) { + return layeredAction(ConfigureUtil.configureUsing(cl)) + } + + LayeredMappingSpec layeredAction(Action action) { + LayeredMappingSpecBuilder builder = new LayeredMappingSpecBuilder() + action.execute(builder) + return builder.build() + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy new file mode 100644 index 00000000..8fa26b34 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsSpecification.groovy @@ -0,0 +1,121 @@ +/* + * 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.test.unit.layeredmappings + +import groovy.transform.CompileStatic +import net.fabricmc.loom.configuration.providers.MinecraftProvider +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec +import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsProcessor +import net.fabricmc.loom.configuration.providers.mappings.MappingContext +import net.fabricmc.loom.configuration.providers.mappings.MappingLayer +import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider +import net.fabricmc.loom.configuration.providers.mappings.MappingsSpec +import net.fabricmc.mappingio.format.Tiny2Writer +import net.fabricmc.mappingio.tree.MappingTree +import net.fabricmc.mappingio.tree.MemoryMappingTree +import org.gradle.api.logging.Logger +import spock.lang.Specification + +import java.util.zip.ZipFile + +abstract class LayeredMappingsSpecification extends Specification implements LayeredMappingsTestConstants { + Logger mockLogger = Mock(Logger) + MappingsProvider mockMappingsProvider = Mock(MappingsProvider) + MinecraftProvider mockMinecraftProvider = Mock(MinecraftProvider) + + MappingContext mappingContext = new TestMappingContext() + + File tempDir = File.createTempDir() + + Map mavenFiles = [:] + + def withMavenFile(String mavenNotation, File file) { + mavenFiles.put(mavenNotation, file) + } + + File downloadFile(String url, String name) { + File dst = new File(tempDir, name) + dst.parentFile.mkdirs() + dst << new URL(url).newInputStream() + return dst + } + + File extractFileFromZip(File zipFile, String name) { + File dst = new File(tempDir, name) + dst.parentFile.mkdirs() + + new ZipFile(zipFile).withCloseable { + dst << it.getInputStream(it.getEntry(name)) + } + return dst + } + + MemoryMappingTree getSingleMapping(MappingsSpec spec) { + MemoryMappingTree mappingTree = new MemoryMappingTree() + spec.createLayer(mappingContext).visit(mappingTree) + return mappingTree + } + + MemoryMappingTree getLayeredMappings(MappingsSpec... specs) { + LayeredMappingSpec spec = new LayeredMappingSpec(specs.toList()) + LayeredMappingsProcessor processor = new LayeredMappingsProcessor(spec) + return processor.getMappings(mappingContext) + } + + String getTiny(MemoryMappingTree mappingTree) { + def sw = new StringWriter() + mappingTree.accept(new Tiny2Writer(sw, false)) + return sw.toString() + } + + @CompileStatic + class TestMappingContext implements MappingContext { + @Override + File mavenFile(String mavenNotation) { + assert mavenFiles.containsKey(mavenNotation) + return mavenFiles.get(mavenNotation) + } + + @Override + MappingsProvider mappingsProvider() { + return mockMappingsProvider + } + + @Override + MinecraftProvider minecraftProvider() { + return mockMinecraftProvider + } + + @Override + File workingDirectory(String name) { + return new File(tempDir, name) + } + + @Override + Logger getLogger() { + return mockLogger + } + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy new file mode 100644 index 00000000..1827ccd6 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingsTestConstants.groovy @@ -0,0 +1,47 @@ +/* + * 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.test.unit.layeredmappings + +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta + +interface LayeredMappingsTestConstants { + public static final String INTERMEDIARY_1_17_URL = "https://maven.fabricmc.net/net/fabricmc/intermediary/1.17/intermediary-1.17-v2.jar" + public static final String INTERMEDIARY_1_16_5_URL = "https://maven.fabricmc.net/net/fabricmc/intermediary/1.16.5/intermediary-1.16.5-v2.jar" + + public static final Map DOWNLOADS_1_17 = [ + client_mappings:new MinecraftVersionMeta.Download(null, "227d16f520848747a59bef6f490ae19dc290a804", 6431705, "https://launcher.mojang.com/v1/objects/227d16f520848747a59bef6f490ae19dc290a804/client.txt"), + server_mappings:new MinecraftVersionMeta.Download(null, "84d80036e14bc5c7894a4fad9dd9f367d3000334", 4948536, "https://launcher.mojang.com/v1/objects/84d80036e14bc5c7894a4fad9dd9f367d3000334/server.txt") + ] + public static final MinecraftVersionMeta VERSION_META_1_17 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_17, null, null, null, null, 0, null, null, null) + + public static final Map DOWNLOADS_1_16_5 = [ + client_mappings:new MinecraftVersionMeta.Download(null, "e3dfb0001e1079a1af72ee21517330edf52e6192", 5746047, "https://launcher.mojang.com/v1/objects/e3dfb0001e1079a1af72ee21517330edf52e6192/client.txt"), + server_mappings:new MinecraftVersionMeta.Download(null, "81d5c793695d8cde63afddb40dde88e3a88132ac", 4400926, "https://launcher.mojang.com/v1/objects/81d5c793695d8cde63afddb40dde88e3a88132ac/server.txt") + ] + public static final MinecraftVersionMeta VERSION_META_1_16_5 = new MinecraftVersionMeta(null, null, null, 0, DOWNLOADS_1_16_5, null, null, null, null, 0, null, null, null) + + public static final String PARCHMENT_NOTATION = "org.parchmentmc.data:parchment-1.16.5:20210608-SNAPSHOT@zip" + public static final String PARCHMENT_URL = "https://ldtteam.jfrog.io/artifactory/parchmentmc-snapshots/org/parchmentmc/data/parchment-1.16.5/20210608-SNAPSHOT/parchment-1.16.5-20210608-SNAPSHOT.zip" +} \ No newline at end of file diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/MojangMappingLayerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/MojangMappingLayerTest.groovy new file mode 100644 index 00000000..28e058dd --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/MojangMappingLayerTest.groovy @@ -0,0 +1,49 @@ +/* + * 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.test.unit.layeredmappings + +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec +import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec + +class MojangMappingLayerTest extends LayeredMappingsSpecification { + def "Read mojang mappings" () { + setup: + mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_17_URL, "intermediary.jar"), "mappings/mappings.tiny") + mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_17 + when: + def mappings = getLayeredMappings( + new IntermediaryMappingsSpec(), + new MojangMappingsSpec() + ) + def tiny = getTiny(mappings) + then: + mappings.srcNamespace == "named" + mappings.dstNamespaces == ["intermediary", "official"] + mappings.classes.size() == 6113 + mappings.classes[0].srcName.hashCode() == 1869546970 // MojMap name, just check the hash + mappings.classes[0].getDstName(0) == "net/minecraft/class_2354" + mappings.classes[0].methods[0].args.size() == 0 // No Args + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/ParchmentMappingLayerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/ParchmentMappingLayerTest.groovy new file mode 100644 index 00000000..597e17f6 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/ParchmentMappingLayerTest.groovy @@ -0,0 +1,74 @@ +/* + * 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.test.unit.layeredmappings + +import net.fabricmc.loom.configuration.providers.mappings.intermediary.IntermediaryMappingsSpec +import net.fabricmc.loom.configuration.providers.mappings.mojmap.MojangMappingsSpec +import net.fabricmc.loom.configuration.providers.mappings.parchment.ParchmentMappingsSpec +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta + +class ParchmentMappingLayerTest extends LayeredMappingsSpecification { + def "Read parchment mappings" () { + setup: + mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_16_5_URL, "intermediary.jar"), "mappings/mappings.tiny") + mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_16_5 + when: + withMavenFile(PARCHMENT_NOTATION, downloadFile(PARCHMENT_URL, "parchment.zip")) + def mappings = getLayeredMappings( + new IntermediaryMappingsSpec(), + new MojangMappingsSpec(), + new ParchmentMappingsSpec(PARCHMENT_NOTATION, false) + ) + def tiny = getTiny(mappings) + then: + mappings.srcNamespace == "named" + mappings.dstNamespaces == ["intermediary", "official"] + mappings.classes.size() == 5747 + mappings.classes[0].srcName.hashCode() == -1112444138 // MojMap name, just check the hash + mappings.classes[0].getDstName(0) == "net/minecraft/class_2573" + mappings.classes[0].methods[0].args[0].srcName == "pStack" + } + + def "Read parchment mappings remove prefix" () { + setup: + mockMappingsProvider.intermediaryTinyFile() >> extractFileFromZip(downloadFile(INTERMEDIARY_1_16_5_URL, "intermediary.jar"), "mappings/mappings.tiny") + mockMinecraftProvider.getVersionInfo() >> VERSION_META_1_16_5 + when: + withMavenFile(PARCHMENT_NOTATION, downloadFile(PARCHMENT_URL, "parchment.zip")) + def mappings = getLayeredMappings( + new IntermediaryMappingsSpec(), + new MojangMappingsSpec(), + new ParchmentMappingsSpec(PARCHMENT_NOTATION, true) + ) + def tiny = getTiny(mappings) + then: + mappings.srcNamespace == "named" + mappings.dstNamespaces == ["intermediary", "official"] + mappings.classes.size() == 5747 + mappings.classes[0].srcName.hashCode() == -1112444138 // MojMap name, just check the hash + mappings.classes[0].getDstName(0) == "net/minecraft/class_2573" + mappings.classes[0].methods[0].args[0].srcName == "stack" + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/util/ProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/ProjectTestTrait.groovy index 56ed8c9c..0f72662d 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/ProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/ProjectTestTrait.groovy @@ -29,7 +29,7 @@ import org.gradle.testkit.runner.GradleRunner trait ProjectTestTrait { final static String DEFAULT_GRADLE = "7.0.1" - final static String PRE_RELEASE_GRADLE = "7.1-20210511220046+0000" + final static String PRE_RELEASE_GRADLE = "7.2-20210612220215+0000" static File gradleHome = File.createTempDir() File testProjectDir = File.createTempDir() diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle new file mode 100644 index 00000000..f02447c7 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/build.gradle @@ -0,0 +1,92 @@ +plugins { + id 'fabric-loom' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +archivesBaseName = project.archives_base_name +version = project.mod_version +group = project.maven_group + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. + // You may need to force-disable transitiveness on them. +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + + // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too + // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. + // We'll use that if it's available, but otherwise we'll use the older option. + def targetVersion = 8 + if (JavaVersion.current().isJava9Compatible()) { + it.options.release = targetVersion + } +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + builtBy remapJar + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/gradle.properties b/src/test/resources/projects/dependencyResolutionManagement/basic/gradle.properties new file mode 100644 index 00000000..40e9e571 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/gradle.properties @@ -0,0 +1,17 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G + +# Fabric Properties + # check these on https://fabricmc.net/use + minecraft_version=1.16.5 + yarn_mappings=1.16.5+build.6 + loader_version=0.11.3 + +# Mod Properties + mod_version = 1.0.0 + maven_group = com.example + archives_base_name = fabric-example-mod + +# Dependencies + # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api + fabric_version=0.32.5+1.16 diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/ExampleMod.java new file mode 100644 index 00000000..e5ed082e --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/ExampleMod.java @@ -0,0 +1,14 @@ +package net.fabricmc.example; + +import net.fabricmc.api.ModInitializer; + +public class ExampleMod implements ModInitializer { + @Override + public void onInitialize() { + // This code runs as soon as Minecraft is in a mod-load-ready state. + // However, some things (like resources) may still be uninitialized. + // Proceed with mild caution. + + System.out.println("Hello Fabric world!"); + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java new file mode 100644 index 00000000..83ee1a89 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java @@ -0,0 +1,15 @@ +package net.fabricmc.example.mixin; + +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + @Inject(at = @At("HEAD"), method = "init()V") + private void init(CallbackInfo info) { + System.out.println("This line is printed by an example mod mixin!"); + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/assets/modid/icon.png b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/assets/modid/icon.png new file mode 100644 index 00000000..047b91f2 Binary files /dev/null and b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/assets/modid/icon.png differ diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/fabric.mod.json b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..df92d8b1 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/fabric.mod.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "Me!" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + + "license": "CC0-1.0", + "icon": "assets/modid/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "net.fabricmc.example.ExampleMod" + ] + }, + "mixins": [ + "modid.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.7.4", + "fabric": "*", + "minecraft": "1.16.x" + }, + "suggests": { + "another-mod": "*" + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/modid.mixins.json b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/modid.mixins.json new file mode 100644 index 00000000..21fe73a4 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/basic/src/main/resources/modid.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.fabricmc.example.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle new file mode 100644 index 00000000..ca8c73f6 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/build.gradle @@ -0,0 +1,96 @@ +plugins { + id 'fabric-loom' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 + +archivesBaseName = project.archives_base_name +version = project.mod_version +group = project.maven_group + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. + // You may need to force-disable transitiveness on them. +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + + // The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too + // JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used. + // We'll use that if it's available, but otherwise we'll use the older option. + def targetVersion = 8 + if (JavaVersion.current().isJava9Compatible()) { + it.options.release = targetVersion + } +} + +java { + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } +} + +minecraft { + accessWidener = file("src/main/resources/modid.accesswidener") +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + // add all the jars that should be included when publishing to maven + artifact(remapJar) { + builtBy remapJar + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/gradle.properties b/src/test/resources/projects/dependencyResolutionManagement/projmap/gradle.properties new file mode 100644 index 00000000..40e9e571 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/gradle.properties @@ -0,0 +1,17 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G + +# Fabric Properties + # check these on https://fabricmc.net/use + minecraft_version=1.16.5 + yarn_mappings=1.16.5+build.6 + loader_version=0.11.3 + +# Mod Properties + mod_version = 1.0.0 + maven_group = com.example + archives_base_name = fabric-example-mod + +# Dependencies + # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api + fabric_version=0.32.5+1.16 diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/ExampleMod.java new file mode 100644 index 00000000..e5ed082e --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/ExampleMod.java @@ -0,0 +1,14 @@ +package net.fabricmc.example; + +import net.fabricmc.api.ModInitializer; + +public class ExampleMod implements ModInitializer { + @Override + public void onInitialize() { + // This code runs as soon as Minecraft is in a mod-load-ready state. + // However, some things (like resources) may still be uninitialized. + // Proceed with mild caution. + + System.out.println("Hello Fabric world!"); + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java new file mode 100644 index 00000000..83ee1a89 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/java/net/fabricmc/example/mixin/ExampleMixin.java @@ -0,0 +1,15 @@ +package net.fabricmc.example.mixin; + +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + @Inject(at = @At("HEAD"), method = "init()V") + private void init(CallbackInfo info) { + System.out.println("This line is printed by an example mod mixin!"); + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/assets/modid/icon.png b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/assets/modid/icon.png new file mode 100644 index 00000000..047b91f2 Binary files /dev/null and b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/assets/modid/icon.png differ diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/fabric.mod.json b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..4764df8c --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/fabric.mod.json @@ -0,0 +1,38 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "authors": [ + "Me!" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + + "license": "CC0-1.0", + "icon": "assets/modid/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "net.fabricmc.example.ExampleMod" + ] + }, + "mixins": [ + "modid.mixins.json" + ], + + "depends": { + "fabricloader": ">=0.7.4", + "fabric": "*", + "minecraft": "1.16.x" + }, + "suggests": { + "another-mod": "*" + }, + "accessWidener" : "modid.accesswidener" +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.accesswidener b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.accesswidener new file mode 100644 index 00000000..9684ec8f --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.accesswidener @@ -0,0 +1,2 @@ +accessWidener v1 named +extendable class net/minecraft/fluid/FluidState diff --git a/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.mixins.json b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.mixins.json new file mode 100644 index 00000000..21fe73a4 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/projmap/src/main/resources/modid.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.fabricmc.example.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/test/resources/projects/dependencyResolutionManagement/settings.gradle b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle new file mode 100644 index 00000000..c05e3699 --- /dev/null +++ b/src/test/resources/projects/dependencyResolutionManagement/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + plugins { + id 'fabric-loom' + } +} + +plugins { + id 'fabric-loom' +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) +} + +include 'basic' +include 'projmap' \ No newline at end of file diff --git a/src/test/resources/projects/parchment/build.gradle b/src/test/resources/projects/parchment/build.gradle new file mode 100644 index 00000000..d36eaaeb --- /dev/null +++ b/src/test/resources/projects/parchment/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'fabric-loom' +} + +repositories { + maven { + name = "ldtteam" + url = "https://ldtteam.jfrog.io/artifactory/parchmentmc-snapshots/" + } +} + +dependencies { + minecraft "com.mojang:minecraft:1.16.5" + mappings loom.layered() { + officialMojangMappings() + parchment("org.parchmentmc.data:parchment-1.16.5:20210608-SNAPSHOT@zip") + } + + modImplementation "net.fabricmc:fabric-loader:0.11.3" +} diff --git a/src/test/resources/projects/signed/build.gradle b/src/test/resources/projects/signed/build.gradle new file mode 100644 index 00000000..5da48236 --- /dev/null +++ b/src/test/resources/projects/signed/build.gradle @@ -0,0 +1,57 @@ +plugins { + id 'fabric-loom' + id 'maven-publish' + id 'signing' +} + +archivesBaseName = "fabric-example-lib" +version = "1.0.0" +group = "com.example" + +dependencies { + minecraft "com.mojang:minecraft:1.16.5" + mappings "net.fabricmc:yarn:1.16.5+build.5:v2" + modImplementation "net.fabricmc:fabric-loader:0.11.2" +} + +processResources { + inputs.property "version", project.version + + filesMatching("fabric.mod.json") { + expand "version": project.version + } +} + +java { + withSourcesJar() +} + +publishing { + publications { + mavenJava(MavenPublication) { + artifact(remapJar) { + builtBy remapJar + } + artifact(remapJar) { + builtBy remapJar + classifier "classifier" + } + artifact(sourcesJar) { + builtBy remapSourcesJar + } + } + } + + repositories { + maven { + url "http://localhost:${System.getProperty("loom.test.mavenPort")}/" + allowInsecureProtocol = true + } + } +} + +signing { + def privateKey = System.getProperty("loom.test.secretKey") ?: "no key!" + useInMemoryPgpKeys(privateKey, 'password') + sign publishing.publications.mavenJava +} diff --git a/src/test/resources/projects/signed/main/java/net/fabricmc/example/ExampleLib.java b/src/test/resources/projects/signed/main/java/net/fabricmc/example/ExampleLib.java new file mode 100644 index 00000000..e6ec1e65 --- /dev/null +++ b/src/test/resources/projects/signed/main/java/net/fabricmc/example/ExampleLib.java @@ -0,0 +1,13 @@ +package net.fabricmc.example; + +import net.fabricmc.api.ModInitializer; + +public class ExampleLib implements ModInitializer { + @Override + public void onInitialize() { + } + + public static void hello() { + System.out.println("Hello Fabric world!"); + } +} diff --git a/src/test/resources/projects/signed/main/resources/fabric.mod.json b/src/test/resources/projects/signed/main/resources/fabric.mod.json new file mode 100644 index 00000000..f527100e --- /dev/null +++ b/src/test/resources/projects/signed/main/resources/fabric.mod.json @@ -0,0 +1,4 @@ +{ + "schemaVersion": 1, + "id": "modid" +} diff --git a/src/test/resources/projects/signed/settings.gradle b/src/test/resources/projects/signed/settings.gradle new file mode 100644 index 00000000..fc4c0f72 --- /dev/null +++ b/src/test/resources/projects/signed/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = "fabric-example-lib" +