From 5a16440c1efec12870953e369f0f481d37c16b23 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Fri, 17 Dec 2021 15:37:00 +0000 Subject: [PATCH 01/15] Support ARM natives, rewrite native handling and misc cleanup. (#554) * Rewrite natives handling, upgrade LWJGL on ARM machines. * Remove old natives override hack, should now always be done via gradle. * Use "idea" everywhere * Add server specific libraries onto their own classpath. Includes misc code cleanup. * Start on writing the excluded server libs. * Gradle deprecation fixes, and misc cleanup * Add support mod. * Make native support mod none transitive. * Update gradle. --- .github/workflows/test-push.yml | 10 +- build.gradle | 10 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../fabricmc/loom/LoomGradleExtension.java | 12 +- .../net/fabricmc/loom/LoomGradlePlugin.java | 2 + .../spec/LayeredMappingSpecBuilder.java | 7 +- .../net/fabricmc/loom/build/JarRemapper.java | 2 +- .../loom/build/ModCompileRemapper.java | 22 +-- .../mixin/AnnotationProcessorInvoker.java | 6 +- .../loom/build/mixin/JavaApInvoker.java | 2 +- .../loom/build/mixin/KaptApInvoker.java | 2 +- .../loom/build/mixin/ScalaApInvoker.java | 2 +- .../nesting/NestedDependencyProvider.java | 3 +- .../configuration/CompileConfiguration.java | 31 ++-- .../configuration/LoomDependencyManager.java | 3 +- .../TransitiveAccessWidenerJarProcessor.java | 2 +- .../loom/configuration/ide/RunConfig.java | 3 +- .../configuration/ide/RunConfigSettings.java | 9 +- .../ide/SetupIntelijRunConfigs.java | 90 ----------- .../ide/idea/IdeaConfiguration.java | 83 ++++++++++ .../configuration/ide/idea/IdeaSyncTask.java | 130 +++++++++++++++ .../configuration/ide/idea/IdeaUtils.java | 45 ++++++ .../configuration/mods/ModVersionParser.java | 4 +- .../providers/BundleMetadata.java | 93 +++++++++++ .../providers/LaunchProvider.java | 6 +- .../providers/MinecraftProvider.java | 4 - .../providers/MinecraftProviderImpl.java | 90 +++-------- .../minecraft/LWJGLVersionOverride.java | 108 +++++++++++++ .../minecraft/MinecraftLibraryProvider.java | 64 ++++++-- .../minecraft/MinecraftMappedProvider.java | 2 +- .../minecraft/MinecraftNativesProvider.java | 150 ------------------ .../minecraft/MinecraftVersionMeta.java | 6 +- .../fabricmc/loom/extension/LoomFiles.java | 2 +- .../loom/extension/LoomFilesBaseImpl.java | 8 +- .../extension/LoomGradleExtensionImpl.java | 5 +- .../loom/extension/MixinExtensionApiImpl.java | 5 +- .../loom/extension/MixinExtensionImpl.java | 10 +- .../fabricmc/loom/task/AbstractRunTask.java | 6 +- .../loom/task/DownloadAssetsTask.java | 2 - .../loom/task/ExtractNativesTask.java | 50 ++++++ .../net/fabricmc/loom/task/LoomTasks.java | 7 +- .../loom/task/MigrateMappingsTask.java | 9 +- .../net/fabricmc/loom/task/RemapJarTask.java | 2 +- .../net/fabricmc/loom/util/Architecture.java | 47 ++++++ .../net/fabricmc/loom/util/ClosureAction.java | 36 +++++ .../net/fabricmc/loom/util/Constants.java | 11 +- .../fabricmc/loom/util/FileSystemUtil.java | 21 +++ .../java/net/fabricmc/loom/util/ModUtils.java | 8 +- .../fabricmc/loom/util/OperatingSystem.java | 26 ++- .../java/net/fabricmc/loom/util/ZipUtils.java | 8 +- .../loom/test/LoomTestConstants.groovy | 2 +- .../loom/test/integration/NativesTest.groovy | 86 ++++++++++ .../LayeredMappingSpecBuilderTest.groovy | 19 +-- 53 files changed, 905 insertions(+), 470 deletions(-) delete mode 100644 src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/BundleMetadata.java create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java delete mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java create mode 100644 src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java create mode 100644 src/main/java/net/fabricmc/loom/util/Architecture.java create mode 100644 src/main/java/net/fabricmc/loom/util/ClosureAction.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/NativesTest.groovy diff --git a/.github/workflows/test-push.yml b/.github/workflows/test-push.yml index 7ef93ccc..63458a4a 100644 --- a/.github/workflows/test-push.yml +++ b/.github/workflows/test-push.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: gradle/wrapper-validation-action@v1 - - run: gradle build check -x test --stacktrace + - run: gradle build check -x test --stacktrace --warning-mode fail # This job is used to feed the test matrix of next job to allow the tests to run in parallel prepare_test_matrix: @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v2 - - run: gradle writeActionsTestMatrix --stacktrace + - run: gradle writeActionsTestMatrix --stacktrace --warning-mode fail - id: set-matrix run: echo "::set-output name=matrix::$(cat build/test_matrix.json)" @@ -51,7 +51,7 @@ jobs: steps: - uses: actions/checkout@v2 - - run: gradle test --tests ${{ matrix.test }} --stacktrace + - run: gradle test --tests ${{ matrix.test }} --stacktrace --warning-mode fail env: TEST_WARNING_MODE: fail @@ -78,7 +78,7 @@ jobs: uses: actions/setup-java@v1 with: java-version: ${{ matrix.java }} - - run: ./gradlew test --tests ${{ matrix.test }} --stacktrace + - run: ./gradlew test --tests ${{ matrix.test }} --stacktrace --warning-mode fail env: TEST_WARNING_MODE: fail @@ -105,7 +105,7 @@ jobs: with: java-version: ${{ matrix.java }} - - run: ./gradlew test --tests *ReproducibleBuildTest --stacktrace + - run: ./gradlew test --tests *ReproducibleBuildTest --stacktrace --warning-mode fail - uses: actions/upload-artifact@v2 if: ${{ failure() }} diff --git a/build.gradle b/build.gradle index 439645f0..53b5e1dd 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,9 @@ repositories { url = 'https://maven.fabricmc.net/' } mavenCentral() + maven { + url = 'https://plugins.gradle.org/m2/' + } mavenLocal() } @@ -92,6 +95,9 @@ dependencies { // source code remapping implementation ('net.fabricmc:mercury:0.2.4') + // IDEA support + implementation ('gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.1.1') + // Kapt integration compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0') @@ -162,8 +168,8 @@ jacoco { jacocoTestReport { dependsOn test reports { - xml.enabled false - csv.enabled false + xml.required = false + csv.required = false html.destination file("${buildDir}/jacocoHtml") } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3cd8500c..ac0b842f 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.3.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 060a094b..5d4e88e7 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -31,6 +31,7 @@ import java.util.function.Supplier; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.mercury.Mercury; +import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -54,7 +55,12 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { LoomFiles getFiles(); - NamedDomainObjectProvider createLazyConfiguration(String name); + default NamedDomainObjectProvider createLazyConfiguration(String name) { + return createLazyConfiguration(name, config -> { + }); + } + + NamedDomainObjectProvider createLazyConfiguration(String name, Action configurationAction); NamedDomainObjectProvider getLazyConfigurationProvider(String name); @@ -94,10 +100,6 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { boolean isRootProject(); - default boolean ideSync() { - return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false")); - } - default String getIntermediaryUrl(String minecraftVersion) { return String.format(this.getIntermediaryUrl().get(), minecraftVersion); } diff --git a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java index 72a9580a..096c1b81 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java +++ b/src/main/java/net/fabricmc/loom/LoomGradlePlugin.java @@ -40,6 +40,7 @@ import net.fabricmc.loom.configuration.CompileConfiguration; import net.fabricmc.loom.configuration.FabricApiExtension; import net.fabricmc.loom.configuration.MavenPublication; import net.fabricmc.loom.configuration.ide.IdeConfiguration; +import net.fabricmc.loom.configuration.ide.idea.IdeaConfiguration; import net.fabricmc.loom.decompilers.DecompilerConfiguration; import net.fabricmc.loom.extension.LoomFiles; import net.fabricmc.loom.extension.LoomGradleExtensionImpl; @@ -84,5 +85,6 @@ public class LoomGradlePlugin implements BootstrappedPlugin { MavenPublication.configure(project); LoomTasks.registerTasks(project); DecompilerConfiguration.setup(project); + IdeaConfiguration.setup(project); } } diff --git a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java index 1ed527e0..f4e5d374 100644 --- a/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java +++ b/src/main/java/net/fabricmc/loom/api/mappings/layered/spec/LayeredMappingSpecBuilder.java @@ -27,9 +27,10 @@ package net.fabricmc.loom.api.mappings.layered.spec; import groovy.lang.Closure; import groovy.lang.DelegatesTo; import org.gradle.api.Action; -import org.gradle.util.ConfigureUtil; import org.jetbrains.annotations.ApiStatus; +import net.fabricmc.loom.util.ClosureAction; + /** * Used to configure a layered mapping spec. */ @@ -52,7 +53,7 @@ public interface LayeredMappingSpecBuilder { */ @SuppressWarnings("rawtypes") default LayeredMappingSpecBuilder officialMojangMappings(@DelegatesTo(value = MojangMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) { - return officialMojangMappings(mojangMappingsSpecBuilder -> ConfigureUtil.configure(closure, mojangMappingsSpecBuilder)); + return officialMojangMappings(new ClosureAction<>(closure)); } /** @@ -66,7 +67,7 @@ public interface LayeredMappingSpecBuilder { @SuppressWarnings("rawtypes") default LayeredMappingSpecBuilder parchment(Object object, @DelegatesTo(value = ParchmentMappingsSpecBuilder.class, strategy = Closure.DELEGATE_FIRST) Closure closure) { - return parchment(object, parchmentMappingsSpecBuilder -> ConfigureUtil.configure(closure, parchmentMappingsSpecBuilder)); + return parchment(object, new ClosureAction<>(closure)); } LayeredMappingSpecBuilder parchment(Object object, Action action); diff --git a/src/main/java/net/fabricmc/loom/build/JarRemapper.java b/src/main/java/net/fabricmc/loom/build/JarRemapper.java index 47158bc0..877d8784 100644 --- a/src/main/java/net/fabricmc/loom/build/JarRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/JarRemapper.java @@ -99,7 +99,7 @@ public class JarRemapper { outputConsumer.addNonClassFiles(data.input); - data.processAccessWidener(remapper.getRemapper()); + data.processAccessWidener(remapper.getEnvironment().getRemapper()); remapper.apply(outputConsumer, data.tag); } diff --git a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java index 47801d4d..d55c6893 100644 --- a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; -import java.util.zip.ZipFile; import com.google.common.io.Files; import org.gradle.api.Project; @@ -58,6 +57,7 @@ import net.fabricmc.loom.configuration.processors.dependency.ModDependencyInfo; import net.fabricmc.loom.configuration.processors.dependency.RemapData; import net.fabricmc.loom.util.Checksum; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.OperatingSystem; import net.fabricmc.loom.util.SourceRemapper; @@ -91,7 +91,7 @@ public class ModCompileRemapper { String name = artifact.getModuleVersion().getId().getName(); String version = replaceIfNullOrEmpty(artifact.getModuleVersion().getId().getVersion(), () -> Checksum.truncatedSha256(artifact.getFile())); - if (!isFabricMod(logger, artifact.getFile(), artifact.getId())) { + if (!ModUtils.isMod(artifact.getFile())) { addToRegularCompile(project, regularConfig, artifact); continue; } @@ -118,7 +118,7 @@ public class ModCompileRemapper { // Create a mod dependency for each file in the file collection for (File artifact : files) { - if (!isFabricMod(logger, artifact, artifact.getName())) { + if (!ModUtils.isMod(artifact)) { dependencies.add(regularConfig.getName(), project.files(artifact)); continue; } @@ -161,22 +161,6 @@ public class ModCompileRemapper { } } - /** - * Checks if an artifact is a fabric mod, according to the presence of a fabric.mod.json. - */ - private static boolean isFabricMod(Logger logger, File artifact, Object id) { - try (ZipFile zipFile = new ZipFile(artifact)) { - if (zipFile.getEntry("fabric.mod.json") != null) { - logger.info("Found Fabric mod in modCompile: {}", id); - return true; - } - - return false; - } catch (IOException e) { - return false; - } - } - private static void addToRegularCompile(Project project, Configuration regularCompile, ResolvedArtifact artifact) { project.getLogger().info(":providing " + artifact); DependencyHandler dependencies = project.getDependencies(); diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index f87ccc21..b8203abf 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -39,8 +39,9 @@ import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.tasks.SourceSet; -import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; +import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.util.Constants; /** @@ -97,9 +98,8 @@ public abstract class AnnotationProcessorInvoker { public void configureMixin() { ConfigurationContainer configs = project.getConfigurations(); - LoomGradleExtension extension = LoomGradleExtension.get(project); - if (!extension.ideSync()) { + if (!IdeaUtils.isIdeaSync()) { for (Configuration processorConfig : apConfigurations) { project.getLogger().info("Adding mixin to classpath of AP config: " + processorConfig.getName()); // Pass named MC classpath to mixin AP classpath diff --git a/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java index 770dbda5..fbd72938 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/JavaApInvoker.java @@ -58,7 +58,7 @@ public class JavaApInvoker extends AnnotationProcessorInvoker { @Override protected File getRefmapDestinationDir(JavaCompile task) { - return task.getDestinationDir(); + return task.getDestinationDirectory().getAsFile().get(); } private static String getAptConfigurationName(String sourceSet) { diff --git a/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java index 7bf54247..7e3a3efb 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/KaptApInvoker.java @@ -84,7 +84,7 @@ public class KaptApInvoker extends AnnotationProcessorInvoker { try { String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get(); Path src = Paths.get(getRefmapDestination(task, refmapName)); - Path dest = Paths.get(task.getDestinationDir().toString(), refmapName); + Path dest = Paths.get(task.getDestinationDirectory().get().getAsFile().toString(), refmapName); // Possible that no mixin annotations exist if (Files.exists(src)) { diff --git a/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java index e24442eb..d361b837 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/ScalaApInvoker.java @@ -59,6 +59,6 @@ public class ScalaApInvoker extends AnnotationProcessorInvoker { @Override protected File getRefmapDestinationDir(ScalaCompile task) { - return task.getDestinationDir(); + return task.getDestinationDirectory().get().getAsFile(); } } 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 18dc816b..a911d30e 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java @@ -53,6 +53,7 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.ZipUtils; public final class NestedDependencyProvider implements NestedJarProvider { @@ -158,7 +159,7 @@ public final class NestedDependencyProvider implements NestedJarProvider { File file = metaFile.file; //A lib that doesnt have a mod.json, we turn it into a fake mod - if (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) { + if (!ModUtils.isMod(file)) { LoomGradleExtension extension = LoomGradleExtension.get(project); File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing"); diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index f99d7216..74abad3c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -26,9 +26,11 @@ package net.fabricmc.loom.configuration; import java.nio.charset.StandardCharsets; +import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.AbstractCopyTask; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.compile.JavaCompile; @@ -39,7 +41,6 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.mixin.JavaApInvoker; 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.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; @@ -53,15 +54,20 @@ public final class CompileConfiguration { public static void setupConfigurations(Project project) { LoomGradleExtension extension = LoomGradleExtension.get(project); - extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH).configure(configuration -> configuration.setTransitive(true)); - extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED).configure(configuration -> configuration.setTransitive(false)); - extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED).configure(configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies - extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false)); - extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES).configure(configuration -> configuration.setTransitive(false)); - extension.createLazyConfiguration(Constants.Configurations.MINECRAFT).configure(configuration -> configuration.setTransitive(false)); - extension.createLazyConfiguration(Constants.Configurations.INCLUDE).configure(configuration -> configuration.setTransitive(false)); // Dont get transitive deps + extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH, configuration -> configuration.setTransitive(true)); + extension.createLazyConfiguration(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED, configuration -> configuration.setTransitive(false)); + extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NAMED, configuration -> configuration.setTransitive(false)); // The launchers do not recurse dependencies + NamedDomainObjectProvider serverDeps = extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, configuration -> configuration.setTransitive(false)); + extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES, configuration -> { + configuration.extendsFrom(serverDeps.get()); + configuration.setTransitive(false); + }); + extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_NATIVES, configuration -> configuration.setTransitive(false)); + extension.createLazyConfiguration(Constants.Configurations.LOADER_DEPENDENCIES, configuration -> configuration.setTransitive(false)); + extension.createLazyConfiguration(Constants.Configurations.MINECRAFT, configuration -> configuration.setTransitive(false)); + extension.createLazyConfiguration(Constants.Configurations.INCLUDE, configuration -> configuration.setTransitive(false)); // Dont get transitive deps extension.createLazyConfiguration(Constants.Configurations.MAPPING_CONSTANTS); - extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS).configure(configuration -> { + extension.createLazyConfiguration(Constants.Configurations.NAMED_ELEMENTS, configuration -> { configuration.setCanBeConsumed(true); configuration.setCanBeResolved(false); configuration.extendsFrom(project.getConfigurations().getByName(JavaPlugin.API_CONFIGURATION_NAME)); @@ -117,9 +123,9 @@ public final class CompileConfiguration { } public static void configureCompile(Project p) { - JavaPluginConvention javaModule = (JavaPluginConvention) p.getConvention().getPlugins().get("java"); + final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class); - SourceSet main = javaModule.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); @@ -140,7 +146,6 @@ public final class CompileConfiguration { project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns")); project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns")); - SetupIntelijRunConfigs.setup(project); extension.getRemapArchives().finalizeValue(); // Enables the default mod remapper diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 29ae1d03..7f92402d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -46,6 +46,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.LoomRepositoryPlugin; import net.fabricmc.loom.build.ModCompileRemapper; import net.fabricmc.loom.configuration.DependencyProvider.DependencyInfo; +import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SourceRemapper; @@ -209,7 +210,7 @@ public class LoomDependencyManager { loaderDepsConfig.getDependencies().add(modDep); // TODO: work around until https://github.com/FabricMC/Mixin/pull/60 and https://github.com/FabricMC/fabric-mixin-compile-extensions/issues/14 is fixed. - if (!extension.ideSync() && extension.getMixin().getUseLegacyMixinAp().get()) { + if (!IdeaUtils.isIdeaSync() && extension.getMixin().getUseLegacyMixinAp().get()) { apDepsConfig.getDependencies().add(modDep); } diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java index d5f234bd..1305de78 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/TransitiveAccessWidenerJarProcessor.java @@ -152,7 +152,7 @@ public class TransitiveAccessWidenerJarProcessor implements JarProcessor { try { AccessWidenerRemapper remappingVisitor = new AccessWidenerRemapper( accessWidener, - tinyRemapper.getRemapper(), + tinyRemapper.getEnvironment().getRemapper(), MappingsNamespace.INTERMEDIARY.toString(), MappingsNamespace.NAMED.toString() ); diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 3e23dec3..3d1b4a08 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -45,6 +45,7 @@ import org.w3c.dom.Node; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.InstallerData; +import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask; public class RunConfig { public String configName; @@ -174,7 +175,7 @@ public class RunConfig { public String fromDummy(String dummy, boolean relativeDir, Project project) throws IOException { String dummyConfig; - try (InputStream input = SetupIntelijRunConfigs.class.getClassLoader().getResourceAsStream(dummy)) { + try (InputStream input = IdeaSyncTask.class.getClassLoader().getResourceAsStream(dummy)) { dummyConfig = new String(input.readAllBytes(), StandardCharsets.UTF_8); } diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java index a7a1c428..81f24a23 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java @@ -34,7 +34,7 @@ import java.util.function.Function; import org.gradle.api.Named; import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; @@ -229,10 +229,7 @@ public final class RunConfigSettings implements Named { } public void source(String source) { - setSource(proj -> { - JavaPluginConvention conv = proj.getConvention().getPlugin(JavaPluginConvention.class); - return conv.getSourceSets().getByName(source); - }); + setSource(proj -> project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().getByName(source)); } public void ideConfigGenerated(boolean ideConfigGenerated) { @@ -243,7 +240,7 @@ public final class RunConfigSettings implements Named { * Add the {@code -XstartOnFirstThread} JVM argument when on OSX. */ public void startFirstThread() { - if (OperatingSystem.getOS().equalsIgnoreCase("osx")) { + if (OperatingSystem.CURRENT_OS.equals(OperatingSystem.MAC_OS)) { vmArg("-XstartOnFirstThread"); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java b/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java deleted file mode 100644 index 8120a63f..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/ide/SetupIntelijRunConfigs.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.ide; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; -import org.gradle.api.Project; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider; -import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider; - -public class SetupIntelijRunConfigs { - public static void setup(Project project) { - File projectDir = project.getRootProject().file(".idea"); - - if (!projectDir.exists()) { - return; - } - - try { - generate(project); - } catch (IOException e) { - throw new RuntimeException("Failed to generate run configs", e); - } - } - - private static void generate(Project project) throws IOException { - Project rootProject = project.getRootProject(); - LoomGradleExtension extension = LoomGradleExtension.get(project); - - if (extension.ideSync()) { - //Ensures the assets are downloaded when idea is syncing a project - MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); - MinecraftNativesProvider.provide(project); - } - - String projectPath = project == rootProject ? "" : project.getPath().replace(':', '_'); - - File projectDir = rootProject.file(".idea"); - File runConfigsDir = new File(projectDir, "runConfigurations"); - - if (!runConfigsDir.exists()) { - runConfigsDir.mkdirs(); - } - - for (RunConfigSettings settings : extension.getRunConfigs()) { - if (!settings.isIdeConfigGenerated()) { - continue; - } - - RunConfig config = RunConfig.runConfig(project, settings); - String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_"); - - File runConfigs = new File(runConfigsDir, name + projectPath + ".xml"); - String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, project); - - if (!runConfigs.exists()) { - FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8); - } - - settings.makeRunDir(); - } - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java new file mode 100644 index 00000000..17a646d7 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java @@ -0,0 +1,83 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.ide.idea; + +import java.util.Objects; + +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Project; +import org.gradle.api.plugins.ExtensionAware; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.plugins.ide.idea.model.IdeaModel; +import org.gradle.plugins.ide.idea.model.IdeaProject; +import org.jetbrains.gradle.ext.ActionDelegationConfig; +import org.jetbrains.gradle.ext.IdeaExtPlugin; +import org.jetbrains.gradle.ext.ProjectSettings; +import org.jetbrains.gradle.ext.RunConfiguration; +import org.jetbrains.gradle.ext.TaskTriggersConfig; + +public class IdeaConfiguration { + public static void setup(Project project) { + TaskProvider ideaSyncTask = project.getTasks().register("ideaSyncTask", IdeaSyncTask.class, ideaSyncTask1 -> { + ideaSyncTask1.dependsOn(project.getTasks().named("downloadAssets")); + }); + + if (!IdeaUtils.isIdeaSync()) { + return; + } + + project.getPlugins().apply(IdeaExtPlugin.class); + project.getPlugins().withType(IdeaExtPlugin.class, ideaExtPlugin -> { + if (project != project.getRootProject()) { + // Also ensure it's applied to the root project. + project.getRootProject().getPlugins().apply(IdeaExtPlugin.class); + } + + final IdeaModel ideaModel = project.getRootProject().getExtensions().findByType(IdeaModel.class); + + if (ideaModel == null) { + return; + } + + final IdeaProject ideaProject = ideaModel.getProject(); + + if (ideaProject == null) { + return; + } + + final ProjectSettings settings = getExtension(ideaProject, ProjectSettings.class); + final ActionDelegationConfig delegateActions = getExtension(settings, ActionDelegationConfig.class); + final TaskTriggersConfig taskTriggers = getExtension(settings, TaskTriggersConfig.class); + final NamedDomainObjectContainer runConfigurations = (NamedDomainObjectContainer) ((ExtensionAware) settings).getExtensions().getByName("runConfigurations"); + + // Run the sync task on import + taskTriggers.afterSync(ideaSyncTask); + }); + } + + private static T getExtension(Object extensionAware, Class type) { + return Objects.requireNonNull(((ExtensionAware) extensionAware).getExtensions().getByType(type)); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java new file mode 100644 index 00000000..58a42241 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java @@ -0,0 +1,130 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.ide.idea; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; + +import org.apache.commons.io.FileUtils; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskAction; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.ide.RunConfig; +import net.fabricmc.loom.configuration.ide.RunConfigSettings; +import net.fabricmc.loom.configuration.providers.BundleMetadata; +import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.util.Constants; + +public abstract class IdeaSyncTask extends AbstractLoomTask { + @Inject + public IdeaSyncTask() { + // Always re-run this task. + getOutputs().upToDateWhen(element -> false); + } + + @TaskAction + public void runTask() throws IOException { + File projectDir = getProject().getRootProject().file(".idea"); + + if (!projectDir.exists()) { + throw new RuntimeException("No .idea directory found"); + } + + generateRunConfigs(); + } + + // See: https://github.com/FabricMC/fabric-loom/pull/206#issuecomment-986054254 for the reason why XML's are still used to provide the run configs + private void generateRunConfigs() throws IOException { + Project rootProject = getProject().getRootProject(); + LoomGradleExtension extension = LoomGradleExtension.get(getProject()); + String projectPath = getProject() == rootProject ? "" : getProject().getPath().replace(':', '_'); + File runConfigsDir = new File(rootProject.file(".idea"), "runConfigurations"); + + if (!runConfigsDir.exists()) { + runConfigsDir.mkdirs(); + } + + final List excludedServerLibraries = getExcludedServerLibraries(); + + for (RunConfigSettings settings : extension.getRunConfigs()) { + if (!settings.isIdeConfigGenerated()) { + continue; + } + + RunConfig config = RunConfig.runConfig(getProject(), settings); + String name = config.configName.replaceAll("[^a-zA-Z0-9$_]", "_"); + + File runConfigs = new File(runConfigsDir, name + projectPath + ".xml"); + String runConfigXml = config.fromDummy("idea_run_config_template.xml", true, getProject()); + + if (!runConfigs.exists()) { + FileUtils.writeStringToFile(runConfigs, runConfigXml, StandardCharsets.UTF_8); + } + + settings.makeRunDir(); + + if (settings.getEnvironment().equals("server") && !excludedServerLibraries.isEmpty()) { + try { + setClasspathModifications(runConfigs, excludedServerLibraries); + } catch (Exception e) { + getProject().getLogger().error("Failed to modify run configuration xml", e); + } + } + } + } + + private List getExcludedServerLibraries() { + final BundleMetadata bundleMetadata = getExtension().getMinecraftProvider().getServerBundleMetadata(); + + if (bundleMetadata == null) { + // Legacy version + return Collections.emptyList(); + } + + final Set allLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles(); + final Set serverLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles(); + final List clientOnlyLibraries = new LinkedList<>(); + + for (File commonLibrary : allLibraries) { + if (!serverLibraries.contains(commonLibrary)) { + clientOnlyLibraries.add(commonLibrary.getAbsolutePath()); + } + } + + return clientOnlyLibraries; + } + + private void setClasspathModifications(File runConfig, List exclusions) throws Exception { + // TODO modify the xml + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java new file mode 100644 index 00000000..e1aa3ea4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaUtils.java @@ -0,0 +1,45 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.ide.idea; + +import java.util.Objects; + +public class IdeaUtils { + public static boolean isIdeaSync() { + return Boolean.parseBoolean(System.getProperty("idea.sync.active", "false")); + } + + public static String getIdeaVersion() { + return Objects.requireNonNull(System.getProperty("idea.version"), "Could not get idea version"); + } + + // 2021.3 or newer + public static boolean supportsCustomizableClasspath() { + final String[] split = getIdeaVersion().split("\\."); + final int major = Integer.parseInt(split[0]); + final int minor = Integer.parseInt(split[1]); + return major > 2021 || (major == 2021 && minor >= 3); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java index 09c4fc18..983bc1e3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModVersionParser.java @@ -30,7 +30,7 @@ import java.io.IOException; import com.google.gson.JsonObject; import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import net.fabricmc.loom.LoomGradlePlugin; @@ -67,7 +67,7 @@ public class ModVersionParser { } private File locateModJsonFile() { - return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets() + return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets() .getByName("main") .getResources() .matching(patternFilterable -> patternFilterable.include("fabric.mod.json")) diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/BundleMetadata.java b/src/main/java/net/fabricmc/loom/configuration/providers/BundleMetadata.java new file mode 100644 index 00000000..aa6b9379 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/BundleMetadata.java @@ -0,0 +1,93 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.util.FileSystemUtil; + +public record BundleMetadata(List libraries, List versions, String mainClass) { + private static final String LIBRARIES_LIST_PATH = "META-INF/libraries.list"; + private static final String VERSIONS_LIST_PATH = "META-INF/versions.list"; + private static final String MAINCLASS_PATH = "META-INF/main-class"; + + @Nullable + public static BundleMetadata fromJar(Path jar) throws IOException { + final List libraries; + final List versions; + final String mainClass; + + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar)) { + if (!Files.exists(fs.get().getPath(VERSIONS_LIST_PATH))) { + // Legacy jar + return null; + } + + libraries = readEntries(fs.readString(LIBRARIES_LIST_PATH), "META-INF/libraries/"); + versions = readEntries(fs.readString(VERSIONS_LIST_PATH), "META-INF/versions/"); + mainClass = fs.readString(MAINCLASS_PATH).trim(); + } + + return new BundleMetadata(libraries, versions, mainClass); + } + + private static List readEntries(String content, String pathPrefix) { + List entries = new ArrayList<>(); + + for (String entry : content.split("\n")) { + if (entry.isBlank()) { + continue; + } + + String[] split = entry.split("\t"); + + if (split.length != 3) { + continue; + } + + entries.add(new Entry(split[0], split[1], pathPrefix + split[2])); + } + + return Collections.unmodifiableList(entries); + } + + public record Entry(String sha1, String name, String path) { + public void unpackEntry(Path jar, Path dest) throws IOException { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar); + InputStream is = Files.newInputStream(fs.get().getPath(path()))) { + Files.copy(is, dest, StandardCopyOption.REPLACE_EXISTING); + } + } + } +} 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 312492a4..fcfa226f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -54,13 +54,15 @@ public class LaunchProvider extends DependencyProvider { @Override public void provide(DependencyInfo dependency, Consumer postPopulationScheduler) throws IOException { + final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath(); + final LaunchConfig launchConfig = new LaunchConfig() .property("fabric.development", "true") .property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath()) .property("log4j.configurationFile", getAllLog4JConfigFiles()) - .property("client", "java.library.path", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()) - .property("client", "org.lwjgl.librarypath", getExtension().getMinecraftProvider().nativesDir().getAbsolutePath()) + .property("client", "java.library.path", nativesPath) + .property("client", "org.lwjgl.librarypath", nativesPath) .argument("client", "--assetIndex") .argument("client", getExtension().getMinecraftProvider().getVersionInfo().assetIndex().fabricId(getExtension().getMinecraftProvider().minecraftVersion())) 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 96593194..d8e4a779 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProvider.java @@ -31,10 +31,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; public interface MinecraftProvider { File workingDir(); - boolean hasCustomNatives(); - - File nativesDir(); - File dir(String path); File file(String path); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java index 4cda3be7..de895de4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/MinecraftProviderImpl.java @@ -27,20 +27,16 @@ package net.fabricmc.loom.configuration.providers; import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.io.InputStream; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.StandardCopyOption; -import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import com.google.common.io.Files; import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.logging.Logger; +import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.DependencyProvider; @@ -48,9 +44,9 @@ 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.MirrorUtil; import net.fabricmc.loom.util.DownloadUtil; import net.fabricmc.loom.util.HashedDownloadUtil; +import net.fabricmc.loom.util.MirrorUtil; import net.fabricmc.stitch.merge.JarMerger; public class MinecraftProviderImpl extends DependencyProvider implements MinecraftProvider { @@ -66,6 +62,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra private File minecraftServerJar; // The extracted server jar from the boostrap, only exists in >=21w39a private File minecraftExtractedServerJar; + @Nullable + private BundleMetadata serverBundleMetadata; private File minecraftMergedJar; private File versionManifestJson; private File experimentalVersionsJson; @@ -101,6 +99,8 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra downloadJars(getProject().getLogger()); } + serverBundleMetadata = BundleMetadata.fromJar(minecraftServerJar.toPath()); + libraryProvider = new MinecraftLibraryProvider(); libraryProvider.provide(this, getProject()); @@ -257,61 +257,22 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra private void mergeJars(Logger logger) throws IOException { logger.info(":merging jars"); - try (JarMerger jarMerger = new JarMerger(minecraftClientJar, getServerJarToMerge(logger), minecraftMergedJar)) { - jarMerger.enableSyntheticParamsOffset(); - jarMerger.merge(); - } - } - - private File getServerJarToMerge(Logger logger) throws IOException { - try (ZipFile zipFile = new ZipFile(minecraftServerJar)) { - ZipEntry versionsListEntry = zipFile.getEntry("META-INF/versions.list"); - - if (versionsListEntry == null) { - // Legacy pre 21w38a jar - return minecraftServerJar; - } + File jarToMerge = minecraftServerJar; + if (serverBundleMetadata != null) { logger.info(":Extracting server jar from bootstrap"); - String versionsList; - - try (InputStream is = zipFile.getInputStream(versionsListEntry)) { - versionsList = new String(is.readAllBytes(), StandardCharsets.UTF_8); + if (serverBundleMetadata.versions().size() != 1) { + throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(serverBundleMetadata.versions().size())); } - String jarPath = null; - String[] versions = versionsList.split("\n"); + serverBundleMetadata.versions().get(0).unpackEntry(minecraftServerJar.toPath(), minecraftExtractedServerJar.toPath()); + jarToMerge = minecraftExtractedServerJar; + } - if (versions.length != 1) { - throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(versions.length)); - } - - for (String version : versions) { - if (version.isBlank()) continue; - - String[] split = version.split("\t"); - - if (split.length != 3) continue; - - final String hash = split[0]; - final String id = split[1]; - final String path = split[2]; - - // Take the first (only) version we find. - jarPath = path; - break; - } - - Objects.requireNonNull(jarPath, "Could not find minecraft server jar for " + minecraftVersion()); - ZipEntry serverJarEntry = zipFile.getEntry("META-INF/versions/" + jarPath); - Objects.requireNonNull(serverJarEntry, "Could not find server jar in boostrap@ " + jarPath); - - try (InputStream is = zipFile.getInputStream(serverJarEntry)) { - java.nio.file.Files.copy(is, minecraftExtractedServerJar.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - - return minecraftExtractedServerJar; + try (JarMerger jarMerger = new JarMerger(minecraftClientJar, jarToMerge, minecraftMergedJar)) { + jarMerger.enableSyntheticParamsOffset(); + jarMerger.merge(); } } @@ -324,20 +285,6 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra return workingDir; } - @Override - public boolean hasCustomNatives() { - return getProject().getProperties().get("fabric.loom.natives.dir") != null; - } - - @Override - public File nativesDir() { - if (hasCustomNatives()) { - return new File((String) getProject().property("fabric.loom.natives.dir")); - } - - return dir("natives"); - } - @Override public File dir(String path) { File dir = file(path); @@ -368,4 +315,9 @@ public class MinecraftProviderImpl extends DependencyProvider implements Minecra public String getTargetConfig() { return Constants.Configurations.MINECRAFT; } + + @Nullable + public BundleMetadata getServerBundleMetadata() { + return serverBundleMetadata; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java new file mode 100644 index 00000000..9471cafe --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/LWJGLVersionOverride.java @@ -0,0 +1,108 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.minecraft; + +import static net.fabricmc.loom.util.OperatingSystem.LINUX; +import static net.fabricmc.loom.util.OperatingSystem.MAC_OS; +import static net.fabricmc.loom.util.OperatingSystem.WINDOWS; + +import java.util.List; + +import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.util.Architecture; +import net.fabricmc.loom.util.OperatingSystem; + +public class LWJGLVersionOverride { + public static final String LWJGL_VERSION = "3.3.0"; + @Nullable + public static final String NATIVE_CLASSIFIER = getNativesClassifier(); + + public static final List DEPENDENCIES = List.of( + "org.lwjgl:lwjgl:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-glfw:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-jemalloc:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-openal:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-opengl:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-stb:" + LWJGL_VERSION, + "org.lwjgl:lwjgl-tinyfd:" + LWJGL_VERSION + ); + public static final List NATIVES = DEPENDENCIES.stream().map(s -> s + ":" + NATIVE_CLASSIFIER).toList(); + + /** + * Update lwjgl by default when running on arm and a supported configuration. + */ + public static boolean overrideByDefault() { + return NATIVE_CLASSIFIER != null && Architecture.CURRENT.isArm(); + } + + public static boolean forceOverride(Project project) { + return project.getProperties().get("fabric.loom.override-lwjgl") != null; + } + + @Nullable + private static String getNativesClassifier() { + return switch (OperatingSystem.CURRENT_OS) { + case WINDOWS -> getWindowsClassifier(); + case MAC_OS -> getMacOSClassifier(); + case LINUX -> getLinuxClassifier(); + default -> null; + }; + } + + private static String getWindowsClassifier() { + if (Architecture.CURRENT.is64Bit()) { + if (Architecture.CURRENT.isArm()) { + // Arm 64 bit + return "natives-windows-arm64"; + } + + // None arm 64bit + return "natives-windows"; + } + + // All 32bit, including arm + return "natives-windows-x86"; + } + + private static String getMacOSClassifier() { + if (Architecture.CURRENT.isArm()) { + // Apple silicone arm + return "natives-macos-arm64"; + } + + // Intel 64bit. + return "natives-macos"; + } + + private static String getLinuxClassifier() { + if (Architecture.CURRENT.isArm()) { + return Architecture.CURRENT.is64Bit() ? "natives-linux-arm64" : "natives-linux-arm32"; + } + + return "natives-linux"; + } +} 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 53c9412d..3e4a3c49 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 @@ -24,31 +24,77 @@ package net.fabricmc.loom.configuration.providers.minecraft; -import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.gradle.api.Project; +import org.gradle.api.artifacts.ExternalModuleDependency; -import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.BundleMetadata; import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.util.Constants; public class MinecraftLibraryProvider { - public File MINECRAFT_LIBS; + private static final Pattern NATIVES_PATTERN = Pattern.compile("^(?.*)/(.*?)/(?.*)/((?.*?)-([0-9].*?)-)(?.*).jar$"); public void provide(MinecraftProviderImpl minecraftProvider, Project project) { MinecraftVersionMeta versionInfo = minecraftProvider.getVersionInfo(); + BundleMetadata serverBundleMetadata = minecraftProvider.getServerBundleMetadata(); - initFiles(project, minecraftProvider); + final boolean overrideLWJGL = LWJGLVersionOverride.overrideByDefault() || LWJGLVersionOverride.forceOverride(project) || Boolean.getBoolean("loom.test.lwjgloverride"); + + if (overrideLWJGL) { + project.getLogger().warn("Loom is upgrading Minecraft's LWJGL version to {}", LWJGLVersionOverride.LWJGL_VERSION); + } for (MinecraftVersionMeta.Library library : versionInfo.libraries()) { - if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) { - project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, project.getDependencies().module(library.name())); + if (overrideLWJGL && library.name().startsWith("org.lwjgl")) { + continue; } + + if (library.isValidForOS() && !library.hasNatives() && library.artifact() != null) { + if (serverBundleMetadata != null && isLibraryInBundle(serverBundleMetadata, library)) { + project.getDependencies().add(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES, library.name()); + } else { + // Client only library, or legacy version + project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, library.name()); + } + } + + if (library.hasNativesForOS()) { + MinecraftVersionMeta.Download nativeDownload = library.classifierForOS(); + + Matcher matcher = NATIVES_PATTERN.matcher(nativeDownload.path()); + + if (!matcher.find()) { + project.getLogger().warn("Failed to match regex for natives path : " + nativeDownload.path()); + continue; + } + + final String group = matcher.group("group").replace("/", "."); + final String name = matcher.group("name"); + final String version = matcher.group("version"); + final String classifier = matcher.group("classifier"); + + final String dependencyNotation = "%s:%s:%s:%s".formatted(group, name, version, classifier); + + project.getLogger().debug("Add native dependency '{}'", dependencyNotation); + project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, dependencyNotation); + } + } + + if (overrideLWJGL) { + LWJGLVersionOverride.DEPENDENCIES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_DEPENDENCIES, s)); + LWJGLVersionOverride.NATIVES.forEach(s -> project.getDependencies().add(Constants.Configurations.MINECRAFT_NATIVES, s)); + + // Add the native support mod that fixes a handful of issues related to the LWJGL update at runtime. + ExternalModuleDependency dependency = (ExternalModuleDependency) project.getDependencies().create(Constants.Dependencies.NATIVE_SUPPORT + Constants.Dependencies.Versions.NATIVE_SUPPORT_VERSION); + dependency.setTransitive(false); + project.getDependencies().add("modLocalRuntime", dependency); } } - private void initFiles(Project project, MinecraftProviderImpl minecraftProvider) { - LoomGradleExtension extension = LoomGradleExtension.get(project); - MINECRAFT_LIBS = new File(extension.getFiles().getUserCache(), "libraries"); + private static boolean isLibraryInBundle(BundleMetadata bundleMetadata, MinecraftVersionMeta.Library library) { + return bundleMetadata.libraries().stream().anyMatch(entry -> entry.name().equals(library.name())); } } 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 264b855c..0025e91d 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 @@ -155,7 +155,7 @@ public class MinecraftMappedProvider extends DependencyProvider { // Remap the sig fixes from intermediary to the target namespace final Map remapped = new HashMap<>(); final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(getProject(), MappingsNamespace.INTERMEDIARY.toString(), toM); - final Remapper sigAsmRemapper = sigTinyRemapper.getRemapper(); + final Remapper sigAsmRemapper = sigTinyRemapper.getEnvironment().getRemapper(); // Remap the class names and the signatures using a new tiny remapper instance. for (Map.Entry entry : mappingsProvider.getSignatureFixes().entrySet()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java deleted file mode 100644 index 03904aa6..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftNativesProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration.providers.minecraft; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -import org.apache.commons.io.FileUtils; -import org.gradle.api.GradleException; -import org.gradle.api.Project; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.util.HashedDownloadUtil; -import net.fabricmc.loom.util.MirrorUtil; -import net.fabricmc.loom.util.ZipUtils; - -public class MinecraftNativesProvider { - private final Project project; - private final LoomGradleExtension extension; - private final File nativesDir; - private final File jarStore; - - public MinecraftNativesProvider(Project project) { - this.project = project; - extension = LoomGradleExtension.get(project); - - nativesDir = extension.getMinecraftProvider().nativesDir(); - jarStore = extension.getFiles().getNativesJarStore(); - } - - public static void provide(Project project) throws IOException { - new MinecraftNativesProvider(project).provide(); - } - - private void provide() throws IOException { - if (extension.getMinecraftProvider().hasCustomNatives()) { - if (!nativesDir.exists()) { - throw new RuntimeException("Could no find custom natives directory at " + nativesDir.getAbsolutePath()); - } - - return; - } - - if (!LoomGradlePlugin.refreshDeps && !requiresExtract()) { - project.getLogger().info("Natives do no need extracting, skipping"); - return; - } - - extractNatives(); - } - - private void extractNatives() throws IOException { - boolean offline = project.getGradle().getStartParameter().isOffline(); - - if (nativesDir.exists()) { - try { - FileUtils.deleteDirectory(nativesDir); - } catch (IOException e) { - throw new IOException("Failed to delete the natives directory, is the game running?", e); - } - } - - nativesDir.mkdirs(); - - for (MinecraftVersionMeta.Download library : getNatives()) { - File libJarFile = library.relativeFile(jarStore); - - if (!offline) { - HashedDownloadUtil.downloadIfInvalid(new URL(MirrorUtil.getLibrariesBase(project) + library.path()), libJarFile, library.sha1(), project.getLogger(), false); - } - - if (!libJarFile.exists()) { - throw new GradleException("Native jar not found at " + libJarFile.getAbsolutePath()); - } - - ZipUtils.unpackAll(libJarFile.toPath(), nativesDir.toPath()); - - // Store a file containing the hash of the extracted natives, used on subsequent runs to skip extracting all the natives if they haven't changed - File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); - FileUtils.writeStringToFile(libSha1File, library.sha1(), StandardCharsets.UTF_8); - } - } - - private boolean requiresExtract() { - List natives = getNatives(); - - if (natives.isEmpty()) { - throw new IllegalStateException("No natives found for the current system"); - } - - for (MinecraftVersionMeta.Download library : natives) { - File libJarFile = library.relativeFile(jarStore); - File libSha1File = new File(nativesDir, libJarFile.getName() + ".sha1"); - - if (!libSha1File.exists()) { - return true; - } - - try { - String sha1 = FileUtils.readFileToString(libSha1File, StandardCharsets.UTF_8); - - if (!sha1.equalsIgnoreCase(library.sha1())) { - return true; - } - } catch (IOException e) { - project.getLogger().error("Failed to read " + libSha1File.getAbsolutePath(), e); - return true; - } - } - - // All looks good, no need to re-extract - return false; - } - - private List getNatives() { - return extension.getMinecraftProvider().getVersionInfo().libraries().stream() - .filter((MinecraftVersionMeta.Library::hasNativesForOS)) - .map(MinecraftVersionMeta.Library::classifierForOS) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java index 58a20dba..86c16286 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftVersionMeta.java @@ -81,7 +81,7 @@ public record MinecraftVersionMeta( return false; } - if (natives.get(OperatingSystem.getOS()) == null) { + if (natives.get(OperatingSystem.CURRENT_OS) == null) { return false; } @@ -89,7 +89,7 @@ public record MinecraftVersionMeta( } public Download classifierForOS() { - return downloads().classifier(natives.get(OperatingSystem.getOS())); + return downloads().classifier(natives.get(OperatingSystem.CURRENT_OS)); } public Download artifact() { @@ -119,7 +119,7 @@ public record MinecraftVersionMeta( public record OS(String name) { public boolean isValidForOS() { - return name() == null || name().equalsIgnoreCase(OperatingSystem.getOS()); + return name() == null || name().equalsIgnoreCase(OperatingSystem.CURRENT_OS); } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFiles.java b/src/main/java/net/fabricmc/loom/extension/LoomFiles.java index 66307c18..4fc07bed 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFiles.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFiles.java @@ -43,7 +43,7 @@ public interface LoomFiles { File getProjectPersistentCache(); File getProjectBuildCache(); File getRemappedModCache(); - File getNativesJarStore(); + File getNativesDirectory(Project project); File getDefaultLog4jConfigFile(); File getDevLauncherConfig(); File getUnpickLoggingConfigFile(); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java index f99395eb..a79bb56f 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomFilesBaseImpl.java @@ -26,6 +26,10 @@ package net.fabricmc.loom.extension; import java.io.File; +import org.gradle.api.Project; + +import net.fabricmc.loom.LoomGradleExtension; + public abstract class LoomFilesBaseImpl implements LoomFiles { protected abstract File getGradleUserHomeDir(); protected abstract File getRootDir(); @@ -70,8 +74,8 @@ public abstract class LoomFilesBaseImpl implements LoomFiles { } @Override - public File getNativesJarStore() { - return createFile(getUserCache(), "natives/jars"); + public File getNativesDirectory(Project project) { + return createFile(getRootProjectPersistentCache(), "natives/" + LoomGradleExtension.get(project).getMinecraftProvider().minecraftVersion()); } @Override diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index da0fca73..5c98ac44 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; import org.cadixdev.lorenz.MappingSet; import org.cadixdev.mercury.Mercury; +import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -145,8 +146,8 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen } @Override - public NamedDomainObjectProvider createLazyConfiguration(String name) { - NamedDomainObjectProvider provider = project.getConfigurations().register(name); + public NamedDomainObjectProvider createLazyConfiguration(String name, Action configurationAction) { + NamedDomainObjectProvider provider = project.getConfigurations().register(name, configurationAction); if (lazyConfigurations.containsKey(name)) { throw new IllegalStateException("Duplicate configuration name" + name); diff --git a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java index cf958d99..58838e0e 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinExtensionApiImpl.java @@ -29,7 +29,7 @@ import java.util.Objects; import org.gradle.api.Action; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Project; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; @@ -110,8 +110,7 @@ public abstract class MixinExtensionApiImpl implements MixinExtensionAPI { private SourceSet resolveSourceSet(String sourceSetName) { // try to find sourceSet with name sourceSetName in this project - SourceSet sourceSet = project.getConvention().getPlugin(JavaPluginConvention.class) - .getSourceSets().findByName(sourceSetName); + SourceSet sourceSet = project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().findByName(sourceSetName); if (sourceSet == null) { throw new InvalidUserDataException("No sourceSet " + sourceSetName + " was found"); diff --git a/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java index 074251e4..c8bf56a0 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java @@ -38,8 +38,8 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.UnknownTaskException; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.plugins.BasePluginConvention; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.BasePluginExtension; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; @@ -67,7 +67,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx } private String getDefaultMixinRefmapName() { - String defaultRefmapName = project.getConvention().getPlugin(BasePluginConvention.class).getArchivesBaseName() + "-refmap.json"; + String defaultRefmapName = project.getExtensions().getByType(BasePluginExtension.class).getArchivesName().get() + "-refmap.json"; project.getLogger().info("Could not find refmap definition, will be using default name: " + defaultRefmapName); return defaultRefmapName; } @@ -87,7 +87,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx @Override @NotNull public Stream getMixinSourceSetsStream() { - return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().stream() + return project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().stream() .filter(sourceSet -> MixinExtension.getMixinInformationContainer(sourceSet) != null); } @@ -122,7 +122,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx @Override public void init() { if (isDefault) { - project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().forEach(this::add); + project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().forEach(this::add); } isDefault = false; diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index 6bb63e53..baa5c931 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -46,6 +46,7 @@ public abstract class AbstractRunTask extends JavaExec { setClasspath(config.sourceSet.getRuntimeClasspath()); args(config.programArgs); + getMainClass().set(config.mainClass); } @Override @@ -64,11 +65,6 @@ public abstract class AbstractRunTask extends JavaExec { super.setWorkingDir(dir); } - @Override - public String getMain() { - return config.mainClass; - } - @Override public List getJvmArgs() { List superArgs = super.getJvmArgs(); diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index d862ea50..f03a1079 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -30,7 +30,6 @@ import org.gradle.api.Project; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.configuration.providers.minecraft.MinecraftNativesProvider; import net.fabricmc.loom.configuration.providers.minecraft.assets.MinecraftAssetsProvider; public class DownloadAssetsTask extends AbstractLoomTask { @@ -40,6 +39,5 @@ public class DownloadAssetsTask extends AbstractLoomTask { LoomGradleExtension extension = getExtension(); MinecraftAssetsProvider.provide(extension.getMinecraftProvider(), project); - MinecraftNativesProvider.provide(project); } } diff --git a/src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java b/src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java new file mode 100644 index 00000000..0086ec47 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/ExtractNativesTask.java @@ -0,0 +1,50 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task; + +import java.io.File; + +import javax.inject.Inject; + +import org.gradle.api.tasks.Sync; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.util.Constants; + +public abstract class ExtractNativesTask extends Sync { + @Inject + public ExtractNativesTask() { + // Is there a lazy way to do this for many files? - Doesnt seem there is... + for (File nativeFile : getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_NATIVES).getFiles()) { + from(getProject().zipTree(nativeFile), copySpec -> { + copySpec.exclude("META-INF/**"); + }); + } + + into(LoomGradleExtension.get(getProject()).getFiles().getNativesDirectory(getProject())); + + setDescription("Downloads and extracts the minecraft natives"); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 70c6a09e..42732f88 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -43,6 +43,7 @@ public final class LoomTasks { public static void registerTasks(Project project) { TaskContainer tasks = project.getTasks(); + LoomGradleExtension extension = LoomGradleExtension.get(project); tasks.register("migrateMappings", MigrateMappingsTask.class, t -> { t.setDescription("Migrates mappings to a new version."); @@ -54,7 +55,11 @@ public final class LoomTasks { t.setGroup(Constants.TaskGroup.FABRIC); }); - tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric.")); + TaskProvider extractNatives = tasks.register("extractNatives", ExtractNativesTask.class); + tasks.register("downloadAssets", DownloadAssetsTask.class, t -> { + t.dependsOn(extractNatives); + t.setDescription("Downloads required assets for Fabric."); + }); tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names.")); TaskProvider validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { diff --git a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java index c976dded..053c93b9 100644 --- a/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MigrateMappingsTask.java @@ -42,7 +42,7 @@ import org.gradle.api.IllegalDependencyNotation; import org.gradle.api.JavaVersion; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; -import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.options.Option; @@ -171,12 +171,7 @@ public class MigrateMappingsTask extends AbstractLoomTask { project.getLogger().lifecycle(":remapping"); Mercury mercury = SourceRemapper.createMercuryWithClassPath(project, false); - final JavaPluginConvention convention = project.getConvention().findPlugin(JavaPluginConvention.class); - final JavaVersion javaVersion = convention != null - ? - convention.getSourceCompatibility() - : - JavaVersion.current(); + final JavaVersion javaVersion = project.getExtensions().getByType(JavaPluginExtension.class).getSourceCompatibility(); mercury.setSourceCompatibility(javaVersion.toString()); mercury.getClassPath().add(minecraftMappedProvider.getMappedJar().toPath()); diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 600d6821..62dd8817 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -124,7 +124,7 @@ public class RemapJarTask extends Jar { Project project = getProject(); LoomGradleExtension extension = LoomGradleExtension.get(getProject()); Path input = this.getInput().getAsFile().get().toPath(); - Path output = this.getArchivePath().toPath(); + Path output = this.getArchiveFile().get().getAsFile().toPath(); if (!Files.exists(input)) { throw new FileNotFoundException(input.toString()); diff --git a/src/main/java/net/fabricmc/loom/util/Architecture.java b/src/main/java/net/fabricmc/loom/util/Architecture.java new file mode 100644 index 00000000..3208a306 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/Architecture.java @@ -0,0 +1,47 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +public class Architecture { + public static final Architecture CURRENT = new Architecture(System.getProperty("os.arch")); + + private final String name; + + public Architecture(String name) { + this.name = name; + } + + public boolean is64Bit() { + return name.contains("64") || name.startsWith("armv8"); + } + + public boolean isArm() { + return name.startsWith("arm") || name.startsWith("aarch64"); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/net/fabricmc/loom/util/ClosureAction.java b/src/main/java/net/fabricmc/loom/util/ClosureAction.java new file mode 100644 index 00000000..887f00f5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/ClosureAction.java @@ -0,0 +1,36 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +import groovy.lang.Closure; +import org.gradle.api.Action; + +public record ClosureAction(Closure closure) implements Action { + @Override + public void execute(T t) { + closure.setDelegate(t); + closure.call(t); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 44b1d405..5a06327e 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -40,8 +40,6 @@ public class Constants { public static final String EXPERIMENTAL_VERSIONS = "https://maven.fabricmc.net/net/minecraft/experimental_versions.json"; public static final String FABRIC_REPOSITORY = "https://maven.fabricmc.net/"; - public static final String SYSTEM_ARCH = System.getProperty("os.arch").equals("64") ? "64" : "32"; - public static final int ASM_VERSION = Opcodes.ASM9; public static final List MOD_COMPILE_ENTRIES = ImmutableList.of( @@ -64,8 +62,13 @@ public class Constants { public static final String MOD_COMPILE_CLASSPATH_MAPPED = "modCompileClasspathMapped"; public static final String INCLUDE = "include"; public static final String MINECRAFT = "minecraft"; + /** + * The server specific configuration will be empty when using a legacy (pre 21w38a server jar) + * find the client only dependencies on the "minecraftLibraries" config. + */ + public static final String MINECRAFT_SERVER_DEPENDENCIES = "minecraftServerLibraries"; public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries"; - public static final String MINECRAFT_REMAP_CLASSPATH = "minecraftRemapClasspath"; + public static final String MINECRAFT_NATIVES = "minecraftNatives"; public static final String MINECRAFT_NAMED = "minecraftNamed"; public static final String MAPPINGS = "mappings"; public static final String MAPPINGS_FINAL = "mappingsFinal"; @@ -93,6 +96,7 @@ public class Constants { public static final String DEV_LAUNCH_INJECTOR = "net.fabricmc:dev-launch-injector:"; public static final String TERMINAL_CONSOLE_APPENDER = "net.minecrell:terminalconsoleappender:"; public static final String JETBRAINS_ANNOTATIONS = "org.jetbrains:annotations:"; + public static final String NATIVE_SUPPORT = "net.fabricmc:fabric-loom-native-support:"; private Dependencies() { } @@ -105,6 +109,7 @@ public class Constants { public static final String DEV_LAUNCH_INJECTOR = "0.2.1+build.8"; public static final String TERMINAL_CONSOLE_APPENDER = "1.2.0"; public static final String JETBRAINS_ANNOTATIONS = "23.0.0"; + public static final String NATIVE_SUPPORT_VERSION = "1.0.1"; private Versions() { } diff --git a/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java b/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java index 377bb8cb..fb87072f 100644 --- a/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java +++ b/src/main/java/net/fabricmc/loom/util/FileSystemUtil.java @@ -28,9 +28,12 @@ import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.FileSystemAlreadyExistsException; import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collections; import java.util.Map; @@ -38,6 +41,20 @@ import java.util.function.Supplier; public final class FileSystemUtil { public record Delegate(FileSystem fs, boolean owner) implements AutoCloseable, Supplier { + public byte[] readAllBytes(String path) throws IOException { + Path fsPath = get().getPath(path); + + if (Files.exists(fsPath)) { + return Files.readAllBytes(fsPath); + } else { + throw new NoSuchFileException(fsPath.toString()); + } + } + + public String readString(String path) throws IOException { + return new String(readAllBytes(path), StandardCharsets.UTF_8); + } + @Override public void close() throws IOException { if (owner) { @@ -65,6 +82,10 @@ public final class FileSystemUtil { return getJarFileSystem(path.toUri(), create); } + public static Delegate getJarFileSystem(Path path) throws IOException { + return getJarFileSystem(path, false); + } + public static Delegate getJarFileSystem(URI uri, boolean create) throws IOException { URI jarUri; diff --git a/src/main/java/net/fabricmc/loom/util/ModUtils.java b/src/main/java/net/fabricmc/loom/util/ModUtils.java index 527bd7dc..d49b5188 100644 --- a/src/main/java/net/fabricmc/loom/util/ModUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ModUtils.java @@ -25,18 +25,12 @@ package net.fabricmc.loom.util; import java.io.File; -import java.io.IOException; -import java.util.zip.ZipFile; public final class ModUtils { private ModUtils() { } public static boolean isMod(File input) { - try (ZipFile zipFile = new ZipFile(input)) { - return zipFile.getEntry("fabric.mod.json") != null; - } catch (IOException e) { - return false; - } + return ZipUtils.contains(input.toPath(), "fabric.mod.json"); } } diff --git a/src/main/java/net/fabricmc/loom/util/OperatingSystem.java b/src/main/java/net/fabricmc/loom/util/OperatingSystem.java index 9df41edb..1e9bdc96 100644 --- a/src/main/java/net/fabricmc/loom/util/OperatingSystem.java +++ b/src/main/java/net/fabricmc/loom/util/OperatingSystem.java @@ -30,23 +30,21 @@ import java.net.StandardProtocolFamily; import java.nio.channels.ServerSocketChannel; public class OperatingSystem { - public static String getOS() { + public static final String WINDOWS = "windows"; + public static final String MAC_OS = "osx"; + public static final String LINUX = "linux"; + + public static final String CURRENT_OS = getOS(); + + private static String getOS() { String osName = System.getProperty("os.name").toLowerCase(); if (osName.contains("win")) { - return "windows"; + return WINDOWS; } else if (osName.contains("mac")) { - return "osx"; + return MAC_OS; } else { - return "linux"; - } - } - - public static String getArch() { - if (is64Bit()) { - return "64"; - } else { - return "32"; + return LINUX; } } @@ -54,10 +52,6 @@ public class OperatingSystem { return System.getProperty("sun.arch.data.model").contains("64"); } - public static boolean isWindows() { - return getOS().equals("windows"); - } - public static boolean isCIBuild() { String loomProperty = System.getProperty("fabric.loom.ci"); diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java index 3416db37..2843a50f 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java @@ -83,13 +83,7 @@ public class ZipUtils { public static byte[] unpack(Path zip, String path) throws IOException { try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(zip, false)) { - Path fsPath = fs.get().getPath(path); - - if (Files.exists(fsPath)) { - return Files.readAllBytes(fsPath); - } else { - throw new NoSuchFileException(fsPath.toString()); - } + return fs.readAllBytes(path); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index aaca59c8..ef596a75 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.4-20211203231050+0000" + public final static String PRE_RELEASE_GRADLE = "7.4-20211216231505+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/NativesTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/NativesTest.groovy new file mode 100644 index 00000000..76d5c438 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/NativesTest.groovy @@ -0,0 +1,86 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.integration + +import net.fabricmc.loom.test.util.GradleProjectTestTrait +import spock.lang.Specification +import spock.lang.Unroll +import spock.util.environment.RestoreSystemProperties + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS +import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE + +class NativesTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "Default natives for 1.18 (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18" + mappings loom.officialMojangMappings() + } + ''' + + when: + // Run the task twice to ensure its up to date + def result = gradle.run(task: "extractNatives") + def result2 = gradle.run(task: "extractNatives") + + then: + result.task(":extractNatives").outcome == SUCCESS + result2.task(":extractNatives").outcome == UP_TO_DATE + + where: + version << STANDARD_TEST_VERSIONS + } + + @RestoreSystemProperties + @Unroll + def "Overridden natives for 1.18 (gradle #version)"() { + setup: + System.setProperty('loom.test.lwjgloverride', "true") + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18" + mappings loom.officialMojangMappings() + } + ''' + + when: + // Run the task twice to ensure its up to date + def result = gradle.run(task: "extractNatives") + + then: + result.task(":extractNatives").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } +} 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 index 572545a8..7164f4fe 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/LayeredMappingSpecBuilderTest.groovy @@ -30,29 +30,29 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuil 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 net.fabricmc.loom.util.ClosureAction import spock.lang.Specification class LayeredMappingSpecBuilderTest extends Specification { def "simple mojmap" () { when: - def spec = layered() { + def spec = layered { officialMojangMappings() } def layers = spec.layers() then: - spec.version == "layered+hash.2198" layers.size() == 2 + spec.version == "layered+hash.2198" layers[0].class == IntermediaryMappingsSpec layers[1].class == MojangMappingsSpec } def "simple mojmap with parchment" () { when: + def dep = "I like cake" def spec = layered() { officialMojangMappings() - parchment("I like cake") + parchment(dep) } def layers = spec.layers() def parchment = layers[2] as ParchmentMappingsSpec @@ -88,7 +88,7 @@ class LayeredMappingSpecBuilderTest extends Specification { def "simple mojmap with parchment keep prefix alternate hash" () { when: - def spec = layered() { + def spec = layered { officialMojangMappings() parchment("I really like cake") { it.removePrefix = false @@ -106,14 +106,9 @@ class LayeredMappingSpecBuilderTest extends Specification { parchment.removePrefix() == false } - // Gradle does this big of magic behind the scenes LayeredMappingSpec layered(@DelegatesTo(LayeredMappingSpecBuilderImpl) Closure cl) { - return layeredAction(ConfigureUtil.configureUsing(cl)) - } - - LayeredMappingSpec layeredAction(Action action) { LayeredMappingSpecBuilderImpl builder = new LayeredMappingSpecBuilderImpl() - action.execute(builder) + new ClosureAction(cl).execute(builder) return builder.build() } } From ab21e0e550f483ce446e8c8115b80b77f8eb7adf Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 20 Dec 2021 16:29:11 +0000 Subject: [PATCH 02/15] Rewrire Jar and Source remapping tasks. (#504) --- .../fabricmc/loom/LoomGradleExtension.java | 7 +- .../net/fabricmc/loom/build/JarRemapper.java | 159 ------- .../loom/build/MixinRefmapHelper.java | 61 +-- .../mixin/AnnotationProcessorInvoker.java | 2 +- .../build/nesting/IncludedJarFactory.java | 203 +++++++++ .../loom/build/nesting/JarNester.java | 3 +- .../nesting/MergedNestedJarProvider.java | 47 -- .../nesting/NestedDependencyProvider.java | 282 ------------ .../build/nesting/NestedJarPathProvider.java | 69 --- .../loom/build/nesting/NestedJarProvider.java | 40 -- .../configuration/CompileConfiguration.java | 12 +- .../JarManifestConfiguration.java | 92 ---- .../configuration/RemapConfiguration.java | 220 --------- .../accesswidener/AccessWidenerFile.java | 4 +- .../AccessWidenerJarProcessor.java | 23 - .../mappings/MappingsProviderImpl.java | 4 + .../extension/LoomGradleExtensionImpl.java | 18 +- .../loom/task/AbstractRemapJarTask.java | 123 ++++++ .../net/fabricmc/loom/task/LoomTasks.java | 6 +- .../loom/task/RemapAllSourcesTask.java | 38 -- .../net/fabricmc/loom/task/RemapJarTask.java | 417 +++++++++--------- .../loom/task/RemapSourcesJarTask.java | 109 +++-- .../loom/task/RemapTaskConfiguration.java | 138 ++++++ .../loom/task/service/JarManifestService.java | 123 ++++++ .../loom/task/service/MappingsService.java | 118 +++++ .../task/service/SourceRemapperService.java | 133 ++++++ .../fabricmc/loom/util/SourceRemapper.java | 7 +- .../loom/util/TinyRemapperHelper.java | 7 + .../loom/test/LoomTestConstants.groovy | 2 +- .../test/integration/FabricAPITest.groovy | 2 +- .../test/integration/MixinApSimpleTest.groovy | 2 +- .../test/integration/MultiProjectTest.groovy | 3 - .../test/integration/SimpleProjectTest.groovy | 1 + .../unit/MergedNestedJarProviderTest.groovy | 55 --- src/test/resources/patches/fabric_api.patch | 44 +- .../resources/projects/simple/build.gradle | 3 + 36 files changed, 1154 insertions(+), 1423 deletions(-) delete mode 100644 src/main/java/net/fabricmc/loom/build/JarRemapper.java create mode 100644 src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java delete mode 100644 src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java delete mode 100644 src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java delete mode 100644 src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java delete mode 100644 src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java delete mode 100644 src/main/java/net/fabricmc/loom/configuration/JarManifestConfiguration.java delete mode 100644 src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java create mode 100644 src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java delete mode 100644 src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java create mode 100644 src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java create mode 100644 src/main/java/net/fabricmc/loom/task/service/JarManifestService.java create mode 100644 src/main/java/net/fabricmc/loom/task/service/MappingsService.java create mode 100644 src/main/java/net/fabricmc/loom/task/service/SourceRemapperService.java delete mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index 5d4e88e7..f1edd74c 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -26,7 +26,6 @@ package net.fabricmc.loom; import java.io.File; import java.util.List; -import java.util.Set; import java.util.function.Supplier; import org.cadixdev.lorenz.MappingSet; @@ -36,6 +35,8 @@ import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.api.LoomGradleExtensionAPI; import net.fabricmc.loom.configuration.InstallerData; @@ -94,9 +95,9 @@ public interface LoomGradleExtension extends LoomGradleExtensionAPI { return getMappingsProvider().mappedProvider; } - File getNextMixinMappings(); + File getMixinMappings(SourceSet sourceSet); - Set getAllMixinMappings(); + FileCollection getAllMixinMappings(); boolean isRootProject(); diff --git a/src/main/java/net/fabricmc/loom/build/JarRemapper.java b/src/main/java/net/fabricmc/loom/build/JarRemapper.java deleted file mode 100644 index 877d8784..00000000 --- a/src/main/java/net/fabricmc/loom/build/JarRemapper.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2020 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.build; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - -import org.gradle.api.Action; -import org.objectweb.asm.commons.Remapper; - -import net.fabricmc.loom.util.CloseableList; -import net.fabricmc.stitch.util.Pair; -import net.fabricmc.tinyremapper.IMappingProvider; -import net.fabricmc.tinyremapper.InputTag; -import net.fabricmc.tinyremapper.OutputConsumerPath; -import net.fabricmc.tinyremapper.TinyRemapper; - -public class JarRemapper { - private final List mappingProviders = new ArrayList<>(); - private final Set classPath = new HashSet<>(); - private final List remapData = new ArrayList<>(); - private List> remapOptions; - - public void addMappings(IMappingProvider mappingProvider) { - mappingProviders.add(mappingProvider); - } - - public void addToClasspath(Path... paths) { - classPath.addAll(Arrays.asList(paths)); - } - - public RemapData scheduleRemap(Path input, Path output) { - RemapData data = new RemapData(input, output); - remapData.add(data); - return data; - } - - public void remap() throws IOException { - TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper(); - mappingProviders.forEach(remapperBuilder::withMappings); - - if (remapOptions != null) { - for (Action remapOption : remapOptions) { - remapOption.execute(remapperBuilder); - } - } - - TinyRemapper remapper = remapperBuilder.build(); - - Path[] remapClasspath = classPath.stream() - .filter(path -> - remapData.stream().noneMatch(remapData -> remapData.input.equals(path)) - ) - .toArray(Path[]::new); - - remapper.readClassPathAsync(remapClasspath); - - for (RemapData data : remapData) { - InputTag tag = remapper.createInputTag(); - data.tag = tag; - remapper.readInputsAsync(tag, data.input); - } - - //noinspection MismatchedQueryAndUpdateOfCollection - try (CloseableList outputConsumers = new CloseableList<>()) { - for (RemapData data : remapData) { - OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(data.output).build(); - outputConsumers.add(outputConsumer); - - outputConsumer.addNonClassFiles(data.input); - - data.processAccessWidener(remapper.getEnvironment().getRemapper()); - remapper.apply(outputConsumer, data.tag); - } - - remapper.finish(); - } catch (Exception e) { - for (RemapData data : remapData) { - // Cleanup bad outputs - Files.deleteIfExists(data.output); - } - - throw new IOException("Failed to remap %s files".formatted(remapData.size()), e); - } - - remapData.forEach(RemapData::complete); - } - - public void addOptions(List> remapOptions) { - this.remapOptions = remapOptions; - } - - public static class RemapData { - public final Path input; - public final Path output; - BiFunction> accesWidenerSupplier; - BiConsumer> onComplete; - - private InputTag tag; - private Pair accessWidener; - - public RemapData(Path input, Path output) { - this.input = input; - this.output = output; - } - - public RemapData complete(BiConsumer> onComplete) { - this.onComplete = onComplete; - return this; - } - - public RemapData supplyAccessWidener(BiFunction> beforeFinish) { - this.accesWidenerSupplier = beforeFinish; - return this; - } - - private void complete() { - if (onComplete != null) { - onComplete.accept(this, accessWidener); - } - } - - private void processAccessWidener(Remapper remapper) { - if (accesWidenerSupplier != null) { - accessWidener = accesWidenerSupplier.apply(this, remapper); - } - } - } -} diff --git a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java index 4d1ba261..49cde0d7 100644 --- a/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java +++ b/src/main/java/net/fabricmc/loom/build/MixinRefmapHelper.java @@ -27,13 +27,9 @@ package net.fabricmc.loom.build; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.io.UncheckedIOException; -import java.nio.file.Path; import java.util.Collection; import java.util.Collections; -import java.util.Objects; import java.util.stream.Collectors; -import java.util.stream.Stream; import java.util.stream.StreamSupport; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -41,74 +37,35 @@ import java.util.zip.ZipFile; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import org.gradle.api.Project; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.extension.MixinExtension; -import net.fabricmc.loom.util.Pair; -import net.fabricmc.loom.util.ZipUtils; public final class MixinRefmapHelper { private MixinRefmapHelper() { } private static final String FABRIC_MOD_JSON = "fabric.mod.json"; - public static boolean addRefmapName(Project project, Path outputPath) { - try { - MixinExtension mixin = LoomGradleExtension.get(project).getMixin(); - File output = outputPath.toFile(); - - Collection allMixinConfigs = getMixinConfigurationFiles(readFabricModJson(output)); - - return mixin.getMixinSourceSetsStream().map(sourceSet -> { - MixinExtension.MixinInformationContainer container = Objects.requireNonNull( - MixinExtension.getMixinInformationContainer(sourceSet) - ); - - Stream mixinConfigs = sourceSet.getResources() - .matching(container.mixinConfigPattern()) - .getFiles() - .stream() - .map(File::getName) - .filter(allMixinConfigs::contains); - - String refmapName = container.refmapNameProvider().get(); - - try { - return ZipUtils.transformJson(JsonObject.class, outputPath, mixinConfigs.map(f -> new Pair<>(f, json -> { - if (!json.has("refmap")) { - json.addProperty("refmap", refmapName); - } - - return json; - }))) > 0; - } catch (IOException e) { - throw new UncheckedIOException("Failed to transform mixin configs in jar", e); - } - }).reduce(false, Boolean::logicalOr); - } catch (Exception e) { - project.getLogger().error(e.getMessage()); - return false; - } - } - - @NotNull - private static JsonObject readFabricModJson(File output) { + @Nullable + public static JsonObject readFabricModJson(File output) { try (ZipFile zip = new ZipFile(output)) { ZipEntry entry = zip.getEntry(FABRIC_MOD_JSON); + if (entry == null) { + return null; + } + try (InputStreamReader reader = new InputStreamReader(zip.getInputStream(entry))) { return LoomGradlePlugin.GSON.fromJson(reader, JsonObject.class); } } catch (IOException e) { - throw new RuntimeException("Cannot read file fabric.mod.json in the output jar.", e); + throw new RuntimeException("Cannot read file fabric.mod.json in the jar.", e); } } @NotNull - private static Collection getMixinConfigurationFiles(JsonObject fabricModJson) { + public static Collection getMixinConfigurationFiles(JsonObject fabricModJson) { JsonArray mixins = fabricModJson.getAsJsonArray("mixins"); if (mixins == null) { diff --git a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java index b8203abf..73780504 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -84,7 +84,7 @@ public abstract class AnnotationProcessorInvoker { String refmapName = Objects.requireNonNull(MixinExtension.getMixinInformationContainer(sourceSet)).refmapNameProvider().get(); Map args = new HashMap<>() {{ put(Constants.MixinArguments.IN_MAP_FILE_NAMED_INTERMEDIARY, loom.getMappingsProvider().tinyMappings.toFile().getCanonicalPath()); - put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getNextMixinMappings().getCanonicalPath()); + put(Constants.MixinArguments.OUT_MAP_FILE_NAMED_INTERMEDIARY, loom.getMixinMappings(sourceSet).getCanonicalPath()); put(Constants.MixinArguments.OUT_REFMAP_FILE, getRefmapDestination(task, refmapName)); put(Constants.MixinArguments.DEFAULT_OBFUSCATION_ENV, "named:intermediary"); }}; diff --git a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java new file mode 100644 index 00000000..610b720a --- /dev/null +++ b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java @@ -0,0 +1,203 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.build.nesting; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Locale; +import java.util.Set; + +import com.google.common.collect.Sets; +import com.google.gson.JsonObject; +import org.apache.commons.io.FileUtils; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +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.file.ConfigurableFileCollection; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.task.RemapTaskConfiguration; +import net.fabricmc.loom.util.ModUtils; +import net.fabricmc.loom.util.ZipUtils; + +public final class IncludedJarFactory { + private final Project project; + + public IncludedJarFactory(Project project) { + this.project = project; + } + + public Provider getNestedJars(final Configuration configuration) { + return project.provider(() -> { + final ConfigurableFileCollection files = project.files(); + final Set visited = Sets.newHashSet(); + + files.from(getProjectDeps(configuration, visited)); + files.from(getFileDeps(configuration, visited)); + files.builtBy(configuration.getBuildDependencies()); + return files; + }); + } + + private ConfigurableFileCollection getFileDeps(Configuration configuration, Set visited) { + final ConfigurableFileCollection files = project.files(); + + final ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); + final Set dependencies = resolvedConfiguration.getFirstLevelModuleDependencies(); + + for (ResolvedDependency dependency : dependencies) { + if (!visited.add(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) { + continue; + } + + for (ResolvedArtifact artifact : dependency.getModuleArtifacts()) { + Metadata metadata = new Metadata( + dependency.getModuleGroup(), + dependency.getModuleName(), + dependency.getModuleVersion(), + artifact.getClassifier() + ); + + files.from(project.provider(() -> getNestableJar(artifact.getFile(), metadata))); + } + } + + return files; + } + + private ConfigurableFileCollection getProjectDeps(Configuration configuration, Set visited) { + final ConfigurableFileCollection files = project.files(); + + for (Dependency dependency : configuration.getDependencies()) { + if (dependency instanceof ProjectDependency projectDependency) { + if (!visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion())) { + continue; + } + + // Get the outputs of the project + final Project dependentProject = projectDependency.getDependencyProject(); + + Collection remapJarTasks = dependentProject.getTasksByName(RemapTaskConfiguration.REMAP_JAR_TASK_NAME, false); + Collection jarTasks = dependentProject.getTasksByName(JavaPlugin.JAR_TASK_NAME, false); + + if (remapJarTasks.isEmpty() && jarTasks.isEmpty()) { + throw new UnsupportedOperationException("%s does not have a remapJar or jar task, cannot nest it".formatted(dependentProject.getName())); + } + + for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) { + if (task instanceof AbstractArchiveTask archiveTask) { + final Metadata metadata = new Metadata( + projectDependency.getGroup(), + projectDependency.getName(), + projectDependency.getVersion(), + archiveTask.getArchiveClassifier().getOrNull() + ); + + Provider provider = archiveTask.getArchiveFile().map(regularFile -> getNestableJar(regularFile.getAsFile(), metadata)); + files.from(provider); + files.builtBy(task); + } else { + throw new UnsupportedOperationException("Cannot nest none AbstractArchiveTask task: " + task.getName()); + } + } + } + } + + return files; + } + + private File getNestableJar(final File input, final Metadata metadata) { + if (ModUtils.isMod(input)) { + // Input is a mod, nothing needs to be done. + return input; + } + + LoomGradleExtension extension = LoomGradleExtension.get(project); + File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing"); + + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + + File tempFile = new File(tempDir, input.getName()); + + if (tempFile.exists()) { + tempFile.delete(); + } + + try { + FileUtils.copyFile(input, tempFile); + ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metadata).getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(input), e); + } + + return tempFile; + } + + // Generates a barebones mod for a dependency + private static String generateModForDependency(Metadata metadata) { + final String modId = (metadata.group() + "_" + metadata.name() + metadata.classifier()) + .replaceAll("\\.", "_") + .toLowerCase(Locale.ENGLISH); + + final JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("schemaVersion", 1); + + jsonObject.addProperty("id", modId); + jsonObject.addProperty("version", metadata.version()); + jsonObject.addProperty("name", metadata.name()); + + JsonObject custom = new JsonObject(); + custom.addProperty("fabric-loom:generated", true); + jsonObject.add("custom", custom); + + return LoomGradlePlugin.GSON.toJson(jsonObject); + } + + private record Metadata(String group, String name, String version, @Nullable String classifier) { + @Override + public String classifier() { + if (classifier == null) { + return ""; + } else { + return "_" + classifier; + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java index efae834d..06bf4b57 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java @@ -36,7 +36,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import org.gradle.api.UncheckedIOException; -import org.gradle.api.logging.Logger; +import org.slf4j.Logger; import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.Pair; @@ -69,6 +69,7 @@ public class JarNester { for (File file : jars) { String nestedJarPath = "META-INF/jars/" + file.getName(); + Preconditions.checkArgument(ModUtils.isMod(file), "Cannot nest none mod jar: " + file.getName()); for (JsonElement nestedJar : nestedJars) { JsonObject jsonObject = nestedJar.getAsJsonObject(); diff --git a/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java deleted file mode 100644 index 48b4db01..00000000 --- a/src/main/java/net/fabricmc/loom/build/nesting/MergedNestedJarProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.build.nesting; - -import java.io.File; -import java.util.Arrays; -import java.util.Collection; -import java.util.stream.Collectors; - -import org.gradle.api.Project; - -public record MergedNestedJarProvider(NestedJarProvider... children) implements NestedJarProvider { - @Override - public Collection provide() { - 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 deleted file mode 100644 index a911d30e..00000000 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedDependencyProvider.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2019-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.build.nesting; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; - -import javax.annotation.Nullable; - -import com.google.gson.JsonObject; -import org.apache.commons.io.FileUtils; -import org.gradle.api.Project; -import org.gradle.api.Task; -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; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.task.RemapJarTask; -import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.ModUtils; -import net.fabricmc.loom.util.ZipUtils; - -public final class NestedDependencyProvider implements NestedJarProvider { - final Project project; - final List> files; - - private NestedDependencyProvider(Project project, List> files) { - this.project = project; - this.files = files; - } - - public static NestedDependencyProvider createNestedDependencyProviderFromConfiguration(Project project, Configuration configuration) { - List> fileList = new ArrayList<>(); - Set visited = new HashSet<>(); - - fileList.addAll(populateProjectDependencies(configuration, visited)); - fileList.addAll(populateResolvedDependencies(configuration, visited)); - - return new NestedDependencyProvider(project, fileList); - } - - // Looks for any deps that require a sub project to be built first - public static List getRequiredTasks(Project project) { - List remapTasks = new ArrayList<>(); - - Configuration configuration = project.getConfigurations().getByName(Constants.Configurations.INCLUDE); - DependencySet dependencies = configuration.getDependencies(); - - for (Dependency dependency : dependencies) { - if (dependency instanceof ProjectDependency projectDependency) { - Project dependencyProject = projectDependency.getDependencyProject(); - - for (Task task : dependencyProject.getTasksByName("remapJar", false)) { - if (task instanceof RemapJarTask remapJarTask) { - remapTasks.add(remapJarTask); - } - } - } - } - - return remapTasks; - } - - private static List> populateProjectDependencies(Configuration configuration, Set visited) { - List> fileList = new ArrayList<>(); - - for (Dependency dependency : configuration.getDependencies()) { - if (dependency instanceof ProjectDependency projectDependency) { - Project dependencyProject = projectDependency.getDependencyProject(); - - visited.add(dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion()); - - Collection remapJarTasks = dependencyProject.getTasksByName("remapJar", false); - Collection jarTasks = dependencyProject.getTasksByName("jar", false); - - for (Task task : remapJarTasks.isEmpty() ? jarTasks : remapJarTasks) { - if (task instanceof AbstractArchiveTask abstractArchiveTask) { - fileList.add(new DependencyInfo<>( - projectDependency, - new ProjectDependencyMetaExtractor(), - abstractArchiveTask.getArchiveFile().get().getAsFile(), - abstractArchiveTask.getArchiveClassifier().getOrNull() - )); - } - } - } - } - - return fileList; - } - - private static List> populateResolvedDependencies(Configuration configuration, Set visited) { - ResolvedConfiguration resolvedConfiguration = configuration.getResolvedConfiguration(); - Set dependencies = resolvedConfiguration.getFirstLevelModuleDependencies(); - - List> fileList = new ArrayList<>(); - - for (ResolvedDependency dependency : dependencies) { - if (visited.contains(dependency.getModuleGroup() + ":" + dependency.getModuleName() + ":" + dependency.getModuleVersion())) { - continue; - } - - for (ResolvedArtifact artifact : dependency.getModuleArtifacts()) { - fileList.add(new DependencyInfo<>( - dependency, - new ResolvedDependencyMetaExtractor(), - artifact.getFile(), - artifact.getClassifier() - )); - } - } - - return fileList; - } - - @Override - public List provide() { - List fileList = new ArrayList<>(); - - for (DependencyInfo metaFile : files) { - metaFile.validateInputs(); - - File file = metaFile.file; - - //A lib that doesnt have a mod.json, we turn it into a fake mod - if (!ModUtils.isMod(file)) { - LoomGradleExtension extension = LoomGradleExtension.get(project); - File tempDir = new File(extension.getFiles().getUserCache(), "temp/modprocessing"); - - if (!tempDir.exists()) { - tempDir.mkdirs(); - } - - File tempFile = new File(tempDir, file.getName()); - - if (tempFile.exists()) { - tempFile.delete(); - } - - try { - FileUtils.copyFile(file, tempFile); - } catch (IOException e) { - throw new RuntimeException("Failed to copy file", e); - } - - try { - ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metaFile).getBytes()); - } catch (IOException e) { - throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(file), e); - } - - fileList.add(tempFile); - } else { - // Default copy the jar right in - fileList.add(file); - } - } - - return fileList; - } - - // Generates a barebones mod for a dependency - private static String generateModForDependency(DependencyInfo info) { - DependencyMetaExtractor metaExtractor = info.metaExtractor; - D dependency = info.dependency; - - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("schemaVersion", 1); - - 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)); - - JsonObject custom = new JsonObject(); - custom.addProperty("fabric-loom:generated", true); - jsonObject.add("custom", custom); - - return LoomGradlePlugin.GSON.toJson(jsonObject); - } - - 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()); - } - - if (file.isDirectory() || !file.getName().endsWith(".jar")) { - 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 { - String group(D dependency); - - String version(D dependency); - - String name(D dependency); - } - - private static final class ProjectDependencyMetaExtractor implements DependencyMetaExtractor { - @Override - public String group(ProjectDependency dependency) { - return dependency.getGroup(); - } - - @Override - public String version(ProjectDependency dependency) { - return dependency.getVersion(); - } - - @Override - public String name(ProjectDependency dependency) { - return dependency.getName(); - } - } - - private static final class ResolvedDependencyMetaExtractor implements DependencyMetaExtractor { - @Override - public String group(ResolvedDependency dependency) { - return dependency.getModuleGroup(); - } - - @Override - public String version(ResolvedDependency dependency) { - return dependency.getModuleVersion(); - } - - @Override - public String name(ResolvedDependency dependency) { - return dependency.getModuleName(); - } - } -} diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java deleted file mode 100644 index deccda49..00000000 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarPathProvider.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.build.nesting; - -import java.io.File; -import java.util.Collection; -import java.util.Set; - -import com.google.common.base.Preconditions; -import org.gradle.api.Project; - -import net.fabricmc.loom.util.ModUtils; - -public final class NestedJarPathProvider implements NestedJarProvider { - private final Set nestedPaths; - private Set files = null; - public NestedJarPathProvider(Set nestedPaths) { - this.nestedPaths = nestedPaths; - } - - private Set resolve(Project project) { - return project.files(nestedPaths).getFiles(); - } - - @Override - public void prepare(Project project) { - if (files == null) { - files = resolve(project); - } - } - - @Override - public Collection provide() { - validateFiles(); - return files; - } - - 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())); - Preconditions.checkArgument(ModUtils.isMod(file), String.format("Cannot use nest none mod jar %s", file.getAbsolutePath())); - } - } -} diff --git a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java b/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java deleted file mode 100644 index 79c605ce..00000000 --- a/src/main/java/net/fabricmc/loom/build/nesting/NestedJarProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.build.nesting; - -import java.io.File; -import java.util.Collection; - -import org.gradle.api.Project; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Internal -public interface NestedJarProvider { - // provide all the files to be included, they should already be resolved but can be transformed here - Collection provide(); - - // Setup the files ready to be provided - default void prepare(Project project) { } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 74abad3c..719e90d4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -35,7 +35,6 @@ import org.gradle.api.tasks.AbstractCopyTask; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; -import org.gradle.jvm.tasks.Jar; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.build.mixin.JavaApInvoker; @@ -124,6 +123,7 @@ public final class CompileConfiguration { public static void configureCompile(Project p) { final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class); + LoomGradleExtension extension = LoomGradleExtension.get(p); SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); @@ -131,8 +131,6 @@ public final class CompileConfiguration { javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); p.afterEvaluate(project -> { - LoomGradleExtension extension = LoomGradleExtension.get(project); - LoomDependencyManager dependencyManager = new LoomDependencyManager(); extension.setDependencyManager(dependencyManager); @@ -148,14 +146,6 @@ public final class CompileConfiguration { extension.getRemapArchives().finalizeValue(); - // Enables the default mod remapper - if (extension.getRemapArchives().get()) { - RemapConfiguration.setupDefaultRemap(project); - } else { - Jar jarTask = (Jar) project.getTasks().getByName("jar"); - extension.getUnmappedModCollection().from(jarTask); - } - MixinExtension mixin = LoomGradleExtension.get(project).getMixin(); if (mixin.getUseLegacyMixinAp().get()) { diff --git a/src/main/java/net/fabricmc/loom/configuration/JarManifestConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/JarManifestConfiguration.java deleted file mode 100644 index eeeb4d20..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/JarManifestConfiguration.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration; - -import java.util.Optional; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -import org.gradle.api.Project; -import org.gradle.api.artifacts.Dependency; -import org.gradle.util.GradleVersion; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; -import net.fabricmc.loom.util.Constants; -import net.fabricmc.tinyremapper.TinyRemapper; - -public final record JarManifestConfiguration(Project project) { - public void configure(Manifest manifest) { - // Dont set when running the reproducible build tests as it will break them when anything updates - if (Boolean.getBoolean("loom.test.reproducible")) { - return; - } - - LoomGradleExtension extension = LoomGradleExtension.get(project); - - Attributes attributes = manifest.getMainAttributes(); - Optional tinyRemapperVersion = Optional.ofNullable(TinyRemapper.class.getPackage().getImplementationVersion()); - - attributes.putValue("Fabric-Gradle-Version", GradleVersion.current().getVersion()); - attributes.putValue("Fabric-Loom-Version", LoomGradlePlugin.LOOM_VERSION); - attributes.putValue("Fabric-Mixin-Compile-Extensions-Version", Constants.Dependencies.Versions.MIXIN_COMPILE_EXTENSIONS); - attributes.putValue("Fabric-Minecraft-Version", extension.getMinecraftProvider().minecraftVersion()); - tinyRemapperVersion.ifPresent(s -> attributes.putValue("Fabric-Tiny-Remapper-Version", s)); - getLoaderVersion().ifPresent(s -> attributes.putValue("Fabric-Loader-Version", s)); - - // This can be overridden by mods if required - if (!attributes.containsKey("Fabric-Mixin-Version")) { - addMixinVersion(attributes); - } - } - - private void addMixinVersion(Attributes attributes) { - // Not super ideal that this uses the mod compile classpath, should prob look into making this not a thing at somepoint - Optional dependency = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES) - .getDependencies() - .stream() - .filter(dep -> "sponge-mixin".equals(dep.getName())) - .findFirst(); - - if (dependency.isEmpty()) { - project.getLogger().warn("Could not determine Mixin version for jar manifest"); - return; - } - - attributes.putValue("Fabric-Mixin-Version", dependency.get().getVersion()); - attributes.putValue("Fabric-Mixin-Group", dependency.get().getGroup()); - } - - private Optional getLoaderVersion() { - LoomGradleExtension extension = LoomGradleExtension.get(project); - - if (extension.getInstallerData() == null) { - project.getLogger().warn("Could not determine fabric loader version for jar manifest"); - return Optional.empty(); - } - - return Optional.of(extension.getInstallerData().version()); - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java deleted file mode 100644 index efbf5a1d..00000000 --- a/src/main/java/net/fabricmc/loom/configuration/RemapConfiguration.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.configuration; - -import java.io.IOException; - -import com.google.common.base.Preconditions; -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.UnknownTaskException; -import org.gradle.api.artifacts.ConfigurablePublishArtifact; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.PublishArtifact; -import org.gradle.api.artifacts.dsl.ArtifactHandler; -import org.gradle.api.plugins.JavaPlugin; -import org.gradle.api.tasks.bundling.AbstractArchiveTask; -import org.jetbrains.annotations.ApiStatus; - -import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.build.JarRemapper; -import net.fabricmc.loom.build.nesting.NestedDependencyProvider; -import net.fabricmc.loom.task.AbstractLoomTask; -import net.fabricmc.loom.task.RemapAllSourcesTask; -import net.fabricmc.loom.task.RemapJarTask; -import net.fabricmc.loom.task.RemapSourcesJarTask; -import net.fabricmc.loom.util.SourceRemapper; - -public class RemapConfiguration { - private static final String DEFAULT_JAR_TASK_NAME = JavaPlugin.JAR_TASK_NAME; - private static final String DEFAULT_SOURCES_JAR_TASK_NAME = "sourcesJar"; - private static final String DEFAULT_REMAP_JAR_TASK_NAME = "remapJar"; - private static final String DEFAULT_REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar"; - private static final String DEFAULT_REMAP_ALL_JARS_TASK_NAME = "remapAllJars"; - private static final String DEFAULT_REMAP_ALL_SOURCES_TASK_NAME = "remapAllSources"; - - public static void setupDefaultRemap(Project project) { - setupRemap(project, true, DEFAULT_JAR_TASK_NAME, DEFAULT_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_JAR_TASK_NAME, DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, DEFAULT_REMAP_ALL_JARS_TASK_NAME, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME); - - LoomGradleExtension extension = LoomGradleExtension.get(project); - extension.getSetupRemappedVariants().finalizeValue(); - - if (extension.getSetupRemappedVariants().get()) { - ArtifactHandler artifacts = project.getArtifacts(); - project.getTasks().named(DEFAULT_REMAP_JAR_TASK_NAME, task -> { - artifacts.add(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, task, artifactConfigurationAction(task, DEFAULT_REMAP_JAR_TASK_NAME, project)); - artifacts.add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, task, artifactConfigurationAction(task, DEFAULT_REMAP_JAR_TASK_NAME, project)); - }); - project.getTasks().named(DEFAULT_REMAP_SOURCES_JAR_TASK_NAME, RemapSourcesJarTask.class, task -> { - if (!project.getConfigurations().getNames().contains(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME)) { - // Sources jar may not have been created with withSourcesJar - project.getLogger().info("Not publishing sources jar as it was not found. Use java.withSourcesJar() to fix."); - return; - } - - PublishArtifact artifact = artifacts.add(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME, task.getOutput(), artifactConfigurationAction(task, DEFAULT_REMAP_ALL_SOURCES_TASK_NAME, project)); - - // Remove the existing artifact that does not run remapSourcesJar. - // It doesn't seem to hurt, but I'm not sure if the file-level duplicates cause issues. - Configuration configuration = project.getConfigurations().getByName(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME); - configuration.getArtifacts().removeIf(a -> a != artifact && artifact.getFile().equals(a.getFile())); - }); - - // Remove -dev jars from the default jar task - for (String configurationName : new String[] { JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME }) { - Configuration configuration = project.getConfigurations().getByName(configurationName); - configuration.getArtifacts().removeIf(artifact -> { - Task jarTask = project.getTasks().getByName(DEFAULT_JAR_TASK_NAME); - // if the artifact is a -dev jar and "builtBy jar" - return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask); - }); - } - } - } - - @ApiStatus.Experimental // This is only an api if you squint really hard, expect it to explode every 5 mins. If you must call in afterEvaluate on all projects - public static void setupRemap(Project project, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) { - setupRemap(project, false, jarTaskName, sourcesJarTaskName, remapJarTaskName, remapSourcesJarTaskName, remapAllJarsTaskName, remapAllSourcesTaskName); - } - - // isDefaultRemap is set to true for the standard remap task, some defaults are left out when this is false. - private static void setupRemap(Project project, boolean isDefaultRemap, String jarTaskName, String sourcesJarTaskName, String remapJarTaskName, String remapSourcesJarTaskName, String remapAllJarsTaskName, String remapAllSourcesTaskName) { - LoomGradleExtension extension = LoomGradleExtension.get(project); - AbstractArchiveTask jarTask = (AbstractArchiveTask) project.getTasks().getByName(jarTaskName); - RemapJarTask remapJarTask = (RemapJarTask) project.getTasks().findByName(remapJarTaskName); - - assert remapJarTask != null; - - if (!remapJarTask.getInput().isPresent() && isDefaultRemap) { - jarTask.getArchiveClassifier().convention("dev"); - remapJarTask.getArchiveClassifier().convention(""); - remapJarTask.getInput().convention(jarTask.getArchiveFile()); - } - - if (isDefaultRemap) { - extension.getUnmappedModCollection().from(jarTask); - remapJarTask.getAddNestedDependencies().convention(true); - remapJarTask.getRemapAccessWidener().convention(true); - - project.getArtifacts().add("archives", remapJarTask); - } - - remapJarTask.dependsOn(jarTask); - project.getTasks().getByName("build").dependsOn(remapJarTask); - - // TODO this might be wrong? - project.getTasks().withType(RemapJarTask.class).forEach(task -> { - if (task.getAddNestedDependencies().getOrElse(false)) { - NestedDependencyProvider.getRequiredTasks(project).forEach(task::dependsOn); - } - }); - - SourceRemapper remapper = null; - // TODO what is this for? - Task parentTask = project.getTasks().getByName("build"); - - if (extension.getShareRemapCaches().get()) { - Project rootProject = project.getRootProject(); - - if (extension.isRootProject()) { - SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false); - JarRemapper jarRemapper = new JarRemapper(); - - remapJarTask.jarRemapper = jarRemapper; - - rootProject.getTasks().register(remapAllSourcesTaskName, RemapAllSourcesTask.class, task -> { - task.sourceRemapper = sourceRemapper; - task.doLast(t -> sourceRemapper.remapAll()); - }); - - parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName); - - rootProject.getTasks().register(remapAllJarsTaskName, AbstractLoomTask.class, task -> { - task.doLast(t -> { - try { - jarRemapper.remap(); - } catch (IOException e) { - throw new RuntimeException("Failed to remap jars", e); - } - }); - }); - } else { - parentTask = rootProject.getTasks().getByName(remapAllSourcesTaskName); - remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper; - Preconditions.checkNotNull(remapper); - - remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName(remapJarTaskName)).jarRemapper; - - project.getTasks().getByName("build").dependsOn(parentTask); - project.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName(remapAllJarsTaskName)); - rootProject.getTasks().getByName(remapAllJarsTaskName).dependsOn(project.getTasks().getByName(remapJarTaskName)); - } - } - - try { - AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project.getTasks().getByName(sourcesJarTaskName); - - RemapSourcesJarTask remapSourcesJarTask = (RemapSourcesJarTask) project.getTasks().findByName(remapSourcesJarTaskName); - Preconditions.checkNotNull(remapSourcesJarTask, "Could not find " + remapSourcesJarTaskName + " in " + project.getName()); - remapSourcesJarTask.getInput().convention(sourcesTask.getArchiveFile()); - remapSourcesJarTask.getOutput().convention(sourcesTask.getArchiveFile()); - remapSourcesJarTask.dependsOn(project.getTasks().getByName(sourcesJarTaskName)); - - if (isDefaultRemap) { - // 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.getShareRemapCaches().get()) { - remapSourcesJarTask.setSourceRemapper(remapper); - } - - parentTask.dependsOn(remapSourcesJarTask); - } catch (UnknownTaskException ignored) { - // pass - } - } - - private static Action artifactConfigurationAction(Task standardTask, String sharedTaskName, Project project) { - LoomGradleExtension extension = LoomGradleExtension.get(project); - - return artifact -> { - Task remapTask = standardTask; - - if (extension.getShareRemapCaches().get()) { - remapTask = project.getRootProject().getTasks().getByName(sharedTaskName); - } - - artifact.builtBy(remapTask); - }; - } -} diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java index 893a1611..72c39b41 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java @@ -37,7 +37,7 @@ import com.google.gson.JsonObject; import net.fabricmc.loom.util.ZipUtils; public record AccessWidenerFile( - String name, + String path, String modId, byte[] content ) { @@ -83,7 +83,7 @@ public record AccessWidenerFile( @Override public int hashCode() { - int result = Objects.hash(name, modId); + int result = Objects.hash(path, modId); result = 31 * result + Arrays.hashCode(content); return result; } diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java index 3baeb087..8b4a2fcc 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerJarProcessor.java @@ -34,14 +34,10 @@ import java.util.Arrays; import com.google.common.hash.Hashing; import org.gradle.api.Project; -import org.objectweb.asm.commons.Remapper; import net.fabricmc.accesswidener.AccessWidener; import net.fabricmc.accesswidener.AccessWidenerReader; -import net.fabricmc.accesswidener.AccessWidenerRemapper; -import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.processors.JarProcessor; import net.fabricmc.loom.util.ZipUtils; @@ -96,25 +92,6 @@ public class AccessWidenerJarProcessor implements JarProcessor { } } - /** - * Get this mods access widener remapped to the intermediary namespace. - */ - public byte[] getRemappedAccessWidener(Remapper asmRemapper, String targetNamespace) throws IOException { - int version = AccessWidenerReader.readVersion(modAccessWidener); - - AccessWidenerWriter writer = new AccessWidenerWriter(version); - AccessWidenerRemapper remapper = new AccessWidenerRemapper( - writer, - asmRemapper, - MappingsNamespace.NAMED.toString(), - targetNamespace - ); - AccessWidenerReader reader = new AccessWidenerReader(remapper); - reader.read(modAccessWidener); - - return writer.write(); - } - @Override public boolean isInvalid(File file) { byte[] hash; 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 index 8ed07b90..2ab18e98 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -491,6 +491,10 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } } + public String getBuildServiceName(String name, String from, String to) { + return "%s:%s:%s>%S".formatted(name, mappingsIdentifier(), from, to); + } + public record UnpickMetadata(String unpickGroup, String unpickVersion) { } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java index 5c98ac44..37077dc9 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionImpl.java @@ -26,13 +26,10 @@ package net.fabricmc.loom.extension; import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.function.Supplier; import org.cadixdev.lorenz.MappingSet; @@ -42,6 +39,8 @@ import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.InstallerData; @@ -55,7 +54,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen private final LoomFiles loomFiles; private final ConfigurableFileCollection unmappedMods; - private final Set mixinMappings = Collections.synchronizedSet(new HashSet<>()); + private final ConfigurableFileCollection mixinMappings; private final MappingSet[] srcMappingCache = new MappingSet[2]; private final Mercury[] srcMercuryCache = new Mercury[2]; private final Map> lazyConfigurations = new HashMap<>(); @@ -70,6 +69,7 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen this.project = project; // Initiate with newInstance to allow gradle to decorate our extension this.mixinApExtension = project.getObjects().newInstance(MixinExtensionImpl.class, project); + this.mixinMappings = project.getObjects().fileCollection(); this.loomFiles = files; this.unmappedMods = project.files(); } @@ -85,15 +85,15 @@ public class LoomGradleExtensionImpl extends LoomGradleExtensionApiImpl implemen } @Override - public synchronized File getNextMixinMappings() { - File mixinMapping = new File(getFiles().getProjectBuildCache(), "mixin-map-" + getMappingsProvider().mappingsIdentifier() + "." + mixinMappings.size() + ".tiny"); - mixinMappings.add(mixinMapping); + public synchronized File getMixinMappings(SourceSet sourceSet) { + File mixinMapping = new File(getFiles().getProjectBuildCache(), "mixin-map-" + getMappingsProvider().mappingsIdentifier() + "." + sourceSet.getName() + ".tiny"); + mixinMappings.from(getProject().files(mixinMapping)); return mixinMapping; } @Override - public Set getAllMixinMappings() { - return mixinMappings; + public FileCollection getAllMixinMappings() { + return mixinMappings.filter(File::exists); } @Override diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java new file mode 100644 index 00000000..af022a8b --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java @@ -0,0 +1,123 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task; + +import java.io.IOException; +import java.nio.file.Path; + +import javax.inject.Inject; + +import org.gradle.api.Action; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.jvm.tasks.Jar; +import org.gradle.workers.WorkAction; +import org.gradle.workers.WorkParameters; +import org.gradle.workers.WorkQueue; +import org.gradle.workers.WorkerExecutor; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.util.ZipReprocessorUtil; + +public abstract class AbstractRemapJarTask extends Jar { + @InputFile + public abstract RegularFileProperty getInputFile(); + + @InputFiles + public abstract ConfigurableFileCollection getClasspath(); + + @Input + public abstract Property getSourceNamespace(); + + @Input + public abstract Property getTargetNamespace(); + + @Inject + protected abstract WorkerExecutor getWorkerExecutor(); + + @Inject + public AbstractRemapJarTask() { + getSourceNamespace().convention(MappingsNamespace.NAMED.toString()).finalizeValueOnRead(); + getTargetNamespace().convention(MappingsNamespace.INTERMEDIARY.toString()).finalizeValueOnRead(); + } + + public final

void submitWork(Class> workAction, Action

action) { + final WorkQueue workQueue = getWorkerExecutor().noIsolation(); + + workQueue.submit(workAction, params -> { + params.getInputFile().set(getInputFile()); + params.getOutputFile().set(getArchiveFile()); + + params.getSourceNamespace().set(getSourceNamespace()); + params.getTargetNamespace().set(getTargetNamespace()); + + params.getArchivePreserveFileTimestamps().set(isPreserveFileTimestamps()); + params.getArchiveReproducibleFileOrder().set(isReproducibleFileOrder()); + + action.execute(params); + }); + } + + public interface AbstractRemapParams extends WorkParameters { + RegularFileProperty getInputFile(); + RegularFileProperty getOutputFile(); + + Property getSourceNamespace(); + Property getTargetNamespace(); + + Property getArchivePreserveFileTimestamps(); + Property getArchiveReproducibleFileOrder(); + } + + public abstract static class AbstractRemapAction implements WorkAction { + protected final Path inputFile; + protected final Path outputFile; + + @Inject + public AbstractRemapAction() { + inputFile = getParameters().getInputFile().getAsFile().get().toPath(); + outputFile = getParameters().getOutputFile().getAsFile().get().toPath(); + } + + protected void rewriteJar() throws IOException { + final boolean isReproducibleFileOrder = getParameters().getArchiveReproducibleFileOrder().get(); + final boolean isPreserveFileTimestamps = getParameters().getArchivePreserveFileTimestamps().get(); + + if (isReproducibleFileOrder || !isPreserveFileTimestamps) { + ZipReprocessorUtil.reprocessZip(outputFile.toFile(), isReproducibleFileOrder, isPreserveFileTimestamps); + } + } + } + + @Deprecated + @InputFile + public RegularFileProperty getInput() { + return getInputFile(); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 42732f88..f34746f5 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -50,17 +50,13 @@ public final class LoomTasks { t.getOutputs().upToDateWhen(o -> false); }); - tasks.register("remapJar", RemapJarTask.class, t -> { - t.setDescription("Remaps the built project jar to intermediary mappings."); - t.setGroup(Constants.TaskGroup.FABRIC); - }); + RemapTaskConfiguration.setupRemap(project); TaskProvider extractNatives = tasks.register("extractNatives", ExtractNativesTask.class); tasks.register("downloadAssets", DownloadAssetsTask.class, t -> { t.dependsOn(extractNatives); t.setDescription("Downloads required assets for Fabric."); }); - tasks.register("remapSourcesJar", RemapSourcesJarTask.class, t -> t.setDescription("Remaps the project sources jar to intermediary names.")); TaskProvider validateAccessWidener = tasks.register("validateAccessWidener", ValidateAccessWidenerTask.class, t -> { t.setDescription("Validate all the rules in the access widener against the Minecraft jar"); diff --git a/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java b/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java deleted file mode 100644 index 62bff631..00000000 --- a/src/main/java/net/fabricmc/loom/task/RemapAllSourcesTask.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2020 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.task; - -import org.gradle.api.tasks.Internal; - -import net.fabricmc.loom.util.SourceRemapper; - -public class RemapAllSourcesTask extends AbstractLoomTask { - public SourceRemapper sourceRemapper; - - @Internal - public SourceRemapper getSourceRemapper() { - return sourceRemapper; - } -} diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 62dd8817..16b86424 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2021 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,276 +27,281 @@ package net.fabricmc.loom.task; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; -import java.io.UncheckedIOException; +import java.io.Serializable; import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Objects; import java.util.jar.Manifest; +import java.util.stream.Collectors; + +import javax.inject.Inject; import com.google.common.base.Preconditions; -import org.gradle.api.Action; -import org.gradle.api.Project; +import com.google.gson.JsonObject; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; -import org.gradle.api.file.RegularFileProperty; import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; -import org.gradle.jvm.tasks.Jar; -import org.jetbrains.annotations.ApiStatus; +import org.objectweb.asm.commons.Remapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import net.fabricmc.accesswidener.AccessWidenerReader; +import net.fabricmc.accesswidener.AccessWidenerRemapper; +import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.build.JarRemapper; import net.fabricmc.loom.build.MixinRefmapHelper; +import net.fabricmc.loom.build.nesting.IncludedJarFactory; import net.fabricmc.loom.build.nesting.JarNester; -import net.fabricmc.loom.build.nesting.MergedNestedJarProvider; -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.JarManifestConfiguration; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; -import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; -import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.extension.MixinExtension; +import net.fabricmc.loom.task.service.JarManifestService; +import net.fabricmc.loom.task.service.MappingsService; import net.fabricmc.loom.util.Constants; -import net.fabricmc.loom.util.TinyRemapperHelper; -import net.fabricmc.loom.util.ZipReprocessorUtil; import net.fabricmc.loom.util.ZipUtils; -import net.fabricmc.stitch.util.Pair; +import net.fabricmc.tinyremapper.InputTag; +import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.TinyUtils; -import net.fabricmc.tinyremapper.extension.mixin.MixinExtension; -public class RemapJarTask extends Jar { +public abstract class RemapJarTask extends AbstractRemapJarTask { private static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; - private final RegularFileProperty input; - private final Property addNestedDependencies; - private final Property addDefaultNestedDependencies; - private final Property remapAccessWidener; - private final List> remapOptions = new ArrayList<>(); - public JarRemapper jarRemapper; - private FileCollection classpath; - private final Set nestedPaths = new LinkedHashSet<>(); + @InputFiles + public abstract ConfigurableFileCollection getNestedJars(); + @Input + public abstract Property getAddNestedDependencies(); + + @Inject public RemapJarTask() { super(); - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - input = getProject().getObjects().fileProperty(); - addNestedDependencies = getProject().getObjects().property(Boolean.class) - .convention(false); - addDefaultNestedDependencies = getProject().getObjects().property(Boolean.class) - .convention(true); - remapAccessWidener = getProject().getObjects().property(Boolean.class) - .convention(false); - if (!extension.getMixin().getUseLegacyMixinAp().get()) { - remapOptions.add(b -> b.extension(new MixinExtension())); - } + getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); + getAddNestedDependencies().convention(true).finalizeValueOnRead(); + + Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); + getNestedJars().from(new IncludedJarFactory(getProject()).getNestedJars(includeConfiguration)); } @TaskAction - public void doTask() throws Throwable { - boolean singleRemap = false; + public void run() { + final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - if (jarRemapper == null) { - singleRemap = true; - jarRemapper = new JarRemapper(); + submitWork(RemapAction.class, params -> { + if (getAddNestedDependencies().get()) { + params.getNestedJars().from(getNestedJars()); + } + + params.getJarManifestService().set(JarManifestService.get(getProject())); + params.getRemapClasspath().from(getClasspath()); + params.getMappings().add(MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get())); + + final boolean legacyMixin = extension.getMixin().getUseLegacyMixinAp().get(); + params.getUseMixinExtension().set(!legacyMixin); + + if (legacyMixin) { + params.getMixinMappings().from(extension.getAllMixinMappings()); + setupLegacyMixinRefmapRemapping(params); + } + }); + } + + private void setupLegacyMixinRefmapRemapping(RemapParams params) { + final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); + final MixinExtension mixinExtension = extension.getMixin(); + + final JsonObject fabricModJson = MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get()); + + if (fabricModJson == null) { + getProject().getLogger().warn("Could not find fabric.mod.json file in: " + getInputFile().getAsFile().get().getName()); + return; } - scheduleRemap(singleRemap || LoomGradleExtension.get(getProject()).isRootProject()); + final Collection allMixinConfigs = MixinRefmapHelper.getMixinConfigurationFiles(fabricModJson); - if (singleRemap) { - jarRemapper.remap(); + for (SourceSet sourceSet : mixinExtension.getMixinSourceSets()) { + MixinExtension.MixinInformationContainer container = Objects.requireNonNull( + MixinExtension.getMixinInformationContainer(sourceSet) + ); + + final String refmapName = container.refmapNameProvider().get(); + final List mixinConfigs = container.sourceSet().getResources() + .matching(container.mixinConfigPattern()) + .getFiles() + .stream() + .map(File::getName) + .filter(allMixinConfigs::contains) + .toList(); + + params.getMixinData().add(new RemapParams.RefmapData(mixinConfigs, refmapName)); } } - public void scheduleRemap(boolean isMainRemapTask) throws Throwable { - Project project = getProject(); - LoomGradleExtension extension = LoomGradleExtension.get(getProject()); - Path input = this.getInput().getAsFile().get().toPath(); - Path output = this.getArchiveFile().get().getAsFile().toPath(); + public interface RemapParams extends AbstractRemapParams { + ConfigurableFileCollection getNestedJars(); + ConfigurableFileCollection getRemapClasspath(); + ConfigurableFileCollection getMixinMappings(); + ListProperty> getMappings(); - if (!Files.exists(input)) { - throw new FileNotFoundException(input.toString()); - } + Property getUseMixinExtension(); - MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); + record RefmapData(List mixinConfigs, String refmapName) implements Serializable { } + ListProperty getMixinData(); - String fromM = MappingsNamespace.NAMED.toString(); - String toM = MappingsNamespace.INTERMEDIARY.toString(); + Property getJarManifestService(); + } - if (isMainRemapTask) { - jarRemapper.addToClasspath(getRemapClasspath()); + public abstract static class RemapAction extends AbstractRemapAction { + private static final Logger LOGGER = LoggerFactory.getLogger(RemapAction.class); - jarRemapper.addMappings(TinyRemapperHelper.create(mappingsProvider.getMappings(), fromM, toM, false)); - } + private TinyRemapper tinyRemapper; - for (File mixinMapFile : extension.getAllMixinMappings()) { - if (mixinMapFile.exists()) { - jarRemapper.addMappings(TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), fromM, toM)); + @Override + public void execute() { + try { + LOGGER.info("Remapping {} to {}", inputFile, outputFile); + + tinyRemapper = createTinyRemapper(); + + remap(); + remapAccessWidener(); + addRefmaps(); + addNestedJars(); + modifyJarManifest(); + rewriteJar(); + + tinyRemapper.finish(); + tinyRemapper = null; + + LOGGER.debug("Finished remapping {}", inputFile); + } catch (Exception e) { + try { + Files.deleteIfExists(outputFile); + } catch (IOException ex) { + LOGGER.error("Failed to delete output file", ex); + } + + throw new RuntimeException("Failed to remap", e); } } - // Add remap options to the jar remapper - jarRemapper.addOptions(this.remapOptions); + private void remap() throws IOException { + final InputTag inputTag = tinyRemapper.createInputTag(); - NestedJarProvider nestedJarProvider = getNestedJarProvider(); - nestedJarProvider.prepare(getProject()); + tinyRemapper.readInputsAsync(inputTag, inputFile); - jarRemapper.scheduleRemap(input, output) - .supplyAccessWidener((remapData, remapper) -> { - if (getRemapAccessWidener().getOrElse(false) && extension.getAccessWidenerPath().isPresent()) { - AccessWidenerJarProcessor accessWidenerJarProcessor = extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class); - byte[] data; - - try { - data = accessWidenerJarProcessor.getRemappedAccessWidener(remapper, toM); - } catch (IOException e) { - throw new RuntimeException("Failed to remap access widener", e); - } - - AccessWidenerFile awFile = AccessWidenerFile.fromModJar(remapData.input); - Preconditions.checkNotNull(awFile, "Failed to find accessWidener in fabric.mod.json: " + remapData.input); - - return Pair.of(awFile.name(), data); - } - - return null; - }) - .complete((data, accessWidener) -> { - if (!Files.exists(output)) { - throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!"); - } - - if (extension.getMixin().getUseLegacyMixinAp().get()) { - if (MixinRefmapHelper.addRefmapName(project, output)) { - project.getLogger().debug("Transformed mixin reference maps in output JAR!"); - } - } - - if (getAddNestedDependencies().getOrElse(false)) { - JarNester.nestJars(nestedJarProvider.provide(), output.toFile(), project.getLogger()); - } - - if (accessWidener != null) { - try { - ZipUtils.replace(data.output, accessWidener.getLeft(), accessWidener.getRight()); - } catch (IOException e) { - throw new UncheckedIOException("Failed to replace access widener in output jar", e); - } - } - - // Add data to the manifest - try { - int count = ZipUtils.transform(data.output, Map.of(MANIFEST_PATH, bytes -> { - var manifest = new Manifest(new ByteArrayInputStream(bytes)); - var manifestConfiguration = new JarManifestConfiguration(project); - - manifestConfiguration.configure(manifest); - manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", toM); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - manifest.write(out); - return out.toByteArray(); - })); - - Preconditions.checkState(count > 0, "Did not transform any jar manifest"); - } catch (IOException e) { - throw new UncheckedIOException("Failed to transform jar manifest", e); - } - - if (isReproducibleFileOrder() || !isPreserveFileTimestamps()) { - try { - ZipReprocessorUtil.reprocessZip(output.toFile(), isReproducibleFileOrder(), isPreserveFileTimestamps()); - } catch (IOException e) { - throw new RuntimeException("Failed to re-process jar", e); - } - } - }); - } - - private NestedJarProvider getNestedJarProvider() { - Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); - - if (!addDefaultNestedDependencies.getOrElse(true)) { - return new NestedJarPathProvider(nestedPaths); + try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(outputFile).build()) { + outputConsumer.addNonClassFiles(inputFile); + tinyRemapper.apply(outputConsumer, inputTag); + } } - NestedJarProvider baseProvider = NestedDependencyProvider.createNestedDependencyProviderFromConfiguration(getProject(), includeConfiguration); + private void remapAccessWidener() throws IOException { + final AccessWidenerFile accessWidenerFile = AccessWidenerFile.fromModJar(inputFile); - if (nestedPaths.isEmpty()) { - return baseProvider; + if (accessWidenerFile == null) { + return; + } + + byte[] remapped = remapAccessWidener(accessWidenerFile.content(), tinyRemapper.getEnvironment().getRemapper(), MappingsNamespace.INTERMEDIARY.toString()); + + // Finally, replace the output with the remaped aw + ZipUtils.replace(outputFile, accessWidenerFile.path(), remapped); } - return new MergedNestedJarProvider( - baseProvider, - new NestedJarPathProvider(nestedPaths) - ); - } + private static byte[] remapAccessWidener(byte[] input, Remapper asmRemapper, String targetNamespace) { + int version = AccessWidenerReader.readVersion(input); - private Path[] getRemapClasspath() { - FileCollection files = this.classpath; + AccessWidenerWriter writer = new AccessWidenerWriter(version); + AccessWidenerRemapper remapper = new AccessWidenerRemapper( + writer, + asmRemapper, + MappingsNamespace.NAMED.toString(), + targetNamespace + ); + AccessWidenerReader reader = new AccessWidenerReader(remapper); + reader.read(input); - if (files == null) { - files = getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME); + return writer.write(); } - return files.getFiles().stream() - .map(File::toPath) - .filter(Files::exists) - .toArray(Path[]::new); - } + private void addNestedJars() { + FileCollection nestedJars = getParameters().getNestedJars(); - @InputFile - public RegularFileProperty getInput() { - return input; - } + if (nestedJars.isEmpty()) { + LOGGER.info("No jars to nest"); + return; + } - @Input - public Property getAddNestedDependencies() { - return addNestedDependencies; - } - - @Input - public Property getAddDefaultNestedDependencies() { - return addDefaultNestedDependencies; - } - - @Input - public Property getRemapAccessWidener() { - return remapAccessWidener; - } - - public void remapOptions(Action action) { - this.remapOptions.add(action); - } - - public RemapJarTask classpath(FileCollection collection) { - if (this.classpath == null) { - this.classpath = collection; - } else { - this.classpath = this.classpath.plus(collection); + JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), LOGGER); } - return this; - } + private void modifyJarManifest() throws IOException { + int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { + var manifest = new Manifest(new ByteArrayInputStream(bytes)); - @ApiStatus.Experimental - // This only allows mod jars, proceed with care when trying to pass in configurations with projects, or something that depends on a task. - public RemapJarTask include(Object... paths) { - Collections.addAll(nestedPaths, paths); - this.addNestedDependencies.set(true); + getParameters().getJarManifestService().get().apply(manifest); + manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get()); - return this; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + return out.toByteArray(); + })); + + Preconditions.checkState(count > 0, "Did not transform any jar manifest"); + } + + private void addRefmaps() throws IOException { + if (getParameters().getUseMixinExtension().get()) { + return; + } + + for (RemapParams.RefmapData refmapData : getParameters().getMixinData().get()) { + int transformed = ZipUtils.transformJson(JsonObject.class, outputFile, refmapData.mixinConfigs().stream().collect(Collectors.toMap(s -> s, s -> json -> { + if (!json.has("refmap")) { + json.addProperty("refmap", refmapData.refmapName()); + } + + return json; + }))); + } + } + + private TinyRemapper createTinyRemapper() { + TinyRemapper.Builder builder = TinyRemapper.newRemapper(); + + for (Provider provider : getParameters().getMappings().get()) { + builder.withMappings(provider.get().getMappingsProvider()); + } + + for (File mixinMapping : getParameters().getMixinMappings()) { + builder.withMappings(TinyUtils.createTinyMappingProvider(mixinMapping.toPath(), getParameters().getSourceNamespace().get(), getParameters().getTargetNamespace().get())); + } + + if (getParameters().getUseMixinExtension().get()) { + builder.extension(new net.fabricmc.tinyremapper.extension.mixin.MixinExtension()); + } + + TinyRemapper remapper = builder.build(); + + // Apply classpath + for (File file : getParameters().getRemapClasspath()) { + remapper.readClassPathAsync(file.toPath()); + } + + return remapper; + } } } diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 26fd6a37..824a6784 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2016-2021 FabricMC + * Copyright (c) 2021 FabricMC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,70 +24,65 @@ package net.fabricmc.loom.task; -import org.gradle.api.file.RegularFileProperty; +import java.io.IOException; +import java.nio.file.Files; + +import javax.inject.Inject; + +import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; -import net.fabricmc.loom.util.SourceRemapper; - -public class RemapSourcesJarTask extends AbstractLoomTask { - private final RegularFileProperty input = getProject().getObjects().fileProperty(); - private final RegularFileProperty output = getProject().getObjects().fileProperty().convention(input); - private final Property targetNamespace = getProject().getObjects().property(String.class).convention(MappingsNamespace.INTERMEDIARY.toString()); - private SourceRemapper sourceRemapper = null; - private final Property preserveFileTimestamps = getProject().getObjects().property(Boolean.class).convention(true); - private final Property reproducibleFileOrder = getProject().getObjects().property(Boolean.class).convention(false); +import net.fabricmc.loom.task.service.MappingsService; +import net.fabricmc.loom.task.service.SourceRemapperService; +public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { + @Inject public RemapSourcesJarTask() { + super(); + + getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); } @TaskAction - public void remap() throws Exception { - if (sourceRemapper == null) { - String direction = targetNamespace.get(); - SourceRemapper.remapSources(getProject(), input.get().getAsFile(), output.get().getAsFile(), direction.equals(MappingsNamespace.NAMED.toString()), reproducibleFileOrder.get(), preserveFileTimestamps.get()); - } else { - sourceRemapper.scheduleRemapSources(input.get().getAsFile(), output.get().getAsFile(), reproducibleFileOrder.get(), preserveFileTimestamps.get()); + public void run() { + submitWork(RemapSourcesAction.class, params -> { + params.getSourcesRemapperService().set(SourceRemapperService.create(getProject(), MappingsService.createDefault(getProject(), getSourceNamespace().get(), getTargetNamespace().get()), getClasspath())); + }); + } + + public interface RemapSourcesParams extends AbstractRemapParams { + Property getSourcesRemapperService(); + } + + public abstract static class RemapSourcesAction extends AbstractRemapAction { + private static final Logger LOGGER = LoggerFactory.getLogger(RemapSourcesAction.class); + + private final SourceRemapperService sourceRemapperService; + + public RemapSourcesAction() { + super(); + + sourceRemapperService = getParameters().getSourcesRemapperService().get(); + } + + @Override + public void execute() { + try { + sourceRemapperService.remapSourcesJar(inputFile, outputFile); + + rewriteJar(); + } catch (Exception e) { + try { + Files.deleteIfExists(outputFile); + } catch (IOException ex) { + LOGGER.error("Failed to delete output file", ex); + } + + throw new RuntimeException("Failed to remap sources", e); + } } } - - @Internal - public SourceRemapper getSourceRemapper() { - return sourceRemapper; - } - - public RemapSourcesJarTask setSourceRemapper(SourceRemapper sourceRemapper) { - this.sourceRemapper = sourceRemapper; - return this; - } - - @InputFile - public RegularFileProperty getInput() { - return input; - } - - @OutputFile - public RegularFileProperty getOutput() { - return output; - } - - @Input - public Property getTargetNamespace() { - return targetNamespace; - } - - @Input - public Property getPreserveFileTimestamps() { - return preserveFileTimestamps; - } - - @Input - public Property getReproducibleFileOrder() { - return reproducibleFileOrder; - } } diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java new file mode 100644 index 00000000..35fa28d3 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -0,0 +1,138 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016-2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task; + +import java.io.File; + +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskContainer; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.api.tasks.bundling.AbstractArchiveTask; +import org.gradle.api.tasks.bundling.Jar; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.util.Constants; + +public class RemapTaskConfiguration { + public static final String REMAP_JAR_TASK_NAME = "remapJar"; + public static final String REMAP_SOURCES_JAR_TASK_NAME = "remapSourcesJar"; + + public static void setupRemap(Project project) { + final TaskContainer tasks = project.getTasks(); + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (!extension.getRemapArchives().get()) { + extension.getUnmappedModCollection().from(project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME)); + return; + } + + // Register the default remap jar task + TaskProvider remapJarTaskProvider = tasks.register(REMAP_JAR_TASK_NAME, RemapJarTask.class, task -> { + final AbstractArchiveTask jarTask = tasks.named(JavaPlugin.JAR_TASK_NAME, AbstractArchiveTask.class).get(); + + // Basic task setup + task.dependsOn(jarTask); + task.setDescription("Remaps the built project jar to intermediary mappings."); + task.setGroup(Constants.TaskGroup.FABRIC); + project.getArtifacts().add(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, task); + project.getArtifacts().add(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME, task); + + // Setup the input file and the nested deps + task.getInputFile().convention(jarTask.getArchiveFile()); + task.dependsOn(tasks.named(JavaPlugin.JAR_TASK_NAME)); + }); + + // Configure the default jar task + tasks.named(JavaPlugin.JAR_TASK_NAME, AbstractArchiveTask.class).configure(task -> { + task.getArchiveClassifier().convention("dev"); + task.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs")); + }); + + tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapJarTaskProvider)); + + trySetupSourceRemapping(project); + + if (extension.getSetupRemappedVariants().get()) { + // Remove -dev jars from the default jar task + for (String configurationName : new String[] { JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME, JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME }) { + Configuration configuration = project.getConfigurations().getByName(configurationName); + configuration.getArtifacts().removeIf(artifact -> { + Task jarTask = project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME); + // if the artifact is a -dev jar and "builtBy jar" + return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask); + }); + } + } + } + + private static void trySetupSourceRemapping(Project project) { + final TaskContainer tasks = project.getTasks(); + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + TaskProvider remapSourcesTask = tasks.register(REMAP_SOURCES_JAR_TASK_NAME, RemapSourcesJarTask.class, task -> { + task.setDescription("Remaps the default sources jar to intermediary mappings."); + task.setGroup(Constants.TaskGroup.FABRIC); + + final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + final String sourcesJarTaskName = javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getSourcesJarTaskName(); + final Task sourcesTask = project.getTasks().findByName(sourcesJarTaskName); + + if (sourcesTask == null) { + project.getLogger().info("{} task was not found, not remapping sources", sourcesJarTaskName); + task.setEnabled(false); + return; + } + + if (!(sourcesTask instanceof Jar sourcesJarTask)) { + project.getLogger().info("{} task is not a Jar task, not remapping sources", sourcesJarTaskName); + task.setEnabled(false); + return; + } + + sourcesJarTask.getArchiveClassifier().convention("dev-sources"); + sourcesJarTask.getDestinationDirectory().set(new File(project.getBuildDir(), "devlibs")); + task.getArchiveClassifier().convention("sources"); + + task.dependsOn(sourcesJarTask); + task.getInputFile().convention(sourcesJarTask.getArchiveFile()); + + if (extension.getSetupRemappedVariants().get()) { + project.getArtifacts().add(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME, task); + + // Remove the dev sources artifact + Configuration configuration = project.getConfigurations().getByName(JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME); + configuration.getArtifacts().removeIf(a -> a.getFile().equals(sourcesJarTask.getArchiveFile().get().getAsFile())); + } + }); + + tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapSourcesTask)); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java new file mode 100644 index 00000000..c80852e8 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java @@ -0,0 +1,123 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.service; + +import java.io.Serializable; +import java.util.Optional; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +import org.gradle.api.Project; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; +import org.gradle.util.GradleVersion; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.tinyremapper.TinyRemapper; + +public abstract class JarManifestService implements BuildService { + interface Params extends BuildServiceParameters { + Property getGradleVersion(); + Property getLoomVersion(); + Property getMCEVersion(); + Property getMinecraftVersion(); + Property getTinyRemapperVersion(); + Property getFabricLoaderVersion(); + Property getMixinVersion(); + } + + public static Provider get(Project project) { + return project.getGradle().getSharedServices().registerIfAbsent("LoomJarManifestService:" + project.getName(), JarManifestService.class, spec -> { + spec.parameters(params -> { + LoomGradleExtension extension = LoomGradleExtension.get(project); + Optional tinyRemapperVersion = Optional.ofNullable(TinyRemapper.class.getPackage().getImplementationVersion()); + + params.getGradleVersion().set(GradleVersion.current().getVersion()); + params.getLoomVersion().set(LoomGradlePlugin.LOOM_VERSION); + params.getMCEVersion().set(Constants.Dependencies.Versions.MIXIN_COMPILE_EXTENSIONS); + params.getMinecraftVersion().set(extension.getMinecraftProvider().minecraftVersion()); + params.getTinyRemapperVersion().set(tinyRemapperVersion.orElse("unknown")); + params.getFabricLoaderVersion().set(getLoaderVersion(project).orElse("unknown")); + params.getMixinVersion().set(getMixinVersion(project).orElse(new MixinVersion("unknown", "unknown"))); + }); + }); + } + + public void apply(Manifest manifest) { + // Don't set when running the reproducible build tests as it will break them when anything updates + if (Boolean.getBoolean("loom.test.reproducible")) { + return; + } + + Attributes attributes = manifest.getMainAttributes(); + Params p = getParameters(); + + attributes.putValue("Fabric-Gradle-Version", p.getGradleVersion().get()); + attributes.putValue("Fabric-Loom-Version", p.getLoomVersion().get()); + attributes.putValue("Fabric-Mixin-Compile-Extensions-Version", p.getMCEVersion().get()); + attributes.putValue("Fabric-Minecraft-Version", p.getMinecraftVersion().get()); + attributes.putValue("Fabric-Tiny-Remapper-Version", p.getTinyRemapperVersion().get()); + attributes.putValue("Fabric-Loader-Version", p.getFabricLoaderVersion().get()); + + // This can be overridden by mods if required + if (!attributes.containsKey("Fabric-Mixin-Version")) { + attributes.putValue("Fabric-Mixin-Version", p.getMixinVersion().get().version()); + attributes.putValue("Fabric-Mixin-Group", p.getMixinVersion().get().group()); + } + } + + private static Optional getLoaderVersion(Project project) { + LoomGradleExtension extension = LoomGradleExtension.get(project); + + if (extension.getInstallerData() == null) { + project.getLogger().warn("Could not determine fabric loader version for jar manifest"); + return Optional.empty(); + } + + return Optional.of(extension.getInstallerData().version()); + } + + private record MixinVersion(String group, String version) implements Serializable { } + + private static Optional getMixinVersion(Project project) { + // Not super ideal that this uses the mod compile classpath, should prob look into making this not a thing at somepoint + Optional dependency = project.getConfigurations().getByName(Constants.Configurations.LOADER_DEPENDENCIES) + .getDependencies() + .stream() + .filter(dep -> "sponge-mixin".equals(dep.getName())) + .findFirst(); + + if (dependency.isEmpty()) { + project.getLogger().warn("Could not determine Mixin version for jar manifest"); + } + + return dependency.map(d -> new MixinVersion(d.getGroup(), d.getVersion())); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/service/MappingsService.java b/src/main/java/net/fabricmc/loom/task/service/MappingsService.java new file mode 100644 index 00000000..99cebcb7 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/service/MappingsService.java @@ -0,0 +1,118 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.service; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.gradle.api.Project; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.mappingio.MappingReader; +import net.fabricmc.mappingio.tree.MemoryMappingTree; +import net.fabricmc.tinyremapper.IMappingProvider; + +public abstract class MappingsService implements BuildService, AutoCloseable { + interface Params extends BuildServiceParameters { + RegularFileProperty getMappingsFile(); + + Property getFromNamespace(); + Property getToNamespace(); + + Property getRemapLocals(); + } + + public static synchronized Provider create(Project project, String name, File mappingsFile, String from, String to, boolean remapLocals) { + return project.getGradle().getSharedServices().registerIfAbsent(name, MappingsService.class, spec -> { + spec.parameters(params -> { + params.getMappingsFile().set(mappingsFile); + params.getFromNamespace().set(from); + params.getToNamespace().set(to); + params.getRemapLocals().set(remapLocals); + }); + }); + } + + public static Provider createDefault(Project project, String from, String to) { + final MappingsProviderImpl mappingsProvider = LoomGradleExtension.get(project).getMappingsProvider(); + final String name = mappingsProvider.getBuildServiceName("mappingsProvider", from, to); + return MappingsService.create(project, name, mappingsProvider.tinyMappings.toFile(), from, to, false); + } + + private IMappingProvider mappingProvider = null; + private MemoryMappingTree memoryMappingTree = null; + + public synchronized IMappingProvider getMappingsProvider() { + if (mappingProvider == null) { + try { + mappingProvider = TinyRemapperHelper.create( + getParameters().getMappingsFile().get().getAsFile().toPath(), + getParameters().getFromNamespace().get(), + getParameters().getToNamespace().get(), + getParameters().getRemapLocals().get() + ); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e); + } + } + + return mappingProvider; + } + + public synchronized MemoryMappingTree getMemoryMappingTree() { + if (memoryMappingTree == null) { + memoryMappingTree = new MemoryMappingTree(); + + try { + MappingReader.read(getParameters().getMappingsFile().get().getAsFile().toPath(), memoryMappingTree); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read mappings from: " + getParameters().getMappingsFile().get().getAsFile().getAbsolutePath(), e); + } + } + + return memoryMappingTree; + } + + public String getFromNamespace() { + return getParameters().getFromNamespace().get(); + } + + public String getToNamespace() { + return getParameters().getToNamespace().get(); + } + + @Override + public void close() { + mappingProvider = null; + } +} diff --git a/src/main/java/net/fabricmc/loom/task/service/SourceRemapperService.java b/src/main/java/net/fabricmc/loom/task/service/SourceRemapperService.java new file mode 100644 index 00000000..891a909c --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/service/SourceRemapperService.java @@ -0,0 +1,133 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.cadixdev.lorenz.MappingSet; +import org.cadixdev.mercury.Mercury; +import org.cadixdev.mercury.remapper.MercuryRemapper; +import org.gradle.api.Project; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileCollection; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.services.BuildService; +import org.gradle.api.services.BuildServiceParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.util.DeletingFileVisitor; +import net.fabricmc.loom.util.FileSystemUtil; +import net.fabricmc.loom.util.SourceRemapper; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.lorenztiny.TinyMappingsReader; + +public abstract class SourceRemapperService implements BuildService, AutoCloseable { + public interface Params extends BuildServiceParameters { + Property> getMappings(); + + ConfigurableFileCollection getClasspath(); + } + + public static synchronized Provider create(Project project, Provider mappings, FileCollection classpath) { + // TODO may need a better name, im not too sure + return project.getGradle().getSharedServices().registerIfAbsent("sourceremapper", SourceRemapperService.class, spec -> + spec.parameters(params -> { + params.getMappings().set(mappings); + params.getClasspath().from(classpath); + } + )); + } + + private static final Logger LOGGER = LoggerFactory.getLogger(SourceRemapperService.class); + + private Mercury mercury; + + public void remapSourcesJar(Path source, Path destination) throws IOException { + if (source.equals(destination)) { + throw new UnsupportedOperationException("Cannot remap in place"); + } + + Path srcPath = source; + boolean isSrcTmp = false; + + // Create a temp directory with all of the sources + if (!Files.isDirectory(source)) { + isSrcTmp = true; + srcPath = Files.createTempDirectory("fabric-loom-src"); + ZipUtils.unpackAll(source, srcPath); + } + + if (!Files.isDirectory(destination) && Files.exists(destination)) { + Files.delete(destination); + } + + try (FileSystemUtil.Delegate dstFs = Files.isDirectory(destination) ? null : FileSystemUtil.getJarFileSystem(destination, true)) { + Path dstPath = dstFs != null ? dstFs.get().getPath("/") : destination; + + doRemap(srcPath, dstPath, source); + SourceRemapper.copyNonJavaFiles(srcPath, dstPath, LOGGER, source); + } finally { + if (isSrcTmp) { + Files.walkFileTree(srcPath, new DeletingFileVisitor()); + } + } + } + + private synchronized void doRemap(Path srcPath, Path dstPath, Path source) throws IOException { + if (mercury == null) { + mercury = new Mercury(); + mercury.setGracefulClasspathChecks(true); + mercury.getProcessors().add(MercuryRemapper.create(getMappings())); + + getParameters().getClasspath().forEach(file -> mercury.getClassPath().add(file.toPath())); + } + + try { + // Not thread safe!! + mercury.rewrite(srcPath, dstPath); + } catch (Exception e) { + LOGGER.warn("Could not remap " + source + " fully!", e); + } + } + + private MappingSet getMappings() throws IOException { + return new TinyMappingsReader(mappingsService().getMemoryMappingTree(), mappingsService().getFromNamespace(), mappingsService().getToNamespace()).read(); + } + + private MappingsService mappingsService() { + return getParameters().getMappings().get().get(); + } + + @Override + public void close() throws Exception { + mercury = null; + // This is required (: + System.gc(); + } +} diff --git a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java index f37211be..08b241d3 100644 --- a/src/main/java/net/fabricmc/loom/util/SourceRemapper.java +++ b/src/main/java/net/fabricmc/loom/util/SourceRemapper.java @@ -37,6 +37,7 @@ import org.cadixdev.lorenz.MappingSet; import org.cadixdev.mercury.Mercury; import org.cadixdev.mercury.remapper.MercuryRemapper; import org.gradle.api.Project; +import org.slf4j.Logger; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; @@ -142,7 +143,7 @@ public class SourceRemapper { project.getLogger().warn("Could not remap " + source.getName() + " fully!", e); } - copyNonJavaFiles(srcPath, dstPath, project, source); + copyNonJavaFiles(srcPath, dstPath, project.getLogger(), source.toPath()); if (dstFs != null) { dstFs.close(); @@ -202,7 +203,7 @@ public class SourceRemapper { return mercury; } - private static void copyNonJavaFiles(Path from, Path to, Project project, File source) throws IOException { + public static void copyNonJavaFiles(Path from, Path to, Logger logger, Path source) throws IOException { Files.walk(from).forEach(path -> { Path targetPath = to.resolve(from.relativize(path).toString()); @@ -210,7 +211,7 @@ public class SourceRemapper { try { Files.copy(path, targetPath); } catch (IOException e) { - project.getLogger().warn("Could not copy non-java sources '" + source.getName() + "' fully!", e); + logger.warn("Could not copy non-java sources '" + source + "' fully!", e); } } }); diff --git a/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java b/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java index d534f972..e0e6b188 100644 --- a/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java +++ b/src/main/java/net/fabricmc/loom/util/TinyRemapperHelper.java @@ -36,6 +36,7 @@ import org.gradle.api.Project; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.mappingio.MappingReader; import net.fabricmc.mappingio.tree.MappingTree; import net.fabricmc.mappingio.tree.MemoryMappingTree; import net.fabricmc.tinyremapper.IMappingProvider; @@ -101,6 +102,12 @@ public final class TinyRemapperHelper { return new IMappingProvider.Member(className, memberName, descriptor); } + public static IMappingProvider create(Path mappings, String from, String to, boolean remapLocalVariables) throws IOException { + MemoryMappingTree mappingTree = new MemoryMappingTree(); + MappingReader.read(mappings, mappingTree); + return create(mappingTree, from, to, remapLocalVariables); + } + public static IMappingProvider create(MappingTree mappings, String from, String to, boolean remapLocalVariables) { return (acceptor) -> { for (MappingTree.ClassMapping classDef : mappings.getClasses()) { diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index ef596a75..74814eb4 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.4-20211216231505+0000" + public final static String PRE_RELEASE_GRADLE = "7.4-20211219231013+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy index b7300249..0bfbf172 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/FabricAPITest.groovy @@ -44,7 +44,7 @@ class FabricAPITest extends Specification implements GradleProjectTestTrait { setup: def gradle = gradleProject( repo: "https://github.com/FabricMC/fabric.git", - commit: "ce6198f63bbe0e17ba631420e9186fb72cc8b2af", + commit: "71b634e5b7845296b11be3fa6545f4fbfacc017f", version: version, patch: "fabric_api" ) diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MixinApSimpleTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MixinApSimpleTest.groovy index 831e226c..123a831e 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MixinApSimpleTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MixinApSimpleTest.groovy @@ -47,7 +47,7 @@ class MixinApSimpleTest extends Specification implements GradleProjectTestTrait result.task(":build").outcome == SUCCESS // verify the ref-map name is correctly generated - def main = new JarFile(gradle.getOutputFile("fabric-example-mod-1.0.0-dev.jar").absoluteFile) + def main = new JarFile(new File(gradle.projectDir, "build/devlibs/fabric-example-mod-1.0.0-dev.jar").absoluteFile) main.getEntry("main-refmap0000.json") != null def mixin = new JarFile(gradle.getOutputFile("fabric-example-mod-1.0.0-mixin.jar").absoluteFile) mixin.getEntry("default-refmap0000.json") != null diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MultiProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MultiProjectTest.groovy index 5aa5b70e..3984e004 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MultiProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MultiProjectTest.groovy @@ -45,9 +45,6 @@ class MultiProjectTest extends Specification implements GradleProjectTestTrait { result.task(":core:build").outcome == SUCCESS result.task(":example:build").outcome == SUCCESS - result.task(":remapAllJars").outcome == SUCCESS - result.task(":remapAllSources").outcome == SUCCESS - gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/example-1.0.0.jar") gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/core-1.0.0.jar") gradle.hasOutputZipEntry("multiproject-1.0.0.jar", "META-INF/jars/fabric-api-base-0.2.1+9354966b7d.jar") diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy index 5adf4ae1..2ceb2a01 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SimpleProjectTest.groovy @@ -51,6 +51,7 @@ class SimpleProjectTest extends Specification implements GradleProjectTestTrait then: result.task(":build").outcome == SUCCESS gradle.getOutputZipEntry("fabric-example-mod-1.0.0.jar", "META-INF/MANIFEST.MF").contains("Fabric-Loom-Version: 0.0.0+unknown") + gradle.getOutputZipEntry("fabric-example-mod-1.0.0-sources.jar", "net/fabricmc/example/mixin/ExampleMixin.java").contains("class_442") // Very basic test to ensure sources got remapped serverResult.successful() serverResult.output.contains("Hello simple Fabric mod") // A check to ensure our mod init was actually called diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy deleted file mode 100644 index 23b7d93e..00000000 --- a/src/test/groovy/net/fabricmc/loom/test/unit/MergedNestedJarProviderTest.groovy +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of fabric-loom, licensed under the MIT License (MIT). - * - * Copyright (c) 2016-2021 FabricMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package net.fabricmc.loom.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/resources/patches/fabric_api.patch b/src/test/resources/patches/fabric_api.patch index b9f578a3..c1d922e9 100644 --- a/src/test/resources/patches/fabric_api.patch +++ b/src/test/resources/patches/fabric_api.patch @@ -1,6 +1,6 @@ diff --git a/build.gradle b/build.gradle ---- a/build.gradle (revision ce6198f63bbe0e17ba631420e9186fb72cc8b2af) -+++ b/build.gradle (date 1637848132986) +--- a/build.gradle (revision 71b634e5b7845296b11be3fa6545f4fbfacc017f) ++++ b/build.gradle (date 1638654919842) @@ -31,17 +31,7 @@ throw new NullPointerException("Could not find version for " + project.name) } @@ -20,43 +20,3 @@ diff --git a/build.gradle b/build.gradle } def getBranch() { -@@ -132,9 +122,8 @@ - include "**/*.java" - } - -- task sourcesJar(type: Jar, dependsOn: classes) { -- archiveClassifier = "sources" -- from sourceSets.main.allSource -+ java { -+ withSourcesJar() - } - - checkstyle { -@@ -229,12 +218,16 @@ - publications { - mavenJava(MavenPublication) { - from components.java -+ -+ artifact javadocJar - } - } - - setupRepositories(repositories) - } - -+ loom.disableDeprecatedPomGeneration(publishing.publications.mavenJava) -+ - javadoc.enabled = false - - afterEvaluate { -@@ -242,10 +235,6 @@ - genSourcesWithFernFlower.enabled = false - genSourcesWithCfr.enabled = false - unpickJar.enabled = false -- -- // Work around a loom bug causing empty jars to be pushed to maven local. -- publishMavenJavaPublicationToMavenLocal.dependsOn rootProject.tasks.getByName("remapAllJars") -- publishMavenJavaPublicationToMavenLocal.dependsOn rootProject.tasks.getByName("remapAllSources") - } - } - diff --git a/src/test/resources/projects/simple/build.gradle b/src/test/resources/projects/simple/build.gradle index 46b957ce..5ce232ea 100644 --- a/src/test/resources/projects/simple/build.gradle +++ b/src/test/resources/projects/simple/build.gradle @@ -29,6 +29,9 @@ dependencies { // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. // You may need to force-disable transitiveness on them. + + // Example include + include "org.xerial:sqlite-jdbc:3.36.0.3" } tasks.withType(JavaCompile).configureEach { From e4244dc895d7a0c915cad45faf63ad414b288555 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Mon, 20 Dec 2021 16:54:03 +0000 Subject: [PATCH 03/15] Remove idea-ext plugin, replace with 1 internal gradle api. Vastly improves import performance. --- build.gradle | 6 --- .../ide/idea/IdeaConfiguration.java | 46 ++----------------- 2 files changed, 5 insertions(+), 47 deletions(-) diff --git a/build.gradle b/build.gradle index 53b5e1dd..74ec4d8d 100644 --- a/build.gradle +++ b/build.gradle @@ -37,9 +37,6 @@ repositories { url = 'https://maven.fabricmc.net/' } mavenCentral() - maven { - url = 'https://plugins.gradle.org/m2/' - } mavenLocal() } @@ -95,9 +92,6 @@ dependencies { // source code remapping implementation ('net.fabricmc:mercury:0.2.4') - // IDEA support - implementation ('gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.1.1') - // Kapt integration compileOnly('org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.0') diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java index 17a646d7..a8055d69 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaConfiguration.java @@ -24,19 +24,11 @@ package net.fabricmc.loom.configuration.ide.idea; -import java.util.Objects; +import java.util.List; -import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; -import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.tasks.TaskProvider; -import org.gradle.plugins.ide.idea.model.IdeaModel; -import org.gradle.plugins.ide.idea.model.IdeaProject; -import org.jetbrains.gradle.ext.ActionDelegationConfig; -import org.jetbrains.gradle.ext.IdeaExtPlugin; -import org.jetbrains.gradle.ext.ProjectSettings; -import org.jetbrains.gradle.ext.RunConfiguration; -import org.jetbrains.gradle.ext.TaskTriggersConfig; +import org.gradle.execution.taskgraph.TaskExecutionGraphInternal; public class IdeaConfiguration { public static void setup(Project project) { @@ -48,36 +40,8 @@ public class IdeaConfiguration { return; } - project.getPlugins().apply(IdeaExtPlugin.class); - project.getPlugins().withType(IdeaExtPlugin.class, ideaExtPlugin -> { - if (project != project.getRootProject()) { - // Also ensure it's applied to the root project. - project.getRootProject().getPlugins().apply(IdeaExtPlugin.class); - } - - final IdeaModel ideaModel = project.getRootProject().getExtensions().findByType(IdeaModel.class); - - if (ideaModel == null) { - return; - } - - final IdeaProject ideaProject = ideaModel.getProject(); - - if (ideaProject == null) { - return; - } - - final ProjectSettings settings = getExtension(ideaProject, ProjectSettings.class); - final ActionDelegationConfig delegateActions = getExtension(settings, ActionDelegationConfig.class); - final TaskTriggersConfig taskTriggers = getExtension(settings, TaskTriggersConfig.class); - final NamedDomainObjectContainer runConfigurations = (NamedDomainObjectContainer) ((ExtensionAware) settings).getExtensions().getByName("runConfigurations"); - - // Run the sync task on import - taskTriggers.afterSync(ideaSyncTask); - }); - } - - private static T getExtension(Object extensionAware, Class type) { - return Objects.requireNonNull(((ExtensionAware) extensionAware).getExtensions().getByType(type)); + // Run the idea sync task, is this exposed via the api? + final TaskExecutionGraphInternal taskGraph = (TaskExecutionGraphInternal) project.getGradle().getTaskGraph(); + taskGraph.whenReady(taskExecutionGraph -> taskGraph.addEntryTasks(List.of(ideaSyncTask.get()))); } } From b377db7297467d40725d6e76d2d8076a6d2bc085 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 22 Dec 2021 22:39:04 +0000 Subject: [PATCH 04/15] Only warn when the yarn version does not match the minecraft version. Should allow yarn versions to be used across mc versions when needed. --- .../configuration/providers/mappings/MappingsProviderImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 8ed07b90..fd1bce5f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -196,7 +196,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings 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)); + getProject().getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion); } // We can save reading the zip file + header by checking the file name From 53b839b739c3c96555a29685be85050924f7f4ee Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 00:10:58 +0000 Subject: [PATCH 05/15] Decompiler API improvements: * Fix decompiler tasks getting registered in afterEvaluate * Allow decompilers to add file collections to the forked JVM classpath. * General code cleanup. --- .../loom/api/LoomGradleExtensionAPI.java | 3 +- .../loom/api/decompilers/LoomDecompiler.java | 12 ++++ .../configuration/CompileConfiguration.java | 64 ++++++++++++++--- .../extension/LoomGradleExtensionApiImpl.java | 8 +-- .../loom/task/GenerateSourcesTask.java | 12 ++++ .../net/fabricmc/loom/task/LoomTasks.java | 68 ++++--------------- .../loom/test/LoomTestConstants.groovy | 2 +- .../test/integration/DecompileTest.groovy | 6 +- .../resources/projects/decompile/build.gradle | 4 ++ 9 files changed, 106 insertions(+), 73 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 5f94336e..d5d50ddc 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.api; import org.gradle.api.Action; +import org.gradle.api.DomainObjectCollection; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.artifacts.Dependency; import org.gradle.api.file.ConfigurableFileCollection; @@ -55,7 +56,7 @@ public interface LoomGradleExtensionAPI { getShareRemapCaches().set(true); } - ListProperty getGameDecompilers(); + DomainObjectCollection getGameDecompilers(); default void addDecompiler(LoomDecompiler decompiler) { getGameDecompilers().add(decompiler); diff --git a/src/main/java/net/fabricmc/loom/api/decompilers/LoomDecompiler.java b/src/main/java/net/fabricmc/loom/api/decompilers/LoomDecompiler.java index 294ba5b2..d903fd96 100644 --- a/src/main/java/net/fabricmc/loom/api/decompilers/LoomDecompiler.java +++ b/src/main/java/net/fabricmc/loom/api/decompilers/LoomDecompiler.java @@ -26,6 +26,10 @@ package net.fabricmc.loom.api.decompilers; import java.nio.file.Path; +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.jetbrains.annotations.Nullable; + public interface LoomDecompiler { String name(); @@ -37,4 +41,12 @@ public interface LoomDecompiler { * @param metaData Additional information that may or may not be needed while decompiling */ void decompile(Path compiledJar, Path sourcesDestination, Path linemapDestination, DecompilationMetadata metaData); + + /** + * Add additional classpath entries to the decompiler classpath, can return a configuration. + */ + @Nullable + default FileCollection getBootstrapClasspath(Project project) { + return null; + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 719e90d4..1826d759 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -24,6 +24,7 @@ package net.fabricmc.loom.configuration; +import java.io.File; import java.nio.charset.StandardCharsets; import org.gradle.api.NamedDomainObjectProvider; @@ -33,6 +34,7 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.tasks.AbstractCopyTask; import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.compile.JavaCompile; import org.gradle.api.tasks.javadoc.Javadoc; @@ -44,6 +46,8 @@ import net.fabricmc.loom.configuration.providers.LaunchProvider; import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.extension.MixinExtension; +import net.fabricmc.loom.task.GenerateSourcesTask; +import net.fabricmc.loom.task.UnpickJarTask; import net.fabricmc.loom.util.Constants; public final class CompileConfiguration { @@ -125,10 +129,10 @@ public final class CompileConfiguration { final JavaPluginExtension javaPluginExtension = p.getExtensions().getByType(JavaPluginExtension.class); LoomGradleExtension extension = LoomGradleExtension.get(p); - SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); - - Javadoc javadoc = (Javadoc) p.getTasks().getByName(JavaPlugin.JAVADOC_TASK_NAME); - javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); + p.getTasks().named(JavaPlugin.JAVADOC_TASK_NAME, Javadoc.class).configure(javadoc -> { + final SourceSet main = javaPluginExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + javadoc.setClasspath(main.getOutput().plus(main.getCompileClasspath())); + }); p.afterEvaluate(project -> { LoomDependencyManager dependencyManager = new LoomDependencyManager(); @@ -140,10 +144,6 @@ public final class CompileConfiguration { dependencyManager.handleDependencies(project); - project.getTasks().getByName("idea").finalizedBy(project.getTasks().getByName("genIdeaWorkspace")); - project.getTasks().getByName("eclipse").finalizedBy(project.getTasks().getByName("genEclipseRuns")); - project.getTasks().getByName("cleanEclipse").finalizedBy(project.getTasks().getByName("cleanEclipseRuns")); - extension.getRemapArchives().finalizeValue(); MixinExtension mixin = LoomGradleExtension.get(project).getMixin(); @@ -151,10 +151,16 @@ public final class CompileConfiguration { if (mixin.getUseLegacyMixinAp().get()) { setupMixinAp(project, mixin); } + + configureDecompileTasks(project); }); + finalizedBy(p, "idea", "genIdeaWorkspace"); + finalizedBy(p, "eclipse", "genEclipseRuns"); + finalizedBy(p, "cleanEclipse", "cleanEclipseRuns"); + // Add the "dev" jar to the "namedElements" configuration - p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().getByName("jar"))); + p.artifacts(artifactHandler -> artifactHandler.add(Constants.Configurations.NAMED_ELEMENTS, p.getTasks().named("jar"))); // 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 @@ -191,7 +197,47 @@ public final class CompileConfiguration { } } + private static void configureDecompileTasks(Project project) { + final TaskContainer tasks = project.getTasks(); + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); + + File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); + + if (mappingsProvider.hasUnpickDefinitions()) { + File outputJar = mappingsProvider.mappedProvider.getUnpickedJar(); + + tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> { + unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile()); + unpickJarTask.getInputJar().set(mappingsProvider.mappedProvider.getMappedJar()); + unpickJarTask.getOutputJar().set(outputJar); + }); + + mappedJar = outputJar; + } + + final File inputJar = mappedJar; + + extension.getGameDecompilers().configureEach(decompiler -> { + String taskName = "genSourcesWith" + decompiler.name(); + + // Set the input jar for the task after evaluation has occurred. + tasks.named(taskName, GenerateSourcesTask.class).configure(task -> { + task.getInputJar().set(inputJar); + + if (mappingsProvider.hasUnpickDefinitions()) { + task.dependsOn(tasks.named("unpickJar")); + } + }); + }); + } + private static void extendsFrom(String a, String b, Project project) { project.getConfigurations().getByName(a, configuration -> configuration.extendsFrom(project.getConfigurations().getByName(b))); } + + private static void finalizedBy(Project project, String a, String b) { + project.getTasks().named(a).configure(task -> task.finalizedBy(project.getTasks().named(b))); + } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index f833e52e..78b1da5e 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.extension; import org.gradle.api.Action; +import org.gradle.api.DomainObjectCollection; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; import org.gradle.api.artifacts.Dependency; @@ -52,7 +53,7 @@ import net.fabricmc.loom.util.DeprecationHelper; */ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionAPI { protected final DeprecationHelper deprecationHelper; - protected final ListProperty decompilers; + protected final DomainObjectCollection decompilers; protected final ListProperty jarProcessors; protected final ConfigurableFileCollection log4jConfigs; protected final RegularFileProperty accessWidener; @@ -70,8 +71,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected LoomGradleExtensionApiImpl(Project project, LoomFiles directories) { this.runConfigs = project.container(RunConfigSettings.class, baseName -> new RunConfigSettings(project, baseName)); - this.decompilers = project.getObjects().listProperty(LoomDecompiler.class) - .empty(); + this.decompilers = project.getObjects().domainObjectSet(LoomDecompiler.class); this.jarProcessors = project.getObjects().listProperty(JarProcessor.class) .empty(); this.log4jConfigs = project.files(directories.getDefaultLog4jConfigFile()); @@ -110,7 +110,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA } @Override - public ListProperty getGameDecompilers() { + public DomainObjectCollection getGameDecompilers() { return decompilers; } diff --git a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java index 9a127d86..3faf6002 100644 --- a/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java @@ -42,11 +42,13 @@ import java.util.stream.Collectors; import javax.inject.Inject; import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.FileCollection; import org.gradle.api.file.RegularFileProperty; import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.TaskAction; import org.gradle.workers.WorkAction; import org.gradle.workers.WorkParameters; @@ -87,6 +89,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { @Input public abstract MapProperty getOptions(); + @InputFiles + public abstract ConfigurableFileCollection getClasspath(); + @Inject public abstract WorkerExecutor getWorkerExecutor(); @@ -100,6 +105,12 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { Objects.requireNonNull(getDecompilerConstructor(this.decompiler.getClass().getCanonicalName()), "%s must have a no args constructor".formatted(this.decompiler.getClass().getCanonicalName())); + FileCollection decompilerClasspath = decompiler.getBootstrapClasspath(getProject()); + + if (decompilerClasspath != null) { + getClasspath().from(decompilerClasspath); + } + getOutputs().upToDateWhen((o) -> false); getMaxMemory().convention(4096L).finalizeValueOnRead(); getOptions().finalizeValueOnRead(); @@ -177,6 +188,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask { spec.forkOptions(forkOptions -> { forkOptions.setMaxHeapSize("%dm".formatted(getMaxMemory().get())); forkOptions.systemProperty(WorkerDaemonClientsManagerHelper.MARKER_PROP, jvmMarkerValue); + forkOptions.bootstrapClasspath(getClasspath()); }); }); } diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index f34746f5..971664fe 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.java @@ -24,17 +24,13 @@ package net.fabricmc.loom.task; -import java.io.File; - import com.google.common.base.Preconditions; import org.gradle.api.Project; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; 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.MappingsProviderImpl; import net.fabricmc.loom.util.Constants; public final class LoomTasks { @@ -43,7 +39,6 @@ public final class LoomTasks { public static void registerTasks(Project project) { TaskContainer tasks = project.getTasks(); - LoomGradleExtension extension = LoomGradleExtension.get(project); tasks.register("migrateMappings", MigrateMappingsTask.class, t -> { t.setDescription("Migrates mappings to a new version."); @@ -118,58 +113,21 @@ public final class LoomTasks { } private static void registerDecompileTasks(TaskContainer tasks, Project project) { - LoomGradleExtension extension = LoomGradleExtension.get(project); - - project.afterEvaluate(p -> { - MappingsProviderImpl mappingsProvider = extension.getMappingsProvider(); - - if (mappingsProvider.mappedProvider == null) { - // If this is ever null something has gone badly wrong, - // for some reason for another this afterEvaluate still gets called when something has gone badly - // wrong, returning here seems to produce nicer errors. - return; - } - - File mappedJar = mappingsProvider.mappedProvider.getMappedJar(); - - if (mappingsProvider.hasUnpickDefinitions()) { - File outputJar = mappingsProvider.mappedProvider.getUnpickedJar(); - - tasks.register("unpickJar", UnpickJarTask.class, unpickJarTask -> { - unpickJarTask.getUnpickDefinitions().set(mappingsProvider.getUnpickDefinitionsFile()); - unpickJarTask.getInputJar().set(mappingsProvider.mappedProvider.getMappedJar()); - unpickJarTask.getOutputJar().set(outputJar); - }); - - mappedJar = outputJar; - } - - final File inputJar = mappedJar; - - extension.getGameDecompilers().finalizeValue(); - - for (LoomDecompiler decompiler : extension.getGameDecompilers().get()) { - String taskName = "genSourcesWith" + decompiler.name(); - // Decompiler will be passed to the constructor of GenerateSourcesTask - tasks.register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> { - task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name())); - task.setGroup(Constants.TaskGroup.FABRIC); - task.getInputJar().set(inputJar); - - if (mappingsProvider.hasUnpickDefinitions()) { - task.dependsOn(tasks.named("unpickJar")); - } - - task.dependsOn(tasks.named("validateAccessWidener")); - }); - } - - tasks.register("genSources", task -> { - task.setDescription("Decompile minecraft using the default decompiler."); + LoomGradleExtension.get(project).getGameDecompilers().configureEach(decompiler -> { + String taskName = "genSourcesWith" + decompiler.name(); + // Decompiler will be passed to the constructor of GenerateSourcesTask + tasks.register(taskName, GenerateSourcesTask.class, decompiler).configure(task -> { + task.setDescription("Decompile minecraft using %s.".formatted(decompiler.name())); task.setGroup(Constants.TaskGroup.FABRIC); - - task.dependsOn(project.getTasks().named("genSourcesWithCfr")); + task.dependsOn(tasks.named("validateAccessWidener")); }); }); + + tasks.register("genSources", task -> { + task.setDescription("Decompile minecraft using the default decompiler."); + task.setGroup(Constants.TaskGroup.FABRIC); + + task.dependsOn(project.getTasks().named("genSourcesWithCfr")); + }); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 74814eb4..01cc67ba 100644 --- a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy @@ -28,7 +28,7 @@ import org.gradle.util.GradleVersion class LoomTestConstants { public final static String DEFAULT_GRADLE = GradleVersion.current().getVersion() - public final static String PRE_RELEASE_GRADLE = "7.4-20211219231013+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20211228231407+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy index d2ed4b26..83c4c401 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy @@ -45,9 +45,9 @@ class DecompileTest extends Specification implements GradleProjectTestTrait { where: decompiler | task | version - 'fernflower' | "genSourcesWithFernFlower" | DEFAULT_GRADLE - 'fernflower' | "genSourcesWithFernFlower" | PRE_RELEASE_GRADLE - 'cfr' | "genSourcesWithCfr" | DEFAULT_GRADLE +// 'fernflower' | "genSourcesWithFernFlower" | DEFAULT_GRADLE +// 'fernflower' | "genSourcesWithFernFlower" | PRE_RELEASE_GRADLE +// 'cfr' | "genSourcesWithCfr" | DEFAULT_GRADLE 'cfr' | "genSourcesWithCfr" | PRE_RELEASE_GRADLE } } diff --git a/src/test/resources/projects/decompile/build.gradle b/src/test/resources/projects/decompile/build.gradle index 2c859c0f..934a8320 100644 --- a/src/test/resources/projects/decompile/build.gradle +++ b/src/test/resources/projects/decompile/build.gradle @@ -7,3 +7,7 @@ dependencies { mappings "net.fabricmc:yarn:21w38a+build.11:v2" modImplementation "net.fabricmc:fabric-loader:0.11.7" } + +tasks.named("genSourcesWithCfr") { + options.put("test", "value") +} \ No newline at end of file From 9ca915df15a55af7f240c403c859ff6f89441475 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 00:12:27 +0000 Subject: [PATCH 06/15] Undo accidental code commenting. --- .../net/fabricmc/loom/test/integration/DecompileTest.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy index 83c4c401..d2ed4b26 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/DecompileTest.groovy @@ -45,9 +45,9 @@ class DecompileTest extends Specification implements GradleProjectTestTrait { where: decompiler | task | version -// 'fernflower' | "genSourcesWithFernFlower" | DEFAULT_GRADLE -// 'fernflower' | "genSourcesWithFernFlower" | PRE_RELEASE_GRADLE -// 'cfr' | "genSourcesWithCfr" | DEFAULT_GRADLE + 'fernflower' | "genSourcesWithFernFlower" | DEFAULT_GRADLE + 'fernflower' | "genSourcesWithFernFlower" | PRE_RELEASE_GRADLE + 'cfr' | "genSourcesWithCfr" | DEFAULT_GRADLE 'cfr' | "genSourcesWithCfr" | PRE_RELEASE_GRADLE } } From 6aa552bad0ef1a9d61f6c2d61a9f31f86831e510 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 18:34:09 +0000 Subject: [PATCH 07/15] Add task to download gradle sources next to the api jar. --- build.gradle | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/build.gradle b/build.gradle index 74ec4d8d..775cd0b9 100644 --- a/build.gradle +++ b/build.gradle @@ -173,6 +173,7 @@ test { useJUnitPlatform() } +import org.gradle.util.GradleVersion import org.w3c.dom.Document import org.w3c.dom.Element import org.w3c.dom.Node @@ -299,4 +300,25 @@ task writeActionsTestMatrix() { tasks.named('wrapper') { distributionType = Wrapper.DistributionType.ALL +} + +/** + * Run this task to download the gradle sources next to the api jar, you may need to manually attach the sources jar + */ +task downloadGradleSources() { + doLast { + // Awful hack to find the gradle api location + def gradleApiFile = project.configurations.detachedConfiguration(dependencies.gradleApi()).files.stream() + .filter { + it.name.startsWith("gradle-api") + }.findFirst().orElseThrow() + + def gradleApiSources = new File(gradleApiFile.absolutePath.replace(".jar", "-sources.jar")) + def url = "https://services.gradle.org/distributions/gradle-${GradleVersion.current().getVersion()}-src.zip" + + gradleApiSources.delete() + + println("Downloading (${url}) to (${gradleApiSources})") + gradleApiSources << new URL(url).newInputStream() + } } \ No newline at end of file From 2752b61cb78a68933fc46714de0077036c3565cd Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 19:27:24 +0000 Subject: [PATCH 08/15] Cleanup signature fixer code + add basic integration test. Fixes #546 --- .../minecraft/MinecraftMappedProvider.java | 61 +------------ .../minecraft/SignatureFixerApplyVisitor.java | 85 +++++++++++++++++++ .../integration/SignatureFixesTest.groovy | 57 +++++++++++++ 3 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SignatureFixerApplyVisitor.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/SignatureFixesTest.groovy 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 0025e91d..d1ecf292 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 @@ -29,15 +29,10 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; -import java.util.HashMap; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.gradle.api.Project; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.commons.Remapper; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.DependencyProvider; @@ -47,7 +42,6 @@ import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; -import net.fabricmc.tinyremapper.api.TrClass; public class MinecraftMappedProvider extends DependencyProvider { private File minecraftMappedJar; @@ -110,66 +104,17 @@ public class MinecraftMappedProvider extends DependencyProvider { for (String toM : Arrays.asList(MappingsNamespace.NAMED.toString(), MappingsNamespace.INTERMEDIARY.toString())) { final boolean toNamed = MappingsNamespace.NAMED.toString().equals(toM); final boolean toIntermediary = MappingsNamespace.INTERMEDIARY.toString().equals(toM); - final boolean fixSignatures = mappingsProvider.getSignatureFixes() != null; final Path output = toNamed ? outputMapped : outputIntermediary; - getProject().getLogger().lifecycle(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); + getProject().getLogger().info(":remapping minecraft (TinyRemapper, " + fromM + " -> " + toM + ")"); Files.deleteIfExists(output); - // Bit ugly but whatever, the whole issue is a bit ugly :) - AtomicReference> remappedSignatures = new AtomicReference<>(); + final Map remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(toIntermediary, mappingsProvider, getProject(), toM); TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), fromM, toM, true, (builder) -> { - if (!fixSignatures) { - return; - } - - builder.extraPostApplyVisitor(new TinyRemapper.ApplyVisitorProvider() { - @Override - public ClassVisitor insertApplyVisitor(TrClass cls, ClassVisitor next) { - return new ClassVisitor(Constants.ASM_VERSION, next) { - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - Map signatureFixes = Objects.requireNonNull(remappedSignatures.get(), "Could not get remapped signatures"); - - if (signature == null) { - signature = signatureFixes.getOrDefault(name, null); - - if (signature != null) { - getProject().getLogger().info("Replaced signature for {} with {}", name, signature); - } - } - - super.visit(version, access, name, signature, superName, interfaces); - } - }; - } - }); + builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures)); }); - if (fixSignatures) { - if (toIntermediary) { - // No need to remap, as these are already intermediary - remappedSignatures.set(mappingsProvider.getSignatureFixes()); - } else { - // Remap the sig fixes from intermediary to the target namespace - final Map remapped = new HashMap<>(); - final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(getProject(), MappingsNamespace.INTERMEDIARY.toString(), toM); - final Remapper sigAsmRemapper = sigTinyRemapper.getEnvironment().getRemapper(); - - // Remap the class names and the signatures using a new tiny remapper instance. - for (Map.Entry entry : mappingsProvider.getSignatureFixes().entrySet()) { - remapped.put( - sigAsmRemapper.map(entry.getKey()), - sigAsmRemapper.mapSignature(entry.getValue(), false) - ); - } - - sigTinyRemapper.finish(); - remappedSignatures.set(remapped); - } - } - try (OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(output).build()) { outputConsumer.addNonClassFiles(input); remapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(getProject())); diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SignatureFixerApplyVisitor.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SignatureFixerApplyVisitor.java new file mode 100644 index 00000000..0ebff346 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SignatureFixerApplyVisitor.java @@ -0,0 +1,85 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.providers.minecraft; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.gradle.api.Project; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.commons.Remapper; + +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.tinyremapper.TinyRemapper; +import net.fabricmc.tinyremapper.api.TrClass; + +public record SignatureFixerApplyVisitor(Map signatureFixes) implements TinyRemapper.ApplyVisitorProvider { + @Override + public ClassVisitor insertApplyVisitor(TrClass cls, ClassVisitor next) { + return new ClassVisitor(Constants.ASM_VERSION, next) { + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if (signature == null) { + signature = signatureFixes.getOrDefault(name, null); + } + + super.visit(version, access, name, signature, superName, interfaces); + } + }; + } + + public static Map getRemappedSignatures(boolean toIntermediary, MappingsProviderImpl mappingsProvider, Project project, String targetNamespace) throws IOException { + if (mappingsProvider.getSignatureFixes() == null) { + // No fixes + return Collections.emptyMap(); + } + + if (toIntermediary) { + // No need to remap, as these are already intermediary + return mappingsProvider.getSignatureFixes(); + } + + // Remap the sig fixes from intermediary to the target namespace + final Map remapped = new HashMap<>(); + final TinyRemapper sigTinyRemapper = TinyRemapperHelper.getTinyRemapper(project, MappingsNamespace.INTERMEDIARY.toString(), targetNamespace); + final Remapper sigAsmRemapper = sigTinyRemapper.getEnvironment().getRemapper(); + + // Remap the class names and the signatures using a new tiny remapper instance. + for (Map.Entry entry : mappingsProvider.getSignatureFixes().entrySet()) { + remapped.put( + sigAsmRemapper.map(entry.getKey()), + sigAsmRemapper.mapSignature(entry.getValue(), false) + ); + } + + sigTinyRemapper.finish(); + return remapped; + } +} diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SignatureFixesTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SignatureFixesTest.groovy new file mode 100644 index 00000000..e91b7c1c --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SignatureFixesTest.groovy @@ -0,0 +1,57 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.integration + +import net.fabricmc.loom.test.util.GradleProjectTestTrait +import spock.lang.Specification +import spock.lang.Unroll + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +// Basic test to just ensure it builds. +class SignatureFixesTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "build (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + dependencies { + minecraft "com.mojang:minecraft:1.18-pre1" + mappings "net.fabricmc:yarn:1.18-pre1+build.14:v2" + } + ''' + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } +} From 005d180e0ce48a0b1eabf2dbd4f0a24e7902767b Mon Sep 17 00:00:00 2001 From: Juuxel <6596629+Juuxel@users.noreply.github.com> Date: Wed, 29 Dec 2021 23:59:19 +0200 Subject: [PATCH 09/15] Fix up-to-date checks for ValidateAccessWidenerTask (#565) * Apply mitigation's against Log4J2 exploit CVE-2021-44228. * Only warn when the yarn version does not match the minecraft version. Should allow yarn versions to be used across mc versions when needed. * Fix up-to-date checks for ValidateAccessWidenerTask Co-authored-by: modmuss50 --- .../loom/configuration/providers/LaunchProvider.java | 1 + .../providers/mappings/MappingsProviderImpl.java | 2 +- .../loom/task/ValidateAccessWidenerTask.java | 3 +++ src/main/resources/log4j2.fabric.xml | 12 ++++++------ 4 files changed, 11 insertions(+), 7 deletions(-) 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 fcfa226f..aeb387d0 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/LaunchProvider.java @@ -60,6 +60,7 @@ public class LaunchProvider extends DependencyProvider { .property("fabric.development", "true") .property("fabric.remapClasspathFile", getRemapClasspathFile().getAbsolutePath()) .property("log4j.configurationFile", getAllLog4JConfigFiles()) + .property("log4j2.formatMsgNoLookups", "true") .property("client", "java.library.path", nativesPath) .property("client", "org.lwjgl.librarypath", nativesPath) 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 index 2ab18e98..3c84fd96 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -196,7 +196,7 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings 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)); + getProject().getLogger().warn("Minecraft Version ({}) does not match yarn's minecraft version ({})", minecraftVersion, yarnMinecraftVersion); } // We can save reading the zip file + header by checking the file name diff --git a/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java b/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java index 51f9d97c..4ebbb331 100644 --- a/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java +++ b/src/main/java/net/fabricmc/loom/task/ValidateAccessWidenerTask.java @@ -59,6 +59,9 @@ public abstract class ValidateAccessWidenerTask extends DefaultTask { getAccessWidener().convention(extension.getAccessWidenerPath()).finalizeValueOnRead(); getTargetJar().convention(getProject().getObjects().fileProperty().fileValue(extension.getMinecraftMappedProvider().getMappedJar())).finalizeValueOnRead(); + + // Ignore outputs for up-to-date checks as there aren't any (so only inputs are checked) + getOutputs().upToDateWhen(task -> true); } @TaskAction diff --git a/src/main/resources/log4j2.fabric.xml b/src/main/resources/log4j2.fabric.xml index fcfd27b5..69c9c0da 100644 --- a/src/main/resources/log4j2.fabric.xml +++ b/src/main/resources/log4j2.fabric.xml @@ -7,7 +7,7 @@ - + @@ -15,9 +15,9 @@ - + - + @@ -25,9 +25,9 @@ - + - + @@ -38,7 +38,7 @@ - + From e985cb85d409aa7e54cae8128ac28201e6d1880e Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 22:02:22 +0000 Subject: [PATCH 10/15] Only quote program arguments if they actually contain spaces --- .../net/fabricmc/loom/configuration/ide/RunConfig.java | 7 ++++++- .../net/fabricmc/loom/test/unit/RunConfigUnitTest.groovy | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 3d1b4a08..37069002 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -210,7 +210,12 @@ public class RunConfig { } first = false; - sb.append("\"").append(arg).append("\""); + + if (arg.contains(" ")) { + sb.append("\"").append(arg).append("\""); + } else { + sb.append(arg); + } } return sb.toString(); diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/RunConfigUnitTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/RunConfigUnitTest.groovy index 8a804d71..63d00e0c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/RunConfigUnitTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/RunConfigUnitTest.groovy @@ -33,6 +33,6 @@ class RunConfigUnitTest extends Specification { def args = RunConfig.joinArguments(["-Dfabric.test=123", "-Dfabric.test=abc 123"]) then: - args == '"-Dfabric.test=123" "-Dfabric.test=abc 123"' + args == '-Dfabric.test=123 "-Dfabric.test=abc 123"' } } From 63f2b51b2cb3bc0340a11dd61d916db0198f77a4 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 22:06:34 +0000 Subject: [PATCH 11/15] Fix include generating modid's that are too long for loader Co-authored-by: NebelNidas --- .../loom/build/nesting/IncludedJarFactory.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java index 610b720a..1a833a76 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java @@ -33,6 +33,7 @@ import java.util.Locale; import java.util.Set; import com.google.common.collect.Sets; +import com.google.common.hash.Hashing; import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; import org.gradle.api.Project; @@ -172,10 +173,18 @@ public final class IncludedJarFactory { // Generates a barebones mod for a dependency private static String generateModForDependency(Metadata metadata) { - final String modId = (metadata.group() + "_" + metadata.name() + metadata.classifier()) + String modId = (metadata.group() + "_" + metadata.name() + metadata.classifier()) .replaceAll("\\.", "_") .toLowerCase(Locale.ENGLISH); + // Fabric Loader can't handle modIds longer than 64 characters + if (modId.length() > 64) { + String hash = Hashing.sha256() + .hashString(modId, StandardCharsets.UTF_8) + .toString(); + modId = modId.substring(0, 50) + hash.substring(0, 14); + } + final JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("schemaVersion", 1); From 7611e3a63277452d2e0bc05ce76d3a31a298c477 Mon Sep 17 00:00:00 2001 From: "val = int(1)" Date: Wed, 29 Dec 2021 23:16:13 +0100 Subject: [PATCH 12/15] Add support for mixin files outside of root in MixinRefmapHelper (#536) * Add support for mixin files outside of root in MixinRefmapHelper * Fix checkstyle + Integration tests + Checks all srcDirs from sourceSet * Redid part that failed to save for last commit * Other issues fixed * Checkstyle again * Made getting root paths safer Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> * CodeNarc for test * Normalized all paths to fix issues on Windows * Removed debug line used to test return value of lamdba Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> Co-authored-by: modmuss50 --- .../loom/extension/MixinExtensionImpl.java | 2 +- .../net/fabricmc/loom/task/RemapJarTask.java | 24 ++++++++++++++++++- .../integration/MixinApAutoRefmapTest.groovy | 3 +++ .../example/mixin/ExampleMixinSubfolder.java | 15 ++++++++++++ .../src/main/resources/fabric.mod.json | 1 + .../resources/subfolder/subfolder.mixins.json | 14 +++++++++++ 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/projects/mixinApAutoRefmap/src/main/java/net/fabricmc/example/mixin/ExampleMixinSubfolder.java create mode 100644 src/test/resources/projects/mixinApAutoRefmap/src/main/resources/subfolder/subfolder.mixins.json diff --git a/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java b/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java index c8bf56a0..d79f5d62 100644 --- a/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/MixinExtensionImpl.java @@ -76,7 +76,7 @@ public class MixinExtensionImpl extends MixinExtensionApiImpl implements MixinEx protected PatternSet add0(SourceSet sourceSet, Provider refmapName) { if (!super.getUseLegacyMixinAp().get()) throw new IllegalStateException("You need to set useLegacyMixinAp = true to configure Mixin annotation processor."); - PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("*.json")); + PatternSet pattern = new PatternSet().setIncludes(Collections.singletonList("**/*.json")); MixinExtension.setMixinInformationContainer(sourceSet, new MixinExtension.MixinInformationContainer(sourceSet, refmapName, pattern)); isDefault = false; diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 16b86424..a9d357aa 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -136,12 +136,34 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { MixinExtension.getMixinInformationContainer(sourceSet) ); + String[] rootPaths = sourceSet.getResources().getSrcDirs().stream() + .map(root -> { + String rootPath = root.getAbsolutePath().replace("\\", "/"); + + if (rootPath.charAt(rootPath.length() - 1) != '/') { + rootPath += '/'; + } + + return rootPath; + }) + .toArray(String[]::new); + final String refmapName = container.refmapNameProvider().get(); final List mixinConfigs = container.sourceSet().getResources() .matching(container.mixinConfigPattern()) .getFiles() .stream() - .map(File::getName) + .map(file -> { + String s = file.getAbsolutePath().replace("\\", "/"); + + for (String rootPath : rootPaths) { + if (s.startsWith(rootPath)) { + s = s.substring(rootPath.length()); + } + } + + return s; + }) .filter(allMixinConfigs::contains) .toList(); diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MixinApAutoRefmapTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MixinApAutoRefmapTest.groovy index f8b5b6d0..69bfa93c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MixinApAutoRefmapTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MixinApAutoRefmapTest.groovy @@ -67,6 +67,9 @@ class MixinApAutoRefmapTest extends Specification implements GradleProjectTestTr def j5 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("irrelevant.mixins.json")))) !j5.asJsonObject.has("refmap") + def j6 = JsonParser.parseReader(new InputStreamReader(jar.getInputStream(jar.getEntry("subfolder/subfolder.mixins.json")))) + j6.asJsonObject.getAsJsonPrimitive("refmap").getAsString() == "refmap0001.json" + where: version << STANDARD_TEST_VERSIONS } diff --git a/src/test/resources/projects/mixinApAutoRefmap/src/main/java/net/fabricmc/example/mixin/ExampleMixinSubfolder.java b/src/test/resources/projects/mixinApAutoRefmap/src/main/java/net/fabricmc/example/mixin/ExampleMixinSubfolder.java new file mode 100644 index 00000000..519929ec --- /dev/null +++ b/src/test/resources/projects/mixinApAutoRefmap/src/main/java/net/fabricmc/example/mixin/ExampleMixinSubfolder.java @@ -0,0 +1,15 @@ +package net.fabricmc.example.mixin; + +import net.minecraft.client.gui.screen.ChatScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChatScreen.class) +public class ExampleMixinSubfolder { + @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/mixinApAutoRefmap/src/main/resources/fabric.mod.json b/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/fabric.mod.json index 62efe822..c92f0ce7 100644 --- a/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/fabric.mod.json +++ b/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/fabric.mod.json @@ -24,6 +24,7 @@ }, "mixins": [ "main.mixins.json", + "subfolder/subfolder.mixins.json", { "config": "blabla.json", "environment": "client" diff --git a/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/subfolder/subfolder.mixins.json b/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/subfolder/subfolder.mixins.json new file mode 100644 index 00000000..ef124cac --- /dev/null +++ b/src/test/resources/projects/mixinApAutoRefmap/src/main/resources/subfolder/subfolder.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.fabricmc.example.mixin", + "compatibilityLevel": "JAVA_16", + "mixins": [ + ], + "client": [ + "ExampleMixinSubfolder" + ], + "injectors": { + "defaultRequire": 1 + } +} From 15afb457690f76f6578c09382256391c6842a9ca Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Wed, 29 Dec 2021 23:52:13 +0000 Subject: [PATCH 13/15] Exclude client only libraries in the IntelliJ server run config. --- .../configuration/ide/idea/IdeaSyncTask.java | 82 +++++++++++++- .../IdeaClasspathModificationsTest.groovy | 104 ++++++++++++++++++ 2 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java index 58a42241..ead38744 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java @@ -26,17 +26,33 @@ package net.fabricmc.loom.configuration.ide.idea; import java.io.File; import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.inject.Inject; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.gradle.api.Project; import org.gradle.api.tasks.TaskAction; +import org.jetbrains.annotations.VisibleForTesting; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfig; @@ -95,7 +111,7 @@ public abstract class IdeaSyncTask extends AbstractLoomTask { if (settings.getEnvironment().equals("server") && !excludedServerLibraries.isEmpty()) { try { - setClasspathModifications(runConfigs, excludedServerLibraries); + setClasspathModifications(runConfigs.toPath(), excludedServerLibraries); } catch (Exception e) { getProject().getLogger().error("Failed to modify run configuration xml", e); } @@ -124,7 +140,67 @@ public abstract class IdeaSyncTask extends AbstractLoomTask { return clientOnlyLibraries; } - private void setClasspathModifications(File runConfig, List exclusions) throws Exception { - // TODO modify the xml + private void setClasspathModifications(Path runConfig, List exclusions) throws IOException { + if (!IdeaUtils.supportsCustomizableClasspath()) { + return; + } + + final String inputXml = Files.readString(runConfig, StandardCharsets.UTF_8); + final String outputXml; + + try { + outputXml = setClasspathModificationsInXml(inputXml, exclusions); + } catch (Exception e) { + getLogger().error("Failed to modify idea xml", e); + + return; + } + + if (!inputXml.equals(outputXml)) { + Files.writeString(runConfig, outputXml, StandardCharsets.UTF_8); + } + } + + @VisibleForTesting + public static String setClasspathModificationsInXml(String input, List exclusions) throws Exception { + final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + final Document document = documentBuilder.parse(new InputSource(new StringReader(input))); + final Element root = document.getDocumentElement(); + + final NodeList nodeList = root.getElementsByTagName("configuration"); + assert nodeList.getLength() == 1; + + final Element configuration = (Element) nodeList.item(0); + final NodeList classpathModificationsList = configuration.getElementsByTagName("classpathModifications"); + + // Remove all the existing exclusions + for (int i = 0; i < classpathModificationsList.getLength(); i++) { + configuration.removeChild(classpathModificationsList.item(i)); + } + + final Element classpathModifications = document.createElement("classpathModifications"); + + for (String exclusionPath : exclusions) { + final Element exclusion = document.createElement("entry"); + + exclusion.setAttribute("exclude", "true"); + exclusion.setAttribute("path", exclusionPath); + + classpathModifications.appendChild(exclusion); + } + + configuration.appendChild(classpathModifications); + + final TransformerFactory transformerFactory = TransformerFactory.newInstance(); + final Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + + final DOMSource source = new DOMSource(document); + + final StringWriter writer = new StringWriter(); + transformer.transform(source, new StreamResult(writer)); + + return writer.toString().replace("\r", ""); } } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy new file mode 100644 index 00000000..31677552 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/IdeaClasspathModificationsTest.groovy @@ -0,0 +1,104 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.unit + +import net.fabricmc.loom.configuration.ide.RunConfig +import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask +import org.intellij.lang.annotations.Language +import spock.lang.Specification + +import java.nio.charset.StandardCharsets + +class IdeaClasspathModificationsTest extends Specification { + + def "configure exclusions"() { + when: + def input = fromDummy() + def output = IdeaSyncTask.setClasspathModificationsInXml(input, ["/path/to/file.jar"]) + + then: + output == EXPECTED + } + + def "re-configure exclusions"() { + when: + def input = fromDummy() + def output = IdeaSyncTask.setClasspathModificationsInXml(input, ["/path/to/file.jar"]) + output = IdeaSyncTask.setClasspathModificationsInXml(output, ["/path/to/file.jar", "/path/to/another.jar"]) + + then: + output == EXPECTED2 + } + + private String fromDummy() { + String dummyConfig + + IdeaSyncTask.class.getClassLoader().getResourceAsStream("idea_run_config_template.xml").withCloseable { + dummyConfig = new String(it.readAllBytes(), StandardCharsets.UTF_8) + } + + dummyConfig = dummyConfig.replace("%NAME%", "Minecraft Client") + dummyConfig = dummyConfig.replace("%MAIN_CLASS%", "net.minecraft.client.Main") + dummyConfig = dummyConfig.replace("%IDEA_MODULE%", "main.test") + dummyConfig = dummyConfig.replace("%RUN_DIRECTORY%", ".run") + dummyConfig = dummyConfig.replace("%PROGRAM_ARGS%", RunConfig.joinArguments([]).replaceAll("\"", """)) + dummyConfig = dummyConfig.replace("%VM_ARGS%", RunConfig.joinArguments([]).replaceAll("\"", """)) + + return dummyConfig + } + + @Language("XML") + private static final String EXPECTED = ''' + + + + +'''.trim() + + @Language("XML") + private static final String EXPECTED2 = ''' + + + + +'''.trim() + +} From 03d3950d119830edb65d35bafb90c330aa3a78d1 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Thu, 30 Dec 2021 00:15:18 +0000 Subject: [PATCH 14/15] Exclude client only libraries in server run task. --- .../loom/configuration/ide/RunConfig.java | 33 ++++++++++++++++++ .../configuration/ide/idea/IdeaSyncTask.java | 34 +++---------------- .../fabricmc/loom/task/AbstractRunTask.java | 21 +++++++++++- .../projects/runconfigs/build.gradle | 6 ++-- 4 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java index 37069002..7979b1b2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfig.java @@ -24,14 +24,18 @@ package net.fabricmc.loom.configuration.ide; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import com.google.common.collect.ImmutableMap; import com.google.gson.JsonElement; @@ -46,6 +50,8 @@ import org.w3c.dom.Node; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.InstallerData; import net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask; +import net.fabricmc.loom.configuration.providers.BundleMetadata; +import net.fabricmc.loom.util.Constants; public class RunConfig { public String configName; @@ -54,6 +60,7 @@ public class RunConfig { public String mainClass; public String runDirIdeaUrl; public String runDir; + public String environment; public List vmArgs = new ArrayList<>(); public List programArgs = new ArrayList<>(); public SourceSet sourceSet; @@ -163,6 +170,7 @@ public class RunConfig { runConfig.runDirIdeaUrl = "file://$PROJECT_DIR$/" + runDir; runConfig.runDir = runDir; runConfig.sourceSet = sourceSet; + runConfig.environment = environment; // Custom parameters runConfig.programArgs.addAll(settings.getProgramArgs()); @@ -251,6 +259,31 @@ public class RunConfig { return defaultMainClass; } + public List getExcludedLibraryPaths(Project project) { + if (!environment.equals("server")) { + return Collections.emptyList(); + } + + final BundleMetadata bundleMetadata = LoomGradleExtension.get(project).getMinecraftProvider().getServerBundleMetadata(); + + if (bundleMetadata == null) { + // Legacy version + return Collections.emptyList(); + } + + final Set allLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles(); + final Set serverLibraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles(); + final List clientOnlyLibraries = new LinkedList<>(); + + for (File commonLibrary : allLibraries) { + if (!serverLibraries.contains(commonLibrary)) { + clientOnlyLibraries.add(commonLibrary.getAbsolutePath()); + } + } + + return clientOnlyLibraries; + } + private static String encodeEscaped(String s) { StringBuilder ret = new StringBuilder(); diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java index ead38744..b2b2369c 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/idea/IdeaSyncTask.java @@ -31,10 +31,7 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Collections; -import java.util.LinkedList; import java.util.List; -import java.util.Set; import javax.inject.Inject; import javax.xml.parsers.DocumentBuilder; @@ -57,9 +54,7 @@ import org.xml.sax.InputSource; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfig; import net.fabricmc.loom.configuration.ide.RunConfigSettings; -import net.fabricmc.loom.configuration.providers.BundleMetadata; import net.fabricmc.loom.task.AbstractLoomTask; -import net.fabricmc.loom.util.Constants; public abstract class IdeaSyncTask extends AbstractLoomTask { @Inject @@ -90,8 +85,6 @@ public abstract class IdeaSyncTask extends AbstractLoomTask { runConfigsDir.mkdirs(); } - final List excludedServerLibraries = getExcludedServerLibraries(); - for (RunConfigSettings settings : extension.getRunConfigs()) { if (!settings.isIdeConfigGenerated()) { continue; @@ -109,9 +102,11 @@ public abstract class IdeaSyncTask extends AbstractLoomTask { settings.makeRunDir(); - if (settings.getEnvironment().equals("server") && !excludedServerLibraries.isEmpty()) { + final List excludedLibraryPaths = config.getExcludedLibraryPaths(getProject()); + + if (!excludedLibraryPaths.isEmpty()) { try { - setClasspathModifications(runConfigs.toPath(), excludedServerLibraries); + setClasspathModifications(runConfigs.toPath(), excludedLibraryPaths); } catch (Exception e) { getProject().getLogger().error("Failed to modify run configuration xml", e); } @@ -119,27 +114,6 @@ public abstract class IdeaSyncTask extends AbstractLoomTask { } } - private List getExcludedServerLibraries() { - final BundleMetadata bundleMetadata = getExtension().getMinecraftProvider().getServerBundleMetadata(); - - if (bundleMetadata == null) { - // Legacy version - return Collections.emptyList(); - } - - final Set allLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles(); - final Set serverLibraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_SERVER_DEPENDENCIES).getFiles(); - final List clientOnlyLibraries = new LinkedList<>(); - - for (File commonLibrary : allLibraries) { - if (!serverLibraries.contains(commonLibrary)) { - clientOnlyLibraries.add(commonLibrary.getAbsolutePath()); - } - } - - return clientOnlyLibraries; - } - private void setClasspathModifications(Path runConfig, List exclusions) throws IOException { if (!IdeaUtils.supportsCustomizableClasspath()) { return; diff --git a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java index baa5c931..d88633f5 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRunTask.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.function.Function; import org.gradle.api.Project; +import org.gradle.api.specs.Spec; import org.gradle.api.tasks.JavaExec; import net.fabricmc.loom.configuration.ide.RunConfig; @@ -44,7 +45,7 @@ public abstract class AbstractRunTask extends JavaExec { setGroup(Constants.TaskGroup.FABRIC); this.config = configProvider.apply(getProject()); - setClasspath(config.sourceSet.getRuntimeClasspath()); + setClasspath(config.sourceSet.getRuntimeClasspath().filter(new LibraryFilter())); args(config.programArgs); getMainClass().set(config.mainClass); } @@ -72,4 +73,22 @@ public abstract class AbstractRunTask extends JavaExec { args.addAll(config.vmArgs); return args; } + + private class LibraryFilter implements Spec { + private List excludedLibraryPaths = null; + + @Override + public boolean isSatisfiedBy(File element) { + if (excludedLibraryPaths == null) { + excludedLibraryPaths = config.getExcludedLibraryPaths(getProject()); + } + + if (excludedLibraryPaths.contains(element.getAbsolutePath())) { + getProject().getLogger().debug("Excluding library {} from {} run config", element.getName(), config.configName); + return false; + } + + return true; + } + } } diff --git a/src/test/resources/projects/runconfigs/build.gradle b/src/test/resources/projects/runconfigs/build.gradle index 9ed945c7..a8e1f6bb 100644 --- a/src/test/resources/projects/runconfigs/build.gradle +++ b/src/test/resources/projects/runconfigs/build.gradle @@ -31,7 +31,7 @@ archivesBaseName = "fabric-example-mod" version = "1.0.0" 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" + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.12:v2" + modImplementation "net.fabricmc:fabric-loader:0.12.12" } \ No newline at end of file From ccfe12eb17c043c73a1419b2dbd766e20648b8e6 Mon Sep 17 00:00:00 2001 From: shartte Date: Tue, 4 Jan 2022 19:15:21 +0100 Subject: [PATCH 15/15] Interface Injection (#496) * Added interface injection via fabric.mod.json. * Added interface injection * Added amending of class signature with injected interface. --- .../loom/api/LoomGradleExtensionAPI.java | 9 + .../InterfaceInjectionProcessor.java | 299 ++++++++++++++++++ .../mappings/MappingsProviderImpl.java | 9 + .../extension/LoomGradleExtensionApiImpl.java | 9 + .../integration/InterfaceInjectionTest.groovy | 51 +++ .../projects/interfaceInjection/build.gradle | 18 ++ .../dummyDependency/fabric.mod.json | 12 + .../src/main/java/ExampleMod.java | 10 + .../src/main/java/InjectedInterface.java | 5 + 9 files changed, 422 insertions(+) create mode 100644 src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java create mode 100644 src/test/groovy/net/fabricmc/loom/test/integration/InterfaceInjectionTest.groovy create mode 100644 src/test/resources/projects/interfaceInjection/build.gradle create mode 100644 src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json create mode 100644 src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java create mode 100644 src/test/resources/projects/interfaceInjection/src/main/java/InjectedInterface.java diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index d5d50ddc..efec1999 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -132,4 +132,13 @@ public interface LoomGradleExtensionAPI { * @return the intermediary url template */ Property getIntermediaryUrl(); + + /** + * When true loom will inject interfaces declared in mod manifests into the minecraft jar file. + * This is used to expose interfaces that are implemented on Minecraft classes by mixins at runtime + * in the dev environment. + * + * @return the property controlling interface injection. + */ + Property getEnableInterfaceInjection(); } diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java new file mode 100644 index 00000000..16666589 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -0,0 +1,299 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.configuration.ifaceinject; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.gradle.api.Project; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.Remapper; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.configuration.RemappedConfigurationEntry; +import net.fabricmc.loom.configuration.processors.JarProcessor; +import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.Pair; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.loom.util.ZipUtils; +import net.fabricmc.tinyremapper.TinyRemapper; + +public class InterfaceInjectionProcessor implements JarProcessor { + // Filename used to store hash of injected interfaces in processed jar file + private static final String HASH_FILENAME = "injected_interfaces.sha256"; + + private final Map> injectedInterfaces; + private final Project project; + private final LoomGradleExtension extension; + private final byte[] inputHash; + private Map> remappedInjectedInterfaces; + + public InterfaceInjectionProcessor(Project project) { + this.project = project; + this.extension = LoomGradleExtension.get(project); + this.injectedInterfaces = getInjectedInterfaces().stream() + .collect(Collectors.groupingBy(InjectedInterface::className)); + + this.inputHash = hashInjectedInterfaces(); + } + + public boolean isEmpty() { + return injectedInterfaces.isEmpty(); + } + + @Override + public void setup() { + } + + @Override + public void process(File jarFile) { + // Lazily remap from intermediary->named + if (remappedInjectedInterfaces == null) { + TinyRemapper tinyRemapper = createTinyRemapper(); + Remapper remapper = tinyRemapper.getEnvironment().getRemapper(); + + try { + remappedInjectedInterfaces = new HashMap<>(injectedInterfaces.size()); + + for (Map.Entry> entry : injectedInterfaces.entrySet()) { + String namedClassName = remapper.map(entry.getKey()); + remappedInjectedInterfaces.put( + namedClassName, + entry.getValue().stream() + .map(injectedInterface -> + new InjectedInterface( + injectedInterface.modId(), + namedClassName, + remapper.map(injectedInterface.ifaceName()) + )) + .toList() + ); + } + } finally { + tinyRemapper.finish(); + } + } + + project.getLogger().lifecycle("Processing file: " + jarFile.getName()); + + try { + ZipUtils.transform(jarFile.toPath(), getTransformers()); + } catch (IOException e) { + throw new RuntimeException("Failed to apply interface injections to " + jarFile, e); + } + } + + private List>> getTransformers() { + return remappedInjectedInterfaces.keySet().stream() + .map(string -> new Pair<>(string.replaceAll("\\.", "/") + ".class", getTransformer(string))) + .collect(Collectors.toList()); + } + + private ZipUtils.UnsafeUnaryOperator getTransformer(String className) { + return input -> { + ClassReader reader = new ClassReader(input); + ClassWriter writer = new ClassWriter(0); + List ifaces = remappedInjectedInterfaces.get(className); + ClassVisitor classVisitor = new InjectingClassVisitor(Constants.ASM_VERSION, writer, ifaces); + + // Log which mods add which interface to the class + project.getLogger().info("Injecting interfaces into " + className + ": " + + ifaces.stream().map(i -> i.ifaceName() + " [" + i.modId() + "]" + ).collect(Collectors.joining(", "))); + + reader.accept(classVisitor, 0); + return writer.toByteArray(); + }; + } + + @Override + public boolean isInvalid(File file) { + byte[] hash; + + try { + hash = ZipUtils.unpackNullable(file.toPath(), HASH_FILENAME); + } catch (IOException e) { + return true; + } + + if (hash == null) { + return true; + } + + return !Arrays.equals(inputHash, hash); + } + + private List getInjectedInterfaces() { + List result = new ArrayList<>(); + + for (RemappedConfigurationEntry entry : Constants.MOD_COMPILE_ENTRIES) { + // Only apply injected interfaces from mods that are part of the compile classpath + if (!entry.compileClasspath()) { + continue; + } + + Set artifacts = extension.getLazyConfigurationProvider(entry.sourceConfiguration()) + .get() + .resolve(); + + for (File artifact : artifacts) { + result.addAll(InjectedInterface.fromModJar(artifact.toPath())); + } + } + + return result; + } + + private record InjectedInterface(String modId, String className, String ifaceName) { + /** + * Reads the injected interfaces contained in a mod jar, or returns null if there is none. + */ + public static List fromModJar(Path modJarPath) { + byte[] modJsonBytes; + + try { + modJsonBytes = ZipUtils.unpackNullable(modJarPath, "fabric.mod.json"); + } catch (IOException e) { + throw new RuntimeException("Failed to extract fabric.mod.json from " + modJarPath); + } + + if (modJsonBytes == null) { + return Collections.emptyList(); + } + + JsonObject jsonObject = new Gson().fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); + + String modId = jsonObject.get("id").getAsString(); + + if (!jsonObject.has("custom")) { + return Collections.emptyList(); + } + + JsonObject custom = jsonObject.getAsJsonObject("custom"); + + if (!custom.has("loom:injected_interfaces")) { + return Collections.emptyList(); + } + + JsonObject addedIfaces = custom.getAsJsonObject("loom:injected_interfaces"); + + List result = new ArrayList<>(); + + for (String className : addedIfaces.keySet()) { + JsonArray ifaceNames = addedIfaces.getAsJsonArray(className); + + for (JsonElement ifaceName : ifaceNames) { + result.add(new InjectedInterface(modId, className, ifaceName.getAsString())); + } + } + + return result; + } + } + + private static class InjectingClassVisitor extends ClassVisitor { + private final List injectedInterfaces; + + InjectingClassVisitor(int asmVersion, ClassWriter writer, List injectedInterfaces) { + super(asmVersion, writer); + this.injectedInterfaces = injectedInterfaces; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + Set modifiedInterfaces = new LinkedHashSet<>(interfaces.length + injectedInterfaces.size()); + Collections.addAll(modifiedInterfaces, interfaces); + + for (InjectedInterface injectedInterface : injectedInterfaces) { + modifiedInterfaces.add(injectedInterface.ifaceName()); + } + + // See JVMS: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-ClassSignature + if (signature != null) { + var resultingSignature = new StringBuilder(signature); + + for (InjectedInterface injectedInterface : injectedInterfaces) { + String superinterfaceSignature = "L" + injectedInterface.ifaceName() + ";"; + + if (resultingSignature.indexOf(superinterfaceSignature) == -1) { + resultingSignature.append(superinterfaceSignature); + } + } + + signature = resultingSignature.toString(); + } + + super.visit(version, access, name, signature, superName, modifiedInterfaces.toArray(new String[0])); + } + } + + private TinyRemapper createTinyRemapper() { + try { + TinyRemapper tinyRemapper = TinyRemapperHelper.getTinyRemapper(project, "intermediary", "named"); + tinyRemapper.readClassPath(TinyRemapperHelper.getMinecraftDependencies(project)); + tinyRemapper.readClassPath(extension.getMinecraftMappedProvider().getIntermediaryJar().toPath()); + + return tinyRemapper; + } catch (IOException e) { + throw new RuntimeException("Failed to create tiny remapper for intermediary->named", e); + } + } + + private byte[] hashInjectedInterfaces() { + // Hash the interfaces we're about to inject to not have to repeat this everytime + Hasher hasher = Hashing.sha256().newHasher(); + + for (Map.Entry> entry : injectedInterfaces.entrySet()) { + hasher.putString("class:", StandardCharsets.UTF_8); + hasher.putString(entry.getKey(), StandardCharsets.UTF_8); + + for (InjectedInterface ifaceName : entry.getValue()) { + hasher.putString("iface:", StandardCharsets.UTF_8); + hasher.putString(ifaceName.ifaceName(), StandardCharsets.UTF_8); + } + } + + return hasher.hash().asBytes(); + } +} 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 index 3c84fd96..3bd3b126 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java @@ -56,6 +56,7 @@ import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.DependencyProvider; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor; import net.fabricmc.loom.configuration.accesswidener.TransitiveAccessWidenerJarProcessor; +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider; import net.fabricmc.loom.configuration.providers.MinecraftProviderImpl; @@ -159,6 +160,14 @@ public class MappingsProviderImpl extends DependencyProvider implements Mappings } } + if (extension.getEnableInterfaceInjection().get()) { + InterfaceInjectionProcessor jarProcessor = new InterfaceInjectionProcessor(getProject()); + + if (!jarProcessor.isEmpty()) { + extension.getGameJarProcessors().add(jarProcessor); + } + } + extension.getAccessWidenerPath().finalizeValue(); extension.getGameJarProcessors().finalizeValue(); JarProcessorManager processorManager = new JarProcessorManager(extension.getGameJarProcessors().get()); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 78b1da5e..58dbd7e5 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -63,6 +63,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property setupRemappedVariants; protected final Property transitiveAccessWideners; protected final Property intermediary; + protected final Property enableInterfaceInjection; private final ModVersionParser versionParser; @@ -88,6 +89,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.transitiveAccessWideners.finalizeValueOnRead(); this.intermediary = project.getObjects().property(String.class) .convention("https://maven.fabricmc.net/net/fabricmc/intermediary/%1$s/intermediary-%1$s-v2.jar"); + this.enableInterfaceInjection = project.getObjects().property(Boolean.class) + .convention(true); + this.enableInterfaceInjection.finalizeValueOnRead(); this.versionParser = new ModVersionParser(project); @@ -181,6 +185,11 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return intermediary; } + @Override + public Property getEnableInterfaceInjection() { + return enableInterfaceInjection; + } + @Override public void disableDeprecatedPomGeneration(MavenPublication publication) { net.fabricmc.loom.configuration.MavenPublication.excludePublication(publication); diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/InterfaceInjectionTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/InterfaceInjectionTest.groovy new file mode 100644 index 00000000..8e731f69 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/InterfaceInjectionTest.groovy @@ -0,0 +1,51 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016-2021 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.integration + +import net.fabricmc.loom.test.util.GradleProjectTestTrait +import net.fabricmc.loom.util.ZipUtils +import spock.lang.Specification +import spock.lang.Unroll + +import static net.fabricmc.loom.test.LoomTestConstants.STANDARD_TEST_VERSIONS +import static org.gradle.testkit.runner.TaskOutcome.SUCCESS + +class InterfaceInjectionTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "interface injection (gradle #version)"() { + setup: + def gradle = gradleProject(project: "interfaceInjection", version: version) + ZipUtils.pack(new File(gradle.projectDir, "dummyDependency").toPath(), new File(gradle.projectDir, "dummy.jar").toPath()) + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } +} diff --git a/src/test/resources/projects/interfaceInjection/build.gradle b/src/test/resources/projects/interfaceInjection/build.gradle new file mode 100644 index 00000000..52f1fc39 --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/build.gradle @@ -0,0 +1,18 @@ +// This is used by a range of tests that append to this file before running the gradle tasks. +// Can be used for tests that require minimal custom setup +plugins { + id 'fabric-loom' + id 'maven-publish' +} + +archivesBaseName = "fabric-example-mod" +version = "1.0.0" +group = "com.example" + +dependencies { + minecraft "com.mojang:minecraft:1.17.1" + mappings "net.fabricmc:yarn:1.17.1+build.59:v2" + modImplementation "net.fabricmc:fabric-loader:0.11.6" + + modImplementation files("dummy.jar") +} \ No newline at end of file diff --git a/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json b/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json new file mode 100644 index 00000000..0d61ca3a --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/dummyDependency/fabric.mod.json @@ -0,0 +1,12 @@ +{ + "schemaVersion": 1, + "id": "dummy", + "version": "1", + "name": "Dummy Mod", + "custom": { + "fabric-api:module-lifecycle": "stable", + "loom:injected_interfaces": { + "net/minecraft/class_2248": ["InjectedInterface"] + } + } +} diff --git a/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java b/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java new file mode 100644 index 00000000..c73a730f --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/src/main/java/ExampleMod.java @@ -0,0 +1,10 @@ +import net.minecraft.block.Blocks; + +import net.fabricmc.api.ModInitializer; + +public class ExampleMod implements ModInitializer { + @Override + public void onInitialize() { + Blocks.AIR.newMethodThatDidNotExist(); + } +} diff --git a/src/test/resources/projects/interfaceInjection/src/main/java/InjectedInterface.java b/src/test/resources/projects/interfaceInjection/src/main/java/InjectedInterface.java new file mode 100644 index 00000000..f7e7a2e7 --- /dev/null +++ b/src/test/resources/projects/interfaceInjection/src/main/java/InjectedInterface.java @@ -0,0 +1,5 @@ + +public interface InjectedInterface { + default void newMethodThatDidNotExist() { + } +}