mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Merge remote-tracking branch 'FabricMC/dev/0.13' into dev/0.13.0
Signed-off-by: shedaniel <daniel@shedaniel.me> # Conflicts: # src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingsProviderImpl.java # src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java
This commit is contained in:
@@ -143,6 +143,10 @@ dependencies {
|
||||
|
||||
compileOnly 'org.jetbrains:annotations:23.0.0'
|
||||
testCompileOnly 'org.jetbrains:annotations:23.0.0'
|
||||
|
||||
testCompileOnly ('net.fabricmc:sponge-mixin:0.11.4+mixin.0.8.5') {
|
||||
transitive = false
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
package net.fabricmc.loom.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -49,6 +50,7 @@ import net.fabricmc.loom.configuration.launch.LaunchProviderSettings;
|
||||
import net.fabricmc.loom.configuration.processors.JarProcessor;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.NoOpIntermediateMappingsProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftJarConfiguration;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.DeprecationHelper;
|
||||
import net.fabricmc.loom.util.ModPlatform;
|
||||
|
||||
@@ -167,6 +169,17 @@ public interface LoomGradleExtensionAPI {
|
||||
setIntermediateMappingsProvider(NoOpIntermediateMappingsProvider.class, p -> { });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tiny mappings file used to remap the game and mods.
|
||||
*/
|
||||
File getMappingsFile();
|
||||
|
||||
/**
|
||||
* Returns the {@link GenerateSourcesTask} for the given {@link DecompilerOptions}.
|
||||
* When env source sets are split and the client param is true the decompile task for the client jar will be returned.
|
||||
*/
|
||||
GenerateSourcesTask getDecompileTask(DecompilerOptions options, boolean client);
|
||||
|
||||
/**
|
||||
* Use "%1$s" as a placeholder for the minecraft version.
|
||||
*
|
||||
|
||||
@@ -67,6 +67,10 @@ public abstract class DecompilerOptions implements Named {
|
||||
getMaxThreads().convention(Runtime.getRuntime().availableProcessors()).finalizeValueOnRead();
|
||||
}
|
||||
|
||||
public String getFormattedName() {
|
||||
return getName().substring(0, 1).toUpperCase() + getName().substring(1);
|
||||
}
|
||||
|
||||
// Done to work around weird issues with the workers, possibly https://github.com/gradle/gradle/issues/13422
|
||||
public record Dto(String className, Map<String, String> options, int maxThreads) implements Serializable { }
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface FileMappingsSpecBuilder {
|
||||
* <p>The default mapping path is {@code mappings/mappings.tiny}, matching regular mapping dependency jars
|
||||
* such as Yarn's.
|
||||
*
|
||||
* @param mappingPath the mapping path, or null if a bare file
|
||||
* @param mappingPath the mapping path
|
||||
* @return this builder
|
||||
*/
|
||||
FileMappingsSpecBuilder mappingPath(String mappingPath);
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings;
|
||||
@@ -161,7 +162,8 @@ public final class RemapConfigurations {
|
||||
return str.substring(0, 1).toUpperCase(Locale.ROOT) + str.substring(1);
|
||||
}
|
||||
|
||||
private record ConfigurationOption(Function<SourceSet, String> targetNameFunc, boolean compileClasspath, boolean runtimeClasspath, RemapConfigurationSettings.PublishingMode publishingMode) {
|
||||
@VisibleForTesting
|
||||
public record ConfigurationOption(Function<SourceSet, String> targetNameFunc, boolean compileClasspath, boolean runtimeClasspath, RemapConfigurationSettings.PublishingMode publishingMode) {
|
||||
String targetName(SourceSet sourceSet) {
|
||||
return targetNameFunc.apply(sourceSet);
|
||||
}
|
||||
@@ -170,27 +172,25 @@ public final class RemapConfigurations {
|
||||
return targetName(sourceSet) != null;
|
||||
}
|
||||
|
||||
String name(SourceSet sourceSet) {
|
||||
public String name(SourceSet sourceSet) {
|
||||
String targetName = targetName(sourceSet);
|
||||
|
||||
if (targetName == null) {
|
||||
throw new UnsupportedOperationException("Configuration option is not available for sourceset (%s)".formatted(sourceSet.getName()));
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (!SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
||||
builder.append(sourceSet.getName());
|
||||
if (targetName.startsWith(sourceSet.getName())) {
|
||||
targetName = targetName.substring(sourceSet.getName().length());
|
||||
}
|
||||
|
||||
if (builder.isEmpty()) {
|
||||
builder.append("mod");
|
||||
} else {
|
||||
builder.append("Mod");
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("mod");
|
||||
|
||||
if (!SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
||||
builder.append(capitalise(sourceSet.getName()));
|
||||
}
|
||||
|
||||
builder.append(capitalise(targetName));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class SingleJarDecompileConfiguration extends DecompileConfiguration<Mapp
|
||||
final File inputJar = mappedJar;
|
||||
|
||||
LoomGradleExtension.get(project).getDecompilerOptions().forEach(options -> {
|
||||
final String decompilerName = options.getName().substring(0, 1).toUpperCase() + options.getName().substring(1);
|
||||
final String decompilerName = options.getFormattedName();
|
||||
String taskName = "genSourcesWith" + decompilerName;
|
||||
// Decompiler will be passed to the constructor of GenerateSourcesTask
|
||||
project.getTasks().register(taskName, GenerateSourcesTask.class, options).configure(task -> {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
import net.fabricmc.loom.api.decompilers.DecompilerOptions;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.mapped.MappedMinecraftProvider;
|
||||
import net.fabricmc.loom.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.task.UnpickJarTask;
|
||||
@@ -84,6 +85,18 @@ public final class SplitDecompileConfiguration extends DecompileConfiguration<Ma
|
||||
task.mustRunAfter(commonDecompileTask);
|
||||
});
|
||||
|
||||
for (DecompilerOptions options : extension.getDecompilerOptions()) {
|
||||
final String decompilerName = options.getFormattedName();
|
||||
|
||||
project.getTasks().register("genSourcesWith" + decompilerName, task -> {
|
||||
task.setDescription("Decompile minecraft using %s.".formatted(decompilerName));
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
|
||||
task.dependsOn(project.getTasks().named("gen%sSourcesWith%s".formatted("Common", decompilerName)));
|
||||
task.dependsOn(project.getTasks().named("gen%sSourcesWith%s".formatted("ClientOnly", decompilerName)));
|
||||
});
|
||||
}
|
||||
|
||||
project.getTasks().register("genSources", task -> {
|
||||
task.setDescription("Decompile minecraft using the default decompiler.");
|
||||
task.setGroup(Constants.TaskGroup.FABRIC);
|
||||
@@ -95,7 +108,7 @@ public final class SplitDecompileConfiguration extends DecompileConfiguration<Ma
|
||||
|
||||
private TaskProvider<Task> createDecompileTasks(String name, Action<GenerateSourcesTask> configureAction) {
|
||||
extension.getDecompilerOptions().forEach(options -> {
|
||||
final String decompilerName = options.getName().substring(0, 1).toUpperCase() + options.getName().substring(1);
|
||||
final String decompilerName = options.getFormattedName();
|
||||
final String taskName = "gen%sSourcesWith%s".formatted(name, decompilerName);
|
||||
|
||||
project.getTasks().register(taskName, GenerateSourcesTask.class, options).configure(task -> {
|
||||
|
||||
@@ -43,7 +43,6 @@ import java.util.stream.Collectors;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.tools.ant.util.StringUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.ModuleVersionIdentifier;
|
||||
import org.gradle.api.artifacts.ResolvedArtifact;
|
||||
|
||||
@@ -47,6 +47,8 @@ import net.fabricmc.loom.task.AbstractRemapJarTask;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public class JarSplitter {
|
||||
public static final String MANIFEST_SPLIT_ENV_NAME_KEY = "Fabric-Loom-Split-Environment-Name";
|
||||
|
||||
final Path inputJar;
|
||||
|
||||
public JarSplitter(Path inputJar) {
|
||||
@@ -155,30 +157,7 @@ public class JarSplitter {
|
||||
|
||||
final String entryPath = relativePath.toString();
|
||||
|
||||
/*
|
||||
Copy the manifest to both jars
|
||||
- Remove signature data
|
||||
- Remove split data as its already been split.
|
||||
*/
|
||||
if (entryPath.equals(AbstractRemapJarTask.MANIFEST_PATH)) {
|
||||
final Manifest outManifest = new Manifest(manifest);
|
||||
final Attributes attributes = outManifest.getMainAttributes();
|
||||
stripSignatureData(outManifest);
|
||||
|
||||
attributes.remove(Attributes.Name.SIGNATURE_VERSION);
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_NAME));
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_NAME));
|
||||
|
||||
// TODO add an attribute to denote if the jar is common or client now
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
outManifest.write(out);
|
||||
final byte[] manifestBytes = out.toByteArray();
|
||||
|
||||
writeBytes(manifestBytes, commonOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
writeBytes(manifestBytes, clientOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -192,12 +171,39 @@ public class JarSplitter {
|
||||
|
||||
Files.copy(entry, outputEntry, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
}
|
||||
|
||||
/*
|
||||
Write the manifest to both jars
|
||||
- Remove signature data
|
||||
- Remove split data as its already been split.
|
||||
- Add env name.
|
||||
*/
|
||||
final Manifest outManifest = new Manifest(manifest);
|
||||
final Attributes attributes = outManifest.getMainAttributes();
|
||||
stripSignatureData(outManifest);
|
||||
|
||||
attributes.remove(Attributes.Name.SIGNATURE_VERSION);
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_SPLIT_ENV_NAME));
|
||||
Objects.requireNonNull(attributes.remove(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_NAME));
|
||||
|
||||
writeBytes(writeWithEnvironment(outManifest, "common"), commonOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
writeBytes(writeWithEnvironment(outManifest, "client"), clientOutput.getPath(AbstractRemapJarTask.MANIFEST_PATH));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private byte[] writeWithEnvironment(Manifest in, String value) throws IOException {
|
||||
final Manifest manifest = new Manifest(in);
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
attributes.putValue(MANIFEST_SPLIT_ENV_NAME_KEY, value);
|
||||
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private List<String> readClientEntries(Manifest manifest) {
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
final String clientEntriesValue = attributes.getValue(AbstractRemapJarTask.MANIFEST_CLIENT_ENTRIES_KEY);
|
||||
|
||||
@@ -103,6 +103,7 @@ public class ModProcessor {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip out all contained jar info as we dont want loader to try and load the jars contained in dev.
|
||||
try {
|
||||
ZipUtils.transformJson(JsonObject.class, path, Map.of("fabric.mod.json", json -> {
|
||||
|
||||
@@ -25,16 +25,20 @@
|
||||
package net.fabricmc.loom.configuration.providers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
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 java.util.Optional;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.AttributeHelper;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public record BundleMetadata(List<Entry> libraries, List<Entry> versions, String mainClass) {
|
||||
@@ -83,10 +87,38 @@ public record BundleMetadata(List<Entry> libraries, List<Entry> versions, String
|
||||
}
|
||||
|
||||
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);
|
||||
public void unpackEntry(Path jar, Path dest, Project project) throws IOException {
|
||||
final LoomGradleExtension extension = LoomGradleExtension.get(project);
|
||||
|
||||
if (!extension.refreshDeps() && Files.exists(dest)) {
|
||||
final String hash = readHash(dest).orElse("");
|
||||
|
||||
if (hash.equals(sha1)) {
|
||||
// File exists with expected hash
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(jar)) {
|
||||
Files.copy(fs.get().getPath(path()), dest, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
writeHash(dest, sha1);
|
||||
}
|
||||
|
||||
private Optional<String> readHash(Path output) {
|
||||
try {
|
||||
return AttributeHelper.readAttribute(output, "LoomHash");
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHash(Path output, String eTag) {
|
||||
try {
|
||||
AttributeHelper.writeAttribute(output, "LoomHash", eTag);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to write hash to (%s)".formatted(output), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ import net.fabricmc.loom.configuration.providers.forge.FieldMigratedMappingsProv
|
||||
import net.fabricmc.loom.configuration.providers.forge.SrgProvider;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.tiny.MappingsMerger;
|
||||
import net.fabricmc.loom.configuration.providers.mappings.tiny.TinyJarInfo;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MergedMinecraftProvider;
|
||||
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftProvider;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
import net.fabricmc.loom.util.DeletingFileVisitor;
|
||||
@@ -314,14 +313,16 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService {
|
||||
// These are unmerged v2 mappings
|
||||
MappingsMerger.mergeAndSaveMappings(baseTinyMappings, tinyMappings, intermediaryService.get());
|
||||
} else {
|
||||
if (minecraftProvider instanceof MergedMinecraftProvider mergedMinecraftProvider) {
|
||||
// These are merged v1 mappings
|
||||
Files.deleteIfExists(tinyMappings);
|
||||
LOGGER.info(":populating field names");
|
||||
suggestFieldNames(mergedMinecraftProvider, baseTinyMappings, tinyMappings);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("V1 mappings only support merged minecraft");
|
||||
final List<Path> minecraftJars = minecraftProvider.getMinecraftJars();
|
||||
|
||||
if (minecraftJars.size() != 1) {
|
||||
throw new UnsupportedOperationException("V1 mappings only support single jar minecraft providers");
|
||||
}
|
||||
|
||||
// These are merged v1 mappings
|
||||
Files.deleteIfExists(tinyMappings);
|
||||
LOGGER.info(":populating field names");
|
||||
suggestFieldNames(minecraftJars.get(0), baseTinyMappings, tinyMappings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,9 +460,9 @@ public class MappingsProviderImpl implements MappingsProvider, SharedService {
|
||||
}
|
||||
}
|
||||
|
||||
private void suggestFieldNames(MergedMinecraftProvider minecraftProvider, Path oldMappings, Path newMappings) {
|
||||
private void suggestFieldNames(Path inputJar, Path oldMappings, Path newMappings) {
|
||||
Command command = new CommandProposeFieldNames();
|
||||
runCommand(command, minecraftProvider.getMergedJar().toFile().getAbsolutePath(),
|
||||
runCommand(command, inputJar.toFile().getAbsolutePath(),
|
||||
oldMappings.toAbsolutePath().toString(),
|
||||
newMappings.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import net.fabricmc.loom.configuration.mods.JarSplitter;
|
||||
import net.fabricmc.loom.util.FileSystemUtil;
|
||||
|
||||
public class MinecraftJarSplitter implements AutoCloseable {
|
||||
@@ -65,8 +66,8 @@ public class MinecraftJarSplitter implements AutoCloseable {
|
||||
// Not something we expect, will require 3 jars, server, client and common.
|
||||
assert entryData.serverOnlyEntries.isEmpty();
|
||||
|
||||
copyEntriesToJar(entryData.commonEntries, serverInputJar, commonOutputJar);
|
||||
copyEntriesToJar(entryData.clientOnlyEntries, clientInputJar, clientOnlyOutputJar);
|
||||
copyEntriesToJar(entryData.commonEntries, serverInputJar, commonOutputJar, "common");
|
||||
copyEntriesToJar(entryData.clientOnlyEntries, clientInputJar, clientOnlyOutputJar, "client");
|
||||
}
|
||||
|
||||
public void sharedEntry(String path) {
|
||||
@@ -104,7 +105,7 @@ public class MinecraftJarSplitter implements AutoCloseable {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private void copyEntriesToJar(Set<String> entries, Path inputJar, Path outputJar) throws IOException {
|
||||
private void copyEntriesToJar(Set<String> entries, Path inputJar, Path outputJar, String env) throws IOException {
|
||||
Files.deleteIfExists(outputJar);
|
||||
|
||||
try (FileSystemUtil.Delegate inputFs = FileSystemUtil.getJarFileSystem(inputJar);
|
||||
@@ -124,13 +125,14 @@ public class MinecraftJarSplitter implements AutoCloseable {
|
||||
Files.copy(inputPath, outputPath, StandardCopyOption.COPY_ATTRIBUTES);
|
||||
}
|
||||
|
||||
writeManifest(outputFs);
|
||||
writeManifest(outputFs, env);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeManifest(FileSystemUtil.Delegate outputFs) throws IOException {
|
||||
private void writeManifest(FileSystemUtil.Delegate outputFs, String env) throws IOException {
|
||||
final Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
manifest.getMainAttributes().putValue(JarSplitter.MANIFEST_SPLIT_ENV_NAME_KEY, env);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
manifest.write(out);
|
||||
Files.createDirectories(outputFs.get().getPath("META-INF"));
|
||||
|
||||
@@ -89,7 +89,7 @@ public class MinecraftLibraryProvider {
|
||||
|
||||
private void provideClientLibraries(boolean overrideLWJGL, boolean hasNativesToExtract) {
|
||||
final boolean isArm = Architecture.CURRENT.isArm();
|
||||
final boolean classpathArmNatives = !hasNativesToExtract && isArm;
|
||||
final boolean classpathArmNatives = !hasNativesToExtract && isArm && !IS_MACOS;
|
||||
|
||||
if (classpathArmNatives) {
|
||||
LoomRepositoryPlugin.forceLWJGLFromMavenCentral(project);
|
||||
|
||||
@@ -225,7 +225,7 @@ public abstract class MinecraftProvider {
|
||||
throw new UnsupportedOperationException("Expected only 1 version in META-INF/versions.list, but got %d".formatted(getServerBundleMetadata().versions().size()));
|
||||
}
|
||||
|
||||
getServerBundleMetadata().versions().get(0).unpackEntry(minecraftServerJar.toPath(), getMinecraftExtractedServerJar().toPath());
|
||||
getServerBundleMetadata().versions().get(0).unpackEntry(minecraftServerJar.toPath(), getMinecraftExtractedServerJar().toPath(), project);
|
||||
}
|
||||
|
||||
public File workingDir() {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
package net.fabricmc.loom.extension;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -45,6 +46,7 @@ import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.publish.maven.MavenPublication;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.api.ForgeExtensionAPI;
|
||||
import net.fabricmc.loom.api.InterfaceInjectionExtensionAPI;
|
||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI;
|
||||
@@ -65,6 +67,7 @@ import net.fabricmc.loom.configuration.providers.mappings.LayeredMappingSpecBuil
|
||||
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.task.GenerateSourcesTask;
|
||||
import net.fabricmc.loom.util.DeprecationHelper;
|
||||
import net.fabricmc.loom.util.ModPlatform;
|
||||
import net.fabricmc.loom.util.gradle.SourceSetHelper;
|
||||
@@ -305,6 +308,25 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA
|
||||
intermediateMappingsProvider.set(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getMappingsFile() {
|
||||
return LoomGradleExtension.get(getProject()).getMappingsProvider().tinyMappings.toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenerateSourcesTask getDecompileTask(DecompilerOptions options, boolean client) {
|
||||
final String decompilerName = options.getFormattedName();
|
||||
final String taskName;
|
||||
|
||||
if (areEnvironmentSourceSetsSplit()) {
|
||||
taskName = "gen%sSourcesWith%s".formatted(client ? "ClientOnly" : "Common", decompilerName);
|
||||
} else {
|
||||
taskName = "genSourcesWith" + decompilerName;
|
||||
}
|
||||
|
||||
return (GenerateSourcesTask) getProject().getTasks().getByName(taskName);
|
||||
}
|
||||
|
||||
protected abstract <T extends IntermediateMappingsProvider> void configureIntermediateMappingsProviderInternal(T provider);
|
||||
|
||||
@Override
|
||||
|
||||
@@ -75,6 +75,8 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask {
|
||||
getMinecraftVersion().set(versionInfo.id());
|
||||
getMinecraftVersion().finalizeValue();
|
||||
getDownloadThreads().convention(Runtime.getRuntime().availableProcessors());
|
||||
getMinecraftVersion().set(versionInfo.id());
|
||||
getMinecraftVersion().finalizeValue();
|
||||
|
||||
if (versionInfo.assets().equals("legacy")) {
|
||||
getLegacyResourcesDirectory().set(new File(assetsDir, "/legacy/" + versionInfo.id()));
|
||||
|
||||
@@ -51,7 +51,9 @@ import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.gradle.workers.WorkAction;
|
||||
import org.gradle.workers.WorkParameters;
|
||||
import org.gradle.workers.WorkQueue;
|
||||
@@ -81,6 +83,7 @@ import net.fabricmc.mappingio.adapter.MappingSourceNsSwitch;
|
||||
import net.fabricmc.mappingio.format.Tiny2Writer;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
@DisableCachingByDefault
|
||||
public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
private final DecompilerOptions decompilerOptions;
|
||||
|
||||
@@ -99,6 +102,9 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
@InputFiles
|
||||
public abstract ConfigurableFileCollection getClasspath();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputJar();
|
||||
|
||||
@Inject
|
||||
public abstract WorkerExecutor getWorkerExecutor();
|
||||
|
||||
@@ -112,6 +118,8 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
getOutputs().upToDateWhen((o) -> false);
|
||||
getClasspath().from(decompilerOptions.getClasspath()).finalizeValueOnRead();
|
||||
dependsOn(decompilerOptions.getClasspath().getBuiltBy());
|
||||
|
||||
getOutputJar().fileProvider(getProject().provider(() -> getMappedJarFileWithSuffix("-sources.jar")));
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
@@ -150,7 +158,7 @@ public abstract class GenerateSourcesTask extends AbstractLoomTask {
|
||||
|
||||
params.getInputJar().set(getInputJar());
|
||||
params.getRuntimeJar().set(getRuntimeJar());
|
||||
params.getSourcesDestinationJar().set(getMappedJarFileWithSuffix("-sources.jar"));
|
||||
params.getSourcesDestinationJar().set(getOutputJar());
|
||||
params.getLinemap().set(getMappedJarFileWithSuffix("-sources.lmap"));
|
||||
params.getLinemapJar().set(getMappedJarFileWithSuffix("-linemapped.jar"));
|
||||
params.getMappings().set(getMappings().toFile());
|
||||
|
||||
@@ -36,7 +36,7 @@ import org.gradle.api.plugins.JavaPlugin;
|
||||
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 org.gradle.jvm.tasks.Jar;
|
||||
|
||||
import net.fabricmc.loom.LoomGradleExtension;
|
||||
import net.fabricmc.loom.util.Constants;
|
||||
@@ -111,10 +111,10 @@ public class RemapTaskConfiguration {
|
||||
// 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);
|
||||
final Task jarTask = project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME);
|
||||
final Jar jarTask = (Jar) project.getTasks().getByName(JavaPlugin.JAR_TASK_NAME);
|
||||
configuration.getArtifacts().removeIf(artifact -> {
|
||||
// if the artifact is a -dev jar and "builtBy jar"
|
||||
return "dev".equals(artifact.getClassifier()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask);
|
||||
// if the artifact is built by the jar task, and has the same output path.
|
||||
return artifact.getFile().getAbsolutePath().equals(jarTask.getArchiveFile().get().getAsFile().getAbsolutePath()) && artifact.getBuildDependencies().getDependencies(null).contains(jarTask);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.gradle.workers.WorkParameters;
|
||||
import org.gradle.workers.WorkQueue;
|
||||
import org.gradle.workers.WorkerExecutor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
@@ -115,7 +116,7 @@ public abstract class ValidateMixinNameTask extends SourceTask {
|
||||
}
|
||||
|
||||
final String mixinClassName = toSimpleName(mixin.className);
|
||||
final String expectedMixinClassName = toSimpleName(mixin.target.getInternalName()).replace("$", "") + (mixin.accessor ? "Accessor" : "Mixin");
|
||||
final String expectedMixinClassName = mixin.expectedClassName();
|
||||
|
||||
if (expectedMixinClassName.startsWith("class_")) {
|
||||
// Don't enforce intermediary named mixins.
|
||||
@@ -140,36 +141,48 @@ public abstract class ValidateMixinNameTask extends SourceTask {
|
||||
|
||||
throw new GradleException("Mixin name validation failed: " + errors.stream().collect(Collectors.joining(System.lineSeparator())));
|
||||
}
|
||||
}
|
||||
|
||||
private static String toSimpleName(String internalName) {
|
||||
return internalName.substring(internalName.lastIndexOf("/") + 1);
|
||||
}
|
||||
private static String toSimpleName(String internalName) {
|
||||
return internalName.substring(internalName.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Mixin getMixin(File file) {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
ClassReader reader = new ClassReader(is);
|
||||
|
||||
var classVisitor = new MixinTargetClassVisitor();
|
||||
reader.accept(classVisitor, ClassReader.SKIP_CODE);
|
||||
|
||||
if (classVisitor.mixinTarget != null) {
|
||||
return new Mixin(classVisitor.className, classVisitor.mixinTarget, classVisitor.accessor);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read input file: " + file, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
@VisibleForTesting
|
||||
public record Mixin(String className, Type target, boolean accessor) {
|
||||
public String expectedClassName() {
|
||||
return toSimpleName(target.getInternalName()).replace("$", "") + (accessor ? "Accessor" : "Mixin");
|
||||
}
|
||||
}
|
||||
|
||||
private record Mixin(String className, Type target, boolean accessor) { }
|
||||
@Nullable
|
||||
private static Mixin getMixin(File file) {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
return getMixin(is);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to read input file: " + file, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
public static Mixin getMixin(InputStream is) throws IOException {
|
||||
final ClassReader reader = new ClassReader(is);
|
||||
|
||||
var classVisitor = new MixinTargetClassVisitor();
|
||||
reader.accept(classVisitor, ClassReader.SKIP_CODE);
|
||||
|
||||
if (classVisitor.mixinTarget != null && classVisitor.targets == 1) {
|
||||
return new Mixin(classVisitor.className, classVisitor.mixinTarget, classVisitor.accessor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class MixinTargetClassVisitor extends ClassVisitor {
|
||||
Type mixinTarget;
|
||||
String className;
|
||||
boolean accessor;
|
||||
int targets = 0;
|
||||
|
||||
boolean isInterface;
|
||||
|
||||
@@ -220,6 +233,7 @@ public abstract class ValidateMixinNameTask extends SourceTask {
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
mixinTarget = Objects.requireNonNull((Type) value);
|
||||
targets++;
|
||||
|
||||
super.visit(name, value);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ package net.fabricmc.loom.util;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystemException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||
@@ -37,25 +39,55 @@ public final class AttributeHelper {
|
||||
}
|
||||
|
||||
public static Optional<String> readAttribute(Path path, String key) throws IOException {
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
final Path attributesFile = getFallbackPath(path, key);
|
||||
|
||||
if (!attributeView.list().contains(key)) {
|
||||
return Optional.empty();
|
||||
if (exists(attributesFile)) {
|
||||
// Use the fallback file if it exists.
|
||||
return Optional.of(Files.readString(attributesFile, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key));
|
||||
attributeView.read(key, buffer);
|
||||
buffer.flip();
|
||||
final String value = StandardCharsets.UTF_8.decode(buffer).toString();
|
||||
return Optional.of(value);
|
||||
try {
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
|
||||
if (!attributeView.list().contains(key)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(attributeView.size(key));
|
||||
attributeView.read(key, buffer);
|
||||
buffer.flip();
|
||||
final String value = StandardCharsets.UTF_8.decode(buffer).toString();
|
||||
return Optional.of(value);
|
||||
} catch (FileSystemException ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeAttribute(Path path, String key, String value) throws IOException {
|
||||
// TODO may need to fallback to creating a separate file if this isnt supported.
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
final int written = attributeView.write(key, buffer);
|
||||
assert written == bytes.length;
|
||||
final Path attributesFile = getFallbackPath(path, key);
|
||||
|
||||
try {
|
||||
final UserDefinedFileAttributeView attributeView = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
final int written = attributeView.write(key, buffer);
|
||||
assert written == bytes.length;
|
||||
|
||||
if (exists(attributesFile)) {
|
||||
Files.delete(attributesFile);
|
||||
}
|
||||
} catch (FileSystemException ignored) {
|
||||
// Fallback to a separate file when using a file system that does not attributes.
|
||||
Files.writeString(attributesFile, value, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getFallbackPath(Path path, String key) {
|
||||
return path.resolveSibling(path.getFileName() + "." + key + ".att");
|
||||
}
|
||||
|
||||
// A faster exists check
|
||||
private static boolean exists(Path path) {
|
||||
return path.getFileSystem() == FileSystems.getDefault() ? path.toFile().exists() : Files.exists(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ public class SourceRemapper {
|
||||
|
||||
public static void remapSources(Project project, File input, File output, String from, String to, boolean reproducibleFileOrder, boolean preserveFileTimestamps) {
|
||||
SourceRemapper sourceRemapper = new SourceRemapper(project, from, to);
|
||||
sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps, () -> {});
|
||||
sourceRemapper.scheduleRemapSources(input, output, reproducibleFileOrder, preserveFileTimestamps, () -> {
|
||||
});
|
||||
sourceRemapper.remapAll();
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@ public class Download {
|
||||
|
||||
if (isHashValid(output)) {
|
||||
// Valid hash, no need to re-download
|
||||
writeHash(output, expectedHash);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -322,9 +323,9 @@ public class Download {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHash(Path output, String eTag) throws DownloadException {
|
||||
private void writeHash(Path output, String value) throws DownloadException {
|
||||
try {
|
||||
AttributeHelper.writeAttribute(output, "LoomHash", eTag);
|
||||
AttributeHelper.writeAttribute(output, "LoomHash", value);
|
||||
} catch (IOException e) {
|
||||
throw error(e, "Failed to write hash to (%s)", output);
|
||||
}
|
||||
@@ -354,7 +355,7 @@ public class Download {
|
||||
|
||||
private boolean getAndResetLock(Path output) throws DownloadException {
|
||||
final Path lock = getLockFile(output);
|
||||
final boolean exists = Files.exists(lock);
|
||||
final boolean exists = exists(lock);
|
||||
|
||||
if (exists) {
|
||||
try {
|
||||
|
||||
@@ -33,16 +33,26 @@ import org.gradle.internal.logging.progress.ProgressLogger;
|
||||
import org.gradle.internal.logging.progress.ProgressLoggerFactory;
|
||||
|
||||
public class ProgressGroup implements Closeable {
|
||||
private final String name;
|
||||
private final ProgressLoggerFactory progressLoggerFactory;
|
||||
private final ProgressLogger progressGroup;
|
||||
|
||||
private ProgressLogger progressGroup;
|
||||
|
||||
public ProgressGroup(Project project, String name) {
|
||||
this.name = name;
|
||||
this.progressLoggerFactory = ((ProjectInternal) project).getServices().get(ProgressLoggerFactory.class);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
this.progressGroup = this.progressLoggerFactory.newOperation(name).setDescription(name);
|
||||
this.progressGroup.started();
|
||||
}
|
||||
|
||||
public ProgressLogger createProgressLogger(String name) {
|
||||
if (progressGroup == null) {
|
||||
start();
|
||||
}
|
||||
|
||||
ProgressLogger progressLogger = this.progressLoggerFactory.newOperation(getClass(), progressGroup);
|
||||
progressLogger.setDescription(name);
|
||||
progressLogger.start(name, null);
|
||||
@@ -51,6 +61,9 @@ public class ProgressGroup implements Closeable {
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.progressGroup.completed();
|
||||
if (this.progressGroup != null) {
|
||||
this.progressGroup.completed();
|
||||
this.progressGroup = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.unit
|
||||
|
||||
import net.fabricmc.loom.api.RemapConfigurationSettings
|
||||
import net.fabricmc.loom.configuration.RemapConfigurations
|
||||
import net.fabricmc.loom.test.util.GradleTestUtil
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import spock.lang.Specification
|
||||
|
||||
class RemapConfigurationsTest extends Specification {
|
||||
private static final RemapConfigurations.ConfigurationOption IMPLEMENTATION_OPTION = new RemapConfigurations.ConfigurationOption(SourceSet::getImplementationConfigurationName, true, true, RemapConfigurationSettings.PublishingMode.RUNTIME_ONLY)
|
||||
|
||||
def "testmod impl name"() {
|
||||
given:
|
||||
def sourceSet = GradleTestUtil.mockSourceSet("testmod")
|
||||
when:
|
||||
def name = IMPLEMENTATION_OPTION.name(sourceSet)
|
||||
then:
|
||||
name == "modTestmodImplementation"
|
||||
}
|
||||
|
||||
def "main impl name"() {
|
||||
given:
|
||||
def sourceSet = GradleTestUtil.mockSourceSet("main")
|
||||
when:
|
||||
def name = IMPLEMENTATION_OPTION.name(sourceSet)
|
||||
then:
|
||||
name == "modImplementation"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.unit
|
||||
|
||||
import net.fabricmc.loom.task.ValidateMixinNameTask
|
||||
import org.spongepowered.asm.mixin.Mixin
|
||||
import org.spongepowered.asm.mixin.gen.Accessor
|
||||
import spock.lang.Specification
|
||||
|
||||
class ValidateMixinNameTest extends Specification {
|
||||
def "TestMixin"() {
|
||||
when:
|
||||
def mixin = getMixin(TestMixin.class)
|
||||
then:
|
||||
mixin.className() == "net/fabricmc/loom/test/unit/TestMixin"
|
||||
mixin.target().internalName == "net/fabricmc/loom/test/unit/Test"
|
||||
mixin.expectedClassName() == "TestMixin"
|
||||
!mixin.accessor()
|
||||
}
|
||||
|
||||
def "TestInnerMixin"() {
|
||||
when:
|
||||
def mixin = getMixin(TestInnerMixin.class)
|
||||
then:
|
||||
mixin.className() == "net/fabricmc/loom/test/unit/TestInnerMixin"
|
||||
mixin.target().internalName == "net/fabricmc/loom/test/unit/Test\$Inner"
|
||||
mixin.expectedClassName() == "TestInnerMixin"
|
||||
!mixin.accessor()
|
||||
}
|
||||
|
||||
def "TestAccessor"() {
|
||||
when:
|
||||
def mixin = getMixin(TestAccessor.class)
|
||||
then:
|
||||
mixin.className() == "net/fabricmc/loom/test/unit/TestAccessor"
|
||||
mixin.target().internalName == "net/fabricmc/loom/test/unit/Test"
|
||||
mixin.expectedClassName() == "TestAccessor"
|
||||
mixin.accessor()
|
||||
}
|
||||
|
||||
def "TestManyTargetsMixin"() {
|
||||
when:
|
||||
def mixin = getMixin(TestManyTargetsMixin.class)
|
||||
then:
|
||||
mixin == null
|
||||
}
|
||||
|
||||
static ValidateMixinNameTask.Mixin getMixin(Class<?> clazz) {
|
||||
return getInput(clazz).withCloseable {
|
||||
return ValidateMixinNameTask.getMixin(it)
|
||||
}
|
||||
}
|
||||
|
||||
static InputStream getInput(Class<?> clazz) {
|
||||
return clazz.classLoader.getResourceAsStream(clazz.name.replace('.', '/') + ".class")
|
||||
}
|
||||
}
|
||||
|
||||
@Mixin(Test.class)
|
||||
class TestMixin {
|
||||
}
|
||||
|
||||
@Mixin(Test.Inner.class)
|
||||
class TestInnerMixin {
|
||||
}
|
||||
|
||||
@Mixin(Test.class)
|
||||
interface TestAccessor {
|
||||
@Accessor
|
||||
Object getNothing();
|
||||
}
|
||||
|
||||
@Mixin([Test.class, Test.Inner.class])
|
||||
class TestManyTargetsMixin {
|
||||
}
|
||||
|
||||
class Test {
|
||||
class Inner {
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,15 @@
|
||||
|
||||
package net.fabricmc.loom.test.util
|
||||
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.internal.tasks.DefaultSourceSet
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.plugins.ExtensionContainer
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.util.PatternFilterable
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any
|
||||
import static org.mockito.Mockito.mock
|
||||
import static org.mockito.Mockito.when
|
||||
|
||||
@@ -35,4 +42,30 @@ class GradleTestUtil {
|
||||
when(mock.get()).thenReturn(Objects.requireNonNull(value))
|
||||
return mock
|
||||
}
|
||||
|
||||
static SourceSet mockSourceSet(String name) {
|
||||
def sourceSet = new DefaultSourceSet(name, mockObjectFactory()) {
|
||||
final ExtensionContainer extensions = null
|
||||
}
|
||||
return sourceSet
|
||||
}
|
||||
|
||||
static ObjectFactory mockObjectFactory() {
|
||||
def mock = mock(ObjectFactory.class)
|
||||
def mockSourceDirectorySet = mockSourceDirectorySet()
|
||||
when(mock.sourceDirectorySet(any(), any())).thenReturn(mockSourceDirectorySet)
|
||||
return mock
|
||||
}
|
||||
|
||||
static SourceDirectorySet mockSourceDirectorySet() {
|
||||
def mock = mock(SourceDirectorySet.class)
|
||||
def mockPatternFilterable = mockPatternFilterable()
|
||||
when(mock.getFilter()).thenReturn(mockPatternFilterable)
|
||||
return mock
|
||||
}
|
||||
|
||||
static PatternFilterable mockPatternFilterable() {
|
||||
def mock = mock(PatternFilterable.class)
|
||||
return mock
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user