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 index 9bdf8b2e..576a243e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/MinecraftSourceSets.java @@ -226,7 +226,7 @@ public abstract sealed class MinecraftSourceSets permits MinecraftSourceSets.Sin } public static SourceSet getClientSourceSet(Project project) { - Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit()); + Preconditions.checkArgument(LoomGradleExtension.get(project).areEnvironmentSourceSetsSplit(), "Cannot get client only sourceset as project is not split"); 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/task/AbstractRemapJarTask.java b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java index e23460fd..dbc8edb4 100644 --- a/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/AbstractRemapJarTask.java @@ -24,18 +24,30 @@ package net.fabricmc.loom.task; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.jar.Manifest; import javax.inject.Inject; +import com.google.common.base.Preconditions; import org.gradle.api.Action; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.RegularFileProperty; +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.InputFile; import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; import org.gradle.jvm.tasks.Jar; import org.gradle.workers.WorkAction; import org.gradle.workers.WorkParameters; @@ -43,9 +55,14 @@ import org.gradle.workers.WorkQueue; import org.gradle.workers.WorkerExecutor; import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.task.service.JarManifestService; import net.fabricmc.loom.util.ZipReprocessorUtil; +import net.fabricmc.loom.util.ZipUtils; public abstract class AbstractRemapJarTask extends Jar { + public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; + public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace"; + @InputFile public abstract RegularFileProperty getInputFile(); @@ -67,11 +84,15 @@ public abstract class AbstractRemapJarTask extends Jar { @Inject protected abstract WorkerExecutor getWorkerExecutor(); + @Input + public abstract Property getIncludesClientOnlyClasses(); + @Inject public AbstractRemapJarTask() { getSourceNamespace().convention(MappingsNamespace.NAMED.toString()).finalizeValueOnRead(); getTargetNamespace().convention(MappingsNamespace.INTERMEDIARY.toString()).finalizeValueOnRead(); getRemapperIsolation().convention(false).finalizeValueOnRead(); + getIncludesClientOnlyClasses().convention(false).finalizeValueOnRead(); } public final

void submitWork(Class> workAction, Action

action) { @@ -87,10 +108,21 @@ public abstract class AbstractRemapJarTask extends Jar { params.getArchivePreserveFileTimestamps().set(isPreserveFileTimestamps()); params.getArchiveReproducibleFileOrder().set(isReproducibleFileOrder()); + params.getJarManifestService().set(JarManifestService.get(getProject())); + + if (getIncludesClientOnlyClasses().get()) { + final List clientOnlyEntries = getClientOnlyEntries(); + applyClientOnlyManifestAttributes(params, clientOnlyEntries); + params.getClientOnlyEntries().set(clientOnlyEntries.stream().filter(s -> s.endsWith(".class")).toList()); + } + action.execute(params); }); } + @Internal + protected abstract List getClientOnlyEntries(); + public interface AbstractRemapParams extends WorkParameters { RegularFileProperty getInputFile(); RegularFileProperty getOutputFile(); @@ -100,6 +132,18 @@ public abstract class AbstractRemapJarTask extends Jar { Property getArchivePreserveFileTimestamps(); Property getArchiveReproducibleFileOrder(); + + Property getJarManifestService(); + MapProperty getManifestAttributes(); + + ListProperty getClientOnlyEntries(); + } + + protected void applyClientOnlyManifestAttributes(AbstractRemapParams params, List entries) { + params.getManifestAttributes().set(Map.of( + "Fabric-Loom-Split-Environment", "true", + "Fabric-Loom-Client-Only-Entries", String.join(";", entries) + )); } public abstract static class AbstractRemapAction implements WorkAction { @@ -112,6 +156,21 @@ public abstract class AbstractRemapJarTask extends Jar { outputFile = getParameters().getOutputFile().getAsFile().get().toPath(); } + protected void modifyJarManifest() throws IOException { + int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { + var manifest = new Manifest(new ByteArrayInputStream(bytes)); + + getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); + manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + manifest.write(out); + return out.toByteArray(); + })); + + Preconditions.checkState(count > 0, "Did not transform any jar manifest"); + } + protected void rewriteJar() throws IOException { final boolean isReproducibleFileOrder = getParameters().getArchiveReproducibleFileOrder().get(); final boolean isPreserveFileTimestamps = getParameters().getArchivePreserveFileTimestamps().get(); @@ -127,4 +186,31 @@ public abstract class AbstractRemapJarTask extends Jar { public RegularFileProperty getInput() { return getInputFile(); } + + protected 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(); + } + + protected 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; + }; + } } diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index d6bd54a7..5fc7a01f 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -24,27 +24,20 @@ 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; -import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.gson.JsonObject; import org.gradle.api.artifacts.Configuration; @@ -52,7 +45,6 @@ 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; @@ -72,7 +64,6 @@ 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.ExceptionUtil; @@ -85,18 +76,12 @@ import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; public abstract class RemapJarTask extends AbstractRemapJarTask { - public static final String MANIFEST_PATH = "META-INF/MANIFEST.MF"; - public static final String MANIFEST_NAMESPACE_KEY = "Fabric-Mapping-Namespace"; - @InputFiles public abstract ConfigurableFileCollection getNestedJars(); @Input public abstract Property getAddNestedDependencies(); - @Input - public abstract Property getIncludesClientOnlyClasses(); - private Supplier tinyRemapperService = Suppliers.memoize(() -> TinyRemapperService.getOrCreate(this)); @Inject @@ -105,7 +90,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { getClasspath().from(getProject().getConfigurations().getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)); getAddNestedDependencies().convention(true).finalizeValueOnRead(); - getIncludesClientOnlyClasses().convention(false).finalizeValueOnRead(); Configuration includeConfiguration = getProject().getConfigurations().getByName(Constants.Configurations.INCLUDE); getNestedJars().from(new IncludedJarFactory(getProject()).getNestedJars(includeConfiguration)); @@ -138,7 +122,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { params.getNestedJars().from(getNestedJars()); } - params.getJarManifestService().set(JarManifestService.get(getProject())); params.getTinyRemapperBuildServiceUuid().set(UnsafeWorkQueueHelper.create(getProject(), tinyRemapperService.get())); params.getRemapClasspath().from(getClasspath()); @@ -148,20 +131,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { if (legacyMixin) { setupLegacyMixinRefmapRemapping(params); } - - if (getIncludesClientOnlyClasses().get()) { - if (!extension.areEnvironmentSourceSetsSplit()) { - throw new UnsupportedOperationException("Jar cannot include client only classes as the sources are not split"); - } - - 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()); - } }); } @@ -207,11 +176,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { record RefmapData(List mixinConfigs, String refmapName) implements Serializable { } ListProperty getMixinData(); - Property getJarManifestService(); Property getTinyRemapperBuildServiceUuid(); - - MapProperty getManifestAttributes(); - ListProperty getClientOnlyClasses(); } public abstract static class RemapAction extends AbstractRemapAction { @@ -233,7 +198,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { remap(); - if (getParameters().getClientOnlyClasses().isPresent()) { + if (getParameters().getClientOnlyEntries().isPresent()) { markClientOnlyClasses(); } @@ -263,7 +228,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } private void markClientOnlyClasses() throws IOException { - final Stream>> tranformers = getParameters().getClientOnlyClasses().get().stream() + final Stream>> tranformers = getParameters().getClientOnlyEntries().get().stream() .map(s -> new Pair<>(s, (ZipUtils.AsmClassOperator) classVisitor -> SidedClassVisitor.CLIENT.insertApplyVisitor(null, classVisitor) )); @@ -311,21 +276,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), LOGGER); } - private void modifyJarManifest() throws IOException { - int count = ZipUtils.transform(outputFile, Map.of(MANIFEST_PATH, bytes -> { - var manifest = new Manifest(new ByteArrayInputStream(bytes)); - - getParameters().getJarManifestService().get().apply(manifest, getParameters().getManifestAttributes().get()); - manifest.getMainAttributes().putValue(MANIFEST_NAMESPACE_KEY, getParameters().getTargetNamespace().get()); - - 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; @@ -343,7 +293,8 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { } } - private List getClientOnlyJarEntries() { + @Override + protected List getClientOnlyEntries() { final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); final ConfigurableFileCollection output = getProject().getObjects().fileCollection(); @@ -360,33 +311,6 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { .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/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 099e4ae6..c65b1c54 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -26,15 +26,18 @@ package net.fabricmc.loom.task; import java.io.IOException; import java.nio.file.Files; +import java.util.List; import javax.inject.Inject; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.fabricmc.loom.configuration.providers.minecraft.MinecraftSourceSets; import net.fabricmc.loom.task.service.SourceRemapperService; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; @@ -53,6 +56,15 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { }); } + @Override + protected List getClientOnlyEntries() { + final SourceSet clientSourceSet = MinecraftSourceSets.Split.getClientSourceSet(getProject()); + + return clientSourceSet.getAllSource().getFiles().stream() + .map(relativePath(getRootPaths(clientSourceSet.getAllSource().getSrcDirs()))) + .toList(); + } + public interface RemapSourcesParams extends AbstractRemapParams { Property getSourcesRemapperServiceUuid(); } @@ -73,6 +85,7 @@ public abstract class RemapSourcesJarTask extends AbstractRemapJarTask { try { sourceRemapperService.remapSourcesJar(inputFile, outputFile); + modifyJarManifest(); rewriteJar(); } catch (Exception e) { try { diff --git a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java index 4d54eefb..9ee9303f 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java +++ b/src/main/java/net/fabricmc/loom/task/RemapTaskConfiguration.java @@ -128,6 +128,7 @@ public class RemapTaskConfiguration { task.dependsOn(sourcesJarTask); task.getInputFile().convention(sourcesJarTask.getArchiveFile()); + task.getIncludesClientOnlyClasses().set(project.provider(extension::areEnvironmentSourceSetsSplit)); }); tasks.named(BasePlugin.ASSEMBLE_TASK_NAME).configure(task -> task.dependsOn(remapSourcesTask)); diff --git a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy index f88e68cc..c3dd4727 100644 --- a/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/util/GradleProjectTestTrait.groovy @@ -50,7 +50,7 @@ trait GradleProjectTestTrait { def homeDirOverride = System.getProperty("fabric.loom.test.homeDir") if (homeDirOverride) { - gradleHomeDir = new File(homeDirOverride); + gradleHomeDir = new File(homeDirOverride) } setupProject(options, projectDir)