diff --git a/bootstrap/build.gradle b/bootstrap/build.gradle index 54c094da..efd4f1e4 100644 --- a/bootstrap/build.gradle +++ b/bootstrap/build.gradle @@ -3,12 +3,14 @@ plugins { id 'groovy' } -sourceCompatibility = 8 -targetCompatibility = 8 +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" - it.options.release = 8 } repositories { diff --git a/build.gradle b/build.gradle index f2341223..9e35f2d6 100644 --- a/build.gradle +++ b/build.gradle @@ -12,12 +12,14 @@ plugins { id "com.diffplug.spotless" version "6.3.0" } -sourceCompatibility = 17 -targetCompatibility = 17 +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} tasks.withType(JavaCompile).configureEach { it.options.encoding = "UTF-8" - it.options.release = 17 } tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index af2479f5..485c4e70 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -174,5 +174,11 @@ public interface LoomGradleExtensionAPI { getMinecraftJarConfiguration().set(MinecraftJarConfiguration.SPLIT); } + @ApiStatus.Experimental + void splitEnvironmentSourceSets(); + + @ApiStatus.Experimental + boolean areEnvironmentSourceSetsSplit(); + Property getRuntimeOnlyLog4j(); } 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 99ca5bac..f6315d37 100644 --- a/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java +++ b/src/main/java/net/fabricmc/loom/build/mixin/AnnotationProcessorInvoker.java @@ -41,6 +41,7 @@ import org.gradle.api.tasks.SourceSet; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.idea.IdeaUtils; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.task.service.MixinMappingsService; import net.fabricmc.loom.util.Constants; @@ -101,15 +102,16 @@ public abstract class AnnotationProcessorInvoker { public void configureMixin() { ConfigurationContainer configs = project.getConfigurations(); + MinecraftSourceSets minecraftSourceSets = MinecraftSourceSets.get(project); 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 processorConfig.extendsFrom( - configs.getByName(Constants.Configurations.MINECRAFT_NAMED), - configs.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED), - configs.getByName(Constants.Configurations.MAPPINGS_FINAL) + configs.getByName(minecraftSourceSets.getCombinedSourceSetName()), + configs.getByName(Constants.Configurations.MOD_COMPILE_CLASSPATH_MAPPED), + configs.getByName(Constants.Configurations.MAPPINGS_FINAL) ); // Add Mixin and mixin extensions (fabric-mixin-compile-extensions pulls mixin itself too) diff --git a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java index 5f265430..673b50c7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/CompileConfiguration.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.configuration; import java.nio.charset.StandardCharsets; +import java.util.List; import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.Project; @@ -48,6 +49,7 @@ import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.mapped.IntermediaryMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.mapped.NamedMinecraftProvider; import net.fabricmc.loom.extension.MixinExtension; @@ -62,7 +64,6 @@ public final class CompileConfiguration { 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_RUNTIME_DEPENDENCIES, configuration -> configuration.setTransitive(false)); extension.createLazyConfiguration(Constants.Configurations.MINECRAFT_DEPENDENCIES, configuration -> { @@ -114,13 +115,7 @@ public final class CompileConfiguration { } } - extendsFrom(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MINECRAFT_NAMED, project); - extendsFrom(Constants.Configurations.LOADER_DEPENDENCIES, Constants.Configurations.MINECRAFT_DEPENDENCIES, project); - extendsFrom(Constants.Configurations.MINECRAFT_NAMED, Constants.Configurations.LOADER_DEPENDENCIES, project); extendsFrom(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MAPPINGS_FINAL, project); extendsFrom(JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME, Constants.Configurations.MAPPINGS_FINAL, project); @@ -146,6 +141,8 @@ public final class CompileConfiguration { }); p.afterEvaluate(project -> { + MinecraftSourceSets.get(project).afterEvaluate(project); + try { setupMinecraft(project); } catch (Exception e) { @@ -284,7 +281,13 @@ public final class CompileConfiguration { .apply(project, extension.getNamedMinecraftProvider()).afterEvaluation(); } - private static void extendsFrom(String a, String b, Project project) { + public static void extendsFrom(List parents, String b, Project project) { + for (String parent : parents) { + extendsFrom(parent, b, project); + } + } + + public static void extendsFrom(String a, String b, Project project) { project.getConfigurations().getByName(a, configuration -> configuration.extendsFrom(project.getConfigurations().getByName(b))); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java new file mode 100644 index 00000000..ef9427ae --- /dev/null +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -0,0 +1,230 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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.configuration.CompileConfiguration.extendsFrom; + +import java.util.List; +import java.util.function.BiConsumer; + +import com.google.common.base.Preconditions; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPlugin; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.tasks.SourceSet; +import org.gradle.jvm.tasks.Jar; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.util.Constants; + +public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Single, MinecraftSourceSets.Split { + public static MinecraftSourceSets get(Project project) { + return LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit() ? Split.INSTANCE : Single.INSTANCE; + } + + public abstract void applyDependencies(BiConsumer consumer, List targets); + + public abstract String getCombinedSourceSetName(); + + public abstract String getSourceSetForEnv(String env); + + protected abstract List getAllSourceSetNames(); + + public void evaluateSplit(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + Preconditions.checkArgument(extension.areEnvironmentSourceSetsSplit()); + + Split.INSTANCE.evaluate(project); + } + + public abstract void afterEvaluate(Project project); + + protected void createSourceSets(Project project) { + final LoomGradleExtension extension = LoomGradleExtension.get(project); + + for (String name : getAllSourceSetNames()) { + extension.createLazyConfiguration(name, configuration -> configuration.setTransitive(false)); + + // All the configurations extend the loader deps. + extendsFrom(name, Constants.Configurations.LOADER_DEPENDENCIES, project); + } + } + + /** + * Used when we have a single source set, either with split or merged jars. + */ + public static final class Single extends MinecraftSourceSets { + private static final String MINECRAFT_NAMED = "minecraftNamed"; + + private static final Single INSTANCE = new Single(); + + @Override + public void applyDependencies(BiConsumer consumer, List targets) { + for (String target : targets) { + consumer.accept(MINECRAFT_NAMED, target); + } + } + + @Override + public String getCombinedSourceSetName() { + return MINECRAFT_NAMED; + } + + @Override + public String getSourceSetForEnv(String env) { + return SourceSet.MAIN_SOURCE_SET_NAME; + } + + @Override + protected List getAllSourceSetNames() { + return List.of(MINECRAFT_NAMED); + } + + @Override + public void afterEvaluate(Project project) { + // This is done in afterEvaluate as we need to be sure that split source sets was not enabled. + createSourceSets(project); + + // Default compile and runtime sourcesets. + extendsFrom(List.of( + JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.TEST_COMPILE_CLASSPATH_CONFIGURATION_NAME, + JavaPlugin.TEST_RUNTIME_CLASSPATH_CONFIGURATION_NAME), + MINECRAFT_NAMED, project + ); + } + } + + /** + * Used when we have a split client/common source set and split jars. + */ + public static final class Split extends MinecraftSourceSets { + private static final String MINECRAFT_COMMON_NAMED = "minecraftCommonNamed"; + private static final String MINECRAFT_CLIENT_ONLY_NAMED = "minecraftClientOnlyNamed"; + private static final String MINECRAFT_COMBINED_NAMED = "minecraftCombinedNamed"; + + private static final String CLIENT_ONLY_SOURCE_SET_NAME = "client"; + + private static final Split INSTANCE = new Split(); + + @Override + public void applyDependencies(BiConsumer consumer, List targets) { + Preconditions.checkArgument(targets.size() == 2); + Preconditions.checkArgument(targets.contains("common")); + Preconditions.checkArgument(targets.contains("clientOnly")); + + consumer.accept(MINECRAFT_COMMON_NAMED, "common"); + consumer.accept(MINECRAFT_CLIENT_ONLY_NAMED, "clientOnly"); + } + + @Override + public String getCombinedSourceSetName() { + return MINECRAFT_COMBINED_NAMED; + } + + @Override + public String getSourceSetForEnv(String env) { + return env.equals("client") ? CLIENT_ONLY_SOURCE_SET_NAME : SourceSet.MAIN_SOURCE_SET_NAME; + } + + @Override + protected List getAllSourceSetNames() { + return List.of(MINECRAFT_COMMON_NAMED, MINECRAFT_CLIENT_ONLY_NAMED, MINECRAFT_COMBINED_NAMED); + } + + // Called during evaluation, when the loom extension method is called. + private void evaluate(Project project) { + createSourceSets(project); + + // Combined extends from the 2 environments. + extendsFrom(MINECRAFT_COMBINED_NAMED, MINECRAFT_COMMON_NAMED, project); + extendsFrom(MINECRAFT_COMBINED_NAMED, MINECRAFT_CLIENT_ONLY_NAMED, project); + + final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + final LoomGradleExtension loomExtension = LoomGradleExtension.get(project); + + // Register our new client only source set, main becomes common only, with their respective jars. + SourceSet mainSourceSet = javaExtension.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME); + SourceSet clientOnlySourceSet = javaExtension.getSourceSets().create(CLIENT_ONLY_SOURCE_SET_NAME); + + extendsFrom(List.of( + mainSourceSet.getCompileClasspathConfigurationName(), + mainSourceSet.getRuntimeClasspathConfigurationName() + ), MINECRAFT_COMMON_NAMED, project + ); + + extendsFrom(List.of( + clientOnlySourceSet.getCompileClasspathConfigurationName(), + clientOnlySourceSet.getRuntimeClasspathConfigurationName() + ), MINECRAFT_CLIENT_ONLY_NAMED, project + ); + + // Client depends on common. + extendsFrom(MINECRAFT_CLIENT_ONLY_NAMED, MINECRAFT_COMMON_NAMED, project); + clientOnlySourceSet.setCompileClasspath( + clientOnlySourceSet.getCompileClasspath() + .plus(mainSourceSet.getCompileClasspath()) + .plus(mainSourceSet.getOutput()) + ); + clientOnlySourceSet.setRuntimeClasspath( + clientOnlySourceSet.getRuntimeClasspath() + .plus(mainSourceSet.getRuntimeClasspath()) + .plus(mainSourceSet.getOutput()) + ); + + loomExtension.mixin(mixinExtension -> { + // Generate a refmap for mixins in the new source set. + mixinExtension.add(clientOnlySourceSet, "client-" + mixinExtension.getDefaultRefmapName().get(), (p) -> { }); + }); + + // Include the client only output in the jars + project.getTasks().named(mainSourceSet.getJarTaskName(), Jar.class).configure(jar -> { + jar.from(clientOnlySourceSet.getOutput().getClassesDirs()); + jar.from(clientOnlySourceSet.getOutput().getResourcesDir()); + }); + + if (project.getTasks().findByName(mainSourceSet.getSourcesJarTaskName()) == null) { + // No sources. + return; + } + + project.getTasks().named(mainSourceSet.getSourcesJarTaskName(), Jar.class).configure(jar -> { + jar.from(clientOnlySourceSet.getAllSource()); + }); + } + + @Override + public void afterEvaluate(Project project) { + } + + public static SourceSet getClientSourceSet(Project project) { + Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit()); + + final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + return javaExtension.getSourceSets().getByName(CLIENT_ONLY_SOURCE_SET_NAME); + } + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java index 319b33e2..b63f4796 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/SplitMinecraftProvider.java @@ -77,7 +77,8 @@ public final class SplitMinecraftProvider extends MinecraftProvider { try (MinecraftJarSplitter jarSplitter = new MinecraftJarSplitter(clientJar, serverJar)) { // Required for loader to compute the version info also useful to have in both jars. jarSplitter.sharedEntry("version.json"); - jarSplitter.forcedClientEntry("assets/.mcassetsroot"); + jarSplitter.sharedEntry("assets/.mcassetsroot"); + jarSplitter.sharedEntry("assets/minecraft/lang/en_us.json"); jarSplitter.split(minecraftClientOnlyJar, minecraftCommonJar); } catch (Exception e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java index 68239f4d..4a1ea140 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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,9 +27,9 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; import org.gradle.api.Project; @@ -38,6 +38,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; import net.fabricmc.loom.configuration.providers.mappings.MappingsProviderImpl; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.SignatureFixerApplyVisitor; import net.fabricmc.loom.util.TinyRemapperHelper; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -58,8 +59,8 @@ public abstract class AbstractMappedMinecraftProvider getRemappedJars(); - protected void applyDependencies(BiConsumer consumer) { - // Override if needed + public List getDependencyTargets() { + return Collections.emptyList(); } public void provide(boolean applyDependencies) throws Exception { @@ -77,7 +78,16 @@ public abstract class AbstractMappedMinecraftProvider getProject().getDependencies().add(configuration, getDependencyNotation(name))); + final List dependencyTargets = getDependencyTargets(); + + if (dependencyTargets.isEmpty()) { + return; + } + + MinecraftSourceSets.get(getProject()).applyDependencies( + (configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)), + dependencyTargets + ); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java index ec838049..9c9cdff7 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/NamedMinecraftProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -26,7 +26,6 @@ package net.fabricmc.loom.configuration.providers.minecraft.mapped; import java.nio.file.Path; import java.util.List; -import java.util.function.BiConsumer; import org.gradle.api.Project; @@ -35,7 +34,6 @@ import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvid import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; -import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.tinyremapper.TinyRemapper; @@ -67,8 +65,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, MERGED); + public List getDependencyTargets() { + return List.of(MERGED); } } @@ -93,9 +91,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, COMMON); - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, CLIENT_ONLY); + public List getDependencyTargets() { + return List.of(CLIENT_ONLY, COMMON); } } @@ -123,8 +120,8 @@ public abstract class NamedMinecraftProvider extend } @Override - protected void applyDependencies(BiConsumer consumer) { - consumer.accept(Constants.Configurations.MINECRAFT_NAMED, envName()); + public List getDependencyTargets() { + return List.of(envName()); } @Override diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java index 90414803..542fdfa6 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/ProcessedNamedMinecraftProvider.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -36,6 +36,7 @@ import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.configuration.processors.JarProcessorManager; import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.configuration.providers.minecraft.SingleJarMinecraftProvider; import net.fabricmc.loom.configuration.providers.minecraft.SplitMinecraftProvider; @@ -85,7 +86,16 @@ public abstract class ProcessedNamedMinecraftProvider getProject().getDependencies().add(configuration, getDependencyNotation(name))); + final List dependencyTargets = parentMinecraftProvider.getDependencyTargets(); + + if (dependencyTargets.isEmpty()) { + return; + } + + MinecraftSourceSets.get(getProject()).applyDependencies( + (configuration, name) -> getProject().getDependencies().add(configuration, getDependencyNotation(name)), + dependencyTargets + ); } } diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index d6594e01..aa6365ec 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -50,6 +50,7 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpec; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuilderImpl; import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingsDependency; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.util.DeprecationHelper; /** @@ -69,6 +70,7 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA protected final Property intermediateMappingsProvider; private final Property runtimeOnlyLog4j; private final Property minecraftJarConfiguration; + private final Property splitEnvironmentalSourceSet; private final InterfaceInjectionExtensionAPI interfaceInjectionExtension; private final ModVersionParser versionParser; @@ -116,6 +118,9 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA this.interfaceInjectionExtension = project.getObjects().newInstance(InterfaceInjectionExtensionAPI.class); + this.splitEnvironmentalSourceSet = project.getObjects().property(Boolean.class).convention(false); + this.splitEnvironmentalSourceSet.finalizeValueOnRead(); + // Add main source set by default interfaceInjection(interfaceInjection -> { final JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class); @@ -254,6 +259,24 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA return runtimeOnlyLog4j; } + @Override + public void splitEnvironmentSourceSets() { + splitMinecraftJar(); + + splitEnvironmentalSourceSet.set(true); + + // We need to lock these values, as we setup the new source sets right away. + splitEnvironmentalSourceSet.finalizeValue(); + minecraftJarConfiguration.finalizeValue(); + + MinecraftSourceSets.get(getProject()).evaluateSplit(getProject()); + } + + @Override + public boolean areEnvironmentSourceSetsSplit() { + return splitEnvironmentalSourceSet.get(); + } + @Override public InterfaceInjectionExtensionAPI getInterfaceInjection() { return interfaceInjectionExtension; diff --git a/src/main/java/net/fabricmc/loom/task/LoomTasks.java b/src/main/java/net/fabricmc/loom/task/LoomTasks.java index 82a11568..78163b24 100644 --- a/src/main/java/net/fabricmc/loom/task/LoomTasks.java +++ b/src/main/java/net/fabricmc/loom/task/LoomTasks.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) 2016-2022 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 @@ -34,6 +34,7 @@ import org.gradle.api.tasks.TaskProvider; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.configuration.ide.RunConfigSettings; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.launch.GenerateDLIConfigTask; import net.fabricmc.loom.task.launch.GenerateLog4jConfigTask; import net.fabricmc.loom.task.launch.GenerateRemapClasspathTask; @@ -155,6 +156,17 @@ public final class LoomTasks { extension.getRunConfigs().removeIf(settings -> settings.getName().equals(taskName)); }); + + // Configure the run config source sets. + project.afterEvaluate(p -> { + if (!extension.areEnvironmentSourceSetsSplit()) { + return; + } + + extension.getRunConfigs().configureEach(settings -> + settings.source(MinecraftSourceSets.get(project).getSourceSetForEnv(settings.getEnvironment())) + ); + }); } public static Provider getIDELaunchConfigureTaskName(Project project) { diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index 56ec3d17..83af5533 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) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -26,16 +26,21 @@ package net.fabricmc.loom.task; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.Serializable; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.jar.Manifest; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Inject; @@ -47,6 +52,7 @@ import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; @@ -64,10 +70,13 @@ import net.fabricmc.loom.build.MixinRefmapHelper; import net.fabricmc.loom.build.nesting.IncludedJarFactory; import net.fabricmc.loom.build.nesting.JarNester; import net.fabricmc.loom.configuration.accesswidener.AccessWidenerFile; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.extension.MixinExtension; import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.task.service.TinyRemapperService; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.Pair; +import net.fabricmc.loom.util.SidedClassVisitor; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; import net.fabricmc.tinyremapper.OutputConsumerPath; @@ -133,6 +142,16 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { if (legacyMixin) { setupLegacyMixinRefmapRemapping(params); } + + if (extension.areEnvironmentSourceSetsSplit()) { + final List clientOnlyJarEntries = getClientOnlyJarEntries(); + params.getManifestAttributes().set(Map.of( + "Fabric-Loom-Split-Environment", "true", + "Fabric-Loom-Client-Only-Entries", String.join(";", clientOnlyJarEntries) + )); + + params.getClientOnlyClasses().set(clientOnlyJarEntries.stream().filter(s -> s.endsWith(".class")).toList()); + } }); } @@ -154,34 +173,14 @@ 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 List rootPaths = getRootPaths(sourceSet.getResources().getSrcDirs()); final String refmapName = container.refmapNameProvider().get(); final List mixinConfigs = container.sourceSet().getResources() .matching(container.mixinConfigPattern()) .getFiles() .stream() - .map(file -> { - String s = file.getAbsolutePath().replace("\\", "/"); - - for (String rootPath : rootPaths) { - if (s.startsWith(rootPath)) { - s = s.substring(rootPath.length()); - } - } - - return s; - }) + .map(relativePath(rootPaths)) .filter(allMixinConfigs::contains) .toList(); @@ -200,6 +199,9 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { Property getJarManifestService(); Property getTinyRemapperBuildServiceUuid(); + + MapProperty getManifestAttributes(); + ListProperty getClientOnlyClasses(); } public abstract static class RemapAction extends AbstractRemapAction { @@ -220,6 +222,11 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { tinyRemapper = tinyRemapperService.getTinyRemapperForRemapping(); remap(); + + if (getParameters().getClientOnlyClasses().isPresent()) { + markClientOnlyClasses(); + } + remapAccessWidener(); addRefmaps(); addNestedJars(); @@ -245,6 +252,15 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } + private void markClientOnlyClasses() throws IOException { + final Stream>> tranformers = getParameters().getClientOnlyClasses().get().stream() + .map(s -> new Pair<>(s, + (ZipUtils.AsmClassOperator) classVisitor -> SidedClassVisitor.CLIENT.insertApplyVisitor(null, classVisitor) + )); + + ZipUtils.transform(outputFile, tranformers); + } + private void remapAccessWidener() throws IOException { final AccessWidenerFile accessWidenerFile = AccessWidenerFile.fromModJar(inputFile); @@ -289,7 +305,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { var manifest = new Manifest(new ByteArrayInputStream(bytes)); - getParameters().getJarManifestService().get().apply(manifest); + getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); manifest.getMainAttributes().putValue("Fabric-Mapping-Namespace", getParameters().getTargetNamespace().get()); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -317,6 +333,50 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } + private List getClientOnlyJarEntries() { + final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); + + final ConfigurableFileCollection output = getProject().getObjects().fileCollection(); + output.from(clientSourceSet.getOutput().getClassesDirs()); + output.from(clientSourceSet.getOutput().getResourcesDir()); + + final List rootPaths = new ArrayList<>(); + + rootPaths.addAll(getRootPaths(clientSourceSet.getOutput().getClassesDirs().getFiles())); + rootPaths.addAll(getRootPaths(Set.of(Objects.requireNonNull(clientSourceSet.getOutput().getResourcesDir())))); + + return output.getAsFileTree().getFiles().stream() + .map(relativePath(rootPaths)) + .toList(); + } + + private static List getRootPaths(Set files) { + return files.stream() + .map(root -> { + String rootPath = root.getAbsolutePath().replace("\\", "/"); + + if (rootPath.charAt(rootPath.length() - 1) != '/') { + rootPath += '/'; + } + + return rootPath; + }).toList(); + } + + private static Function relativePath(List rootPaths) { + return file -> { + String s = file.getAbsolutePath().replace("\\", "/"); + + for (String rootPath : rootPaths) { + if (s.startsWith(rootPath)) { + s = s.substring(rootPath.length()); + } + } + + return s; + }; + } + @Internal public TinyRemapperService getTinyRemapperService() { return tinyRemapperService.get(); diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index 144f9fa9..38d2637f 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -40,6 +40,7 @@ import org.gradle.api.logging.configuration.ConsoleOutput; import org.gradle.api.tasks.TaskAction; import net.fabricmc.loom.configuration.providers.minecraft.MinecraftVersionMeta; +import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider; import net.fabricmc.loom.task.AbstractLoomTask; public abstract class GenerateDLIConfigTask extends AbstractLoomTask { @@ -68,6 +69,11 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .argument("client", "--assetsDir") .argument("client", assetsDirectory.getAbsolutePath()); + if (getExtension().areEnvironmentSourceSetsSplit()) { + launchConfig.property("client", "fabric.gameJarPath.client", getGameJarPath("client")); + launchConfig.property("fabric.gameJarPath", getGameJarPath("common")); + } + final boolean plainConsole = getProject().getGradle().getStartParameter().getConsoleOutput() == ConsoleOutput.Plain; final boolean ansiSupportedIDE = new File(getProject().getRootDir(), ".vscode").exists() || new File(getProject().getRootDir(), ".idea").exists() @@ -87,6 +93,16 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .collect(Collectors.joining(",")); } + private String getGameJarPath(String env) { + MappedMinecraftProvider.Split split = (MappedMinecraftProvider.Split) getExtension().getNamedMinecraftProvider(); + + return switch (env) { + case "client" -> split.getClientOnlyJar().toAbsolutePath().toString(); + case "common" -> split.getCommonJar().toAbsolutePath().toString(); + default -> throw new UnsupportedOperationException(); + }; + } + public static class LaunchConfig { private final Map> values = new HashMap<>(); diff --git a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java index 4de6cba1..117207fa 100644 --- a/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java +++ b/src/main/java/net/fabricmc/loom/task/service/JarManifestService.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -25,6 +25,7 @@ package net.fabricmc.loom.task.service; import java.io.Serializable; +import java.util.Map; import java.util.Optional; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -70,7 +71,7 @@ public abstract class JarManifestService implements BuildService extraValues) { // Don't set when running the reproducible build tests as it will break them when anything updates if (Boolean.getBoolean("loom.test.reproducible")) { return; @@ -91,6 +92,10 @@ public abstract class JarManifestService implements BuildService entry : extraValues.entrySet()) { + attributes.putValue(entry.getKey(), entry.getValue()); + } } private static Optional getLoaderVersion(Project project) { diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 8cb8bc78..cd90b25d 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -70,7 +70,6 @@ public class Constants { public static final String MINECRAFT_DEPENDENCIES = "minecraftLibraries"; public static final String MINECRAFT_RUNTIME_DEPENDENCIES = "minecraftRuntimeOnlyLibraries"; 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"; public static final String LOADER_DEPENDENCIES = "loaderLibraries"; diff --git a/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java index 51fe9c51..5d2f7c6a 100644 --- a/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java +++ b/src/main/java/net/fabricmc/loom/util/SidedClassVisitor.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -42,6 +42,7 @@ public final class SidedClassVisitor extends ClassVisitor { private static final String SIDE_DESCRIPTOR = "Lnet/fabricmc/api/EnvType;"; private final String side; + private boolean hasExisting = false; private SidedClassVisitor(String side, ClassVisitor next) { super(Constants.ASM_VERSION, next); @@ -49,11 +50,22 @@ public final class SidedClassVisitor extends ClassVisitor { } @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - super.visit(version, access, name, signature, superName, interfaces); + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + if (ENVIRONMENT_DESCRIPTOR.equals(descriptor)) { + hasExisting = true; + } - final AnnotationVisitor annotationVisitor = visitAnnotation(ENVIRONMENT_DESCRIPTOR, true); - annotationVisitor.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT)); - annotationVisitor.visitEnd(); + return super.visitAnnotation(descriptor, visible); + } + + @Override + public void visitEnd() { + if (!hasExisting) { + final AnnotationVisitor annotationVisitor = visitAnnotation(ENVIRONMENT_DESCRIPTOR, true); + annotationVisitor.visitEnum("value", SIDE_DESCRIPTOR, side.toUpperCase(Locale.ROOT)); + annotationVisitor.visitEnd(); + } + + super.visitEnd(); } } diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java index c4a35a3a..6c4518fe 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -44,6 +44,9 @@ import java.util.function.Function; import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; import net.fabricmc.loom.LoomGradlePlugin; @@ -231,6 +234,20 @@ public class ZipUtils { T apply(T arg) throws IOException; } + public interface AsmClassOperator extends UnsafeUnaryOperator { + ClassVisitor visit(ClassVisitor classVisitor); + + @Override + default byte[] apply(byte[] arg) throws IOException { + final ClassReader reader = new ClassReader(arg); + final ClassWriter writer = new ClassWriter(0); + + reader.accept(visit(writer), 0); + + return writer.toByteArray(); + } + } + private static Map> collectTransformersStream(Stream>> transforms) { Map> map = new HashMap<>(); Iterator>> iterator = transforms.iterator(); diff --git a/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy b/src/test/groovy/net/fabricmc/loom/test/LoomTestConstants.groovy index 6f852317..91bc751c 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.5-20220129032445+0000" + public final static String PRE_RELEASE_GRADLE = "7.5-20220228014109+0000" public final static String[] STANDARD_TEST_VERSIONS = [DEFAULT_GRADLE, PRE_RELEASE_GRADLE] } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy index b349e25f..d58d0741 100644 --- a/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/integration/MCJarConfigTest.groovy @@ -1,7 +1,7 @@ /* * This file is part of fabric-loom, licensed under the MIT License (MIT). * - * Copyright (c) 2021 FabricMC + * Copyright (c) 2021-2022 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 @@ -112,4 +112,31 @@ class MCJarConfigTest extends Specification implements GradleProjectTestTrait { where: version << STANDARD_TEST_VERSIONS } + + @Unroll + def "split env (gradle #version)"() { + setup: + def gradle = gradleProject(project: "minimalBase", version: version) + + gradle.buildGradle << ''' + loom { + splitEnvironmentSourceSets() + } + + dependencies { + minecraft "com.mojang:minecraft:1.18.1" + mappings "net.fabricmc:yarn:1.18.1+build.18:v2" + modImplementation "net.fabricmc:fabric-loader:0.12.12" + } + ''' + + when: + def result = gradle.run(task: "build") + + then: + result.task(":build").outcome == SUCCESS + + where: + version << STANDARD_TEST_VERSIONS + } } diff --git a/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy b/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy new file mode 100644 index 00000000..b7440833 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/integration/SplitProjectTest.groovy @@ -0,0 +1,49 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2022 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 + +class SplitProjectTest extends Specification implements GradleProjectTestTrait { + @Unroll + def "build (gradle #version)"() { + setup: + def gradle = gradleProject(project: "splitSources", version: version) + + 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/splitSources/build.gradle b/src/test/resources/projects/splitSources/build.gradle new file mode 100644 index 00000000..ea40b502 --- /dev/null +++ b/src/test/resources/projects/splitSources/build.gradle @@ -0,0 +1,27 @@ +plugins { + id 'fabric-loom' version '0.12.local' + id 'maven-publish' +} + +sourceCompatibility = JavaVersion.VERSION_17 +targetCompatibility = JavaVersion.VERSION_17 + +loom { + splitEnvironmentSourceSets() +} + +dependencies { + minecraft "com.mojang:minecraft:1.18.2" + mappings "net.fabricmc:yarn:1.18.2+build.1:v2" + modImplementation "net.fabricmc:fabric-loader:0.13.3" + + modImplementation "net.fabricmc.fabric-api:fabric-api:0.47.8+1.18.2" +} + +tasks.withType(JavaCompile).configureEach { + it.options.release = 17 +} + +java { + withSourcesJar() +} \ No newline at end of file diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java new file mode 100644 index 00000000..9047ed62 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/ExampleModClient.java @@ -0,0 +1,19 @@ +package net.fabricmc.example.client; + +import net.fabricmc.api.ClientModInitializer; + +import net.minecraft.client.MinecraftClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExampleModClient implements ClientModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger(ExampleModClient.class); + + @Override + public void onInitializeClient() { + LOGGER.info("Hello Client"); + + // Check we can compile against the client. + MinecraftClient client; + } +} diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java new file mode 100644 index 00000000..46dbdecc --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/TestClientClass.java @@ -0,0 +1,8 @@ +package net.fabricmc.example.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class TestClientClass { +} diff --git a/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java new file mode 100644 index 00000000..afd4a3bc --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/java/net/fabricmc/example/client/mixin/ExampleMixin.java @@ -0,0 +1,16 @@ +package net.fabricmc.example.client.mixin; + +import net.fabricmc.example.ExampleMod; +import net.minecraft.client.gui.screen.TitleScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(TitleScreen.class) +public class ExampleMixin { + @Inject(at = @At("HEAD"), method = "init()V") + private void init(CallbackInfo info) { + ExampleMod.LOGGER.info("This line is printed by an example mod mixin!"); + } +} diff --git a/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json b/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json new file mode 100644 index 00000000..7c42cb48 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/client/resources/modid.client.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.fabricmc.example.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + ], + "client": [ + "ExampleMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java b/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java new file mode 100644 index 00000000..72df3bf7 --- /dev/null +++ b/src/test/resources/projects/splitSources/src/main/java/net/fabricmc/example/ExampleMod.java @@ -0,0 +1,19 @@ +package net.fabricmc.example; + +import net.fabricmc.api.ModInitializer; + +import net.minecraft.block.Block; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExampleMod implements ModInitializer { + public static final Logger LOGGER = LoggerFactory.getLogger("modid"); + + @Override + public void onInitialize() { + LOGGER.info("Hello Fabric world!"); + + // Check we can compile against common code. + Block block; + } +} diff --git a/src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png b/src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png new file mode 100644 index 00000000..047b91f2 Binary files /dev/null and b/src/test/resources/projects/splitSources/src/main/resources/assets/modid/icon.png differ diff --git a/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json b/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..f68569fa --- /dev/null +++ b/src/test/resources/projects/splitSources/src/main/resources/fabric.mod.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 1, + "id": "modid", + "version": "${version}", + + "name": "Example Mod", + "description": "This is an example description! Tell everyone what your mod is about!", + "icon": "assets/modid/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "net.fabricmc.example.ExampleMod" + ], + "client": [ + "net.fabricmc.example.client.ExampleModClient" + ] + }, + "mixins": [ + "modid.client.mixins.json" + ] +}